Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/hooks/useControlOnlyRuleUpgradeRedirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {arePolicyRulesEnabled, isCollectPolicy, tryNavigateToControlPolicyUpgrade} from '@libs/PolicyUtils';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';

import {useEffect, useRef} from 'react';

import useOnyx from './useOnyx';
import usePermissions from './usePermissions';
import usePolicy from './usePolicy';

/**
* Sends a Collect admin who lands on a Control-only Rules page to the Control upgrade page.
*
* The Rules page gates its own Control-only features on press, but these pages are also reachable in ways that
* skip it: direct deep links to the page and its child pickers, and Wallet > Expensify card > Edit spend rules.
* An `accessVariants` CONTROL check can't be used for that, because AccessOrNotFoundWrapper only ever renders
* Not Found, never an upgrade path.
*
* @param policyID - The policy the page belongs to.
* @param backTo - Where the upgrade page should return to. Defaults to the workspace Rules page.
*/
function useControlOnlyRuleUpgradeRedirect(policyID: string, backTo?: Route) {
const policy = usePolicy(policyID);
const {isBetaEnabled} = usePermissions();
const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`);

const isCollect = isCollectPolicy(policy);
// Mirrors the feature check in AccessOrNotFoundWrapper. When Rules itself is disabled, that wrapper already
// redirects to More features, so redirecting to the upgrade page too would flash it on the way there.
const isRulesFeatureEnabled = arePolicyRulesEnabled(policy, policyCategories, isBetaEnabled(CONST.BETAS.RULES_REVAMP));
const hasRedirectedToUpgrade = useRef(false);
const upgradeBackTo = backTo ?? ROUTES.WORKSPACE_RULES.getRoute(policyID);

useEffect(() => {
if (!isCollect || !isRulesFeatureEnabled || hasRedirectedToUpgrade.current) {
return;
}

// Replace rather than push: Back from the upgrade page must not land on a page Collect can't use.
hasRedirectedToUpgrade.current = tryNavigateToControlPolicyUpgrade(policy, CONST.UPGRADE_FEATURE_INTRO_MAPPING.rules.alias, upgradeBackTo, true);
}, [isCollect, isRulesFeatureEnabled, policy, upgradeBackTo]);
}

export default useControlOnlyRuleUpgradeRedirect;
8 changes: 6 additions & 2 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1446,13 +1446,17 @@ function isCollectPolicy(policy: OnyxEntry<Policy>): boolean {
/**
* Collect workspaces can access a limited subset of Rules features. When a Collect admin tries to
* access a Control-only Rules feature, navigate to the upgrade flow and return true.
*
* @param shouldReplace - Replace the current screen instead of pushing the upgrade page on top of it. Use this when
* the redirect happens from the Control-only page itself, so pressing Back doesn't land back on a page Collect
* can't use. Press handlers on a page Collect *can* use should keep the default push.
*/
function tryNavigateToControlPolicyUpgrade(policy: OnyxEntry<Policy>, upgradeFeatureAlias: string, backTo?: string): boolean {
function tryNavigateToControlPolicyUpgrade(policy: OnyxEntry<Policy>, upgradeFeatureAlias: string, backTo?: string, shouldReplace = false): boolean {
if (!policy?.id || isControlPolicy(policy) || !isCollectPolicy(policy)) {
return false;
}

Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policy.id, upgradeFeatureAlias, backTo ?? ROUTES.WORKSPACE_RULES.getRoute(policy.id)));
Navigation.navigate(ROUTES.WORKSPACE_UPGRADE.getRoute(policy.id, upgradeFeatureAlias, backTo ?? ROUTES.WORKSPACE_RULES.getRoute(policy.id)), {forceReplace: shouldReplace});
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ function WalletExpensifyCardSpendRulesPage({route}: WalletExpensifyCardSpendRule
ruleID={isNewRule ? undefined : ruleID}
titleKey={isNewRule ? 'workspace.rules.merchantRules.addRuleTitle' : 'workspace.rules.spendRules.editRuleTitle'}
testID="WalletExpensifyCardSpendRulesPage"
// Come back here after upgrading rather than dropping the user on the workspace Rules page,
// since this flow starts from the Wallet.
upgradeBackTo={ROUTES.SETTINGS_WALLET_EXPENSIFY_CARD_SPEND_RULES.getRoute(policyID, isNewRule ? undefined : ruleID)}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {ListItem} from '@components/SelectionList/types';

import useCanWriteCardSpendRules from '@hooks/useCanWriteCardSpendRules';
import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons';
import useControlOnlyRuleUpgradeRedirect from '@hooks/useControlOnlyRuleUpgradeRedirect';
import useDefaultFundID from '@hooks/useDefaultFundID';
import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -104,6 +105,7 @@ function SpendRuleCardPage({route}: SpendRuleCardPageProps) {

const [selectedCardIDs, setSelectedCardIDs] = useState<string[]>([]);
const {isLoading, startWithLoading} = usePressLoading();
useControlOnlyRuleUpgradeRedirect(policyID);

useFocusEffect(
useCallback(() => {
Expand Down Expand Up @@ -209,7 +211,7 @@ function SpendRuleCardPage({route}: SpendRuleCardPageProps) {
<AccessOrNotFoundWrapper
policyID={policyID}
featureName={CONST.POLICY.MORE_FEATURES.ARE_RULES_ENABLED}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.PAID, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.PAID]}
Comment thread
Krishna2323 marked this conversation as resolved.
shouldBeBlocked={!canWriteCardSpendRules}
>
{isCardSettingsLoading ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import SpendRuleCategoryBase from '@components/SpendRules/configuration/SpendRuleCategoryBase';

import useCanWriteCardSpendRules from '@hooks/useCanWriteCardSpendRules';
import useControlOnlyRuleUpgradeRedirect from '@hooks/useControlOnlyRuleUpgradeRedirect';
import useOnyx from '@hooks/useOnyx';

import {updateDraftSpendRule} from '@libs/actions/User';
Expand All @@ -22,6 +23,7 @@ function SpendRuleCategoryPage({route}: SpendRuleCategoryPageProps) {
const {policyID} = route.params;
const canWriteCardSpendRules = useCanWriteCardSpendRules(policyID);
const [spendRuleForm] = useOnyx(ONYXKEYS.FORMS.SPEND_RULE_FORM);
useControlOnlyRuleUpgradeRedirect(policyID);

const onCategoriesChange = (categories: SpendRuleCategory[]) => {
updateDraftSpendRule({categories});
Expand All @@ -31,7 +33,7 @@ function SpendRuleCategoryPage({route}: SpendRuleCategoryPageProps) {
<AccessOrNotFoundWrapper
policyID={policyID}
featureName={CONST.POLICY.MORE_FEATURES.ARE_RULES_ENABLED}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.PAID, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.PAID]}
shouldBeBlocked={!canWriteCardSpendRules}
>
<SpendRuleCategoryBase
Expand Down
10 changes: 8 additions & 2 deletions src/pages/workspace/rules/SpendRules/SpendRulePageBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Text from '@components/Text';

import useCanWriteCardSpendRules from '@hooks/useCanWriteCardSpendRules';
import useConfirmModal from '@hooks/useConfirmModal';
import useControlOnlyRuleUpgradeRedirect from '@hooks/useControlOnlyRuleUpgradeRedirect';
import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useDefaultFundID from '@hooks/useDefaultFundID';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
Expand Down Expand Up @@ -40,6 +41,7 @@ import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import type {SpendRuleCategory} from '@src/types/form/SpendRuleForm';
import type IconAsset from '@src/types/utils/IconAsset';
Expand All @@ -54,6 +56,9 @@ type SpendRulePageBaseProps = {
ruleID?: string;
titleKey: TranslationPaths;
testID: string;

/** Where the Control upgrade page should return to. Defaults to the workspace Rules page. */
upgradeBackTo?: Route;
};

function getErrorMessage(hasSelectedCards: boolean, hasAnyRuleApplied: boolean, translate: (path: TranslationPaths) => string) {
Expand All @@ -69,7 +74,7 @@ function getErrorMessage(hasSelectedCards: boolean, hasAnyRuleApplied: boolean,
return '';
}

function SpendRulePageBase({policyID, ruleID, titleKey, testID}: SpendRulePageBaseProps) {
function SpendRulePageBase({policyID, ruleID, titleKey, testID, upgradeBackTo}: SpendRulePageBaseProps) {
const {convertToDisplayString} = useCurrencyListActions();
const styles = useThemeStyles();
const {translate} = useLocalize();
Expand All @@ -78,6 +83,7 @@ function SpendRulePageBase({policyID, ruleID, titleKey, testID}: SpendRulePageBa

const {showReadOnlyModal} = usePolicyFeatureWriteAccess(policy, CONST.POLICY.POLICY_FEATURE.RULES);
const canWriteSpendRules = useCanWriteCardSpendRules(policyID);
useControlOnlyRuleUpgradeRedirect(policyID, upgradeBackTo);
const {isBetaEnabled} = usePermissions();
const isRulesRevampEnabled = isBetaEnabled(CONST.BETAS.RULES_REVAMP);
const icons = useMemoizedLazyExpensifyIcons(['CreditCardHourglass', 'MoneyCircle', 'CoinsButton', 'Basket']);
Expand Down Expand Up @@ -491,7 +497,7 @@ function SpendRulePageBase({policyID, ruleID, titleKey, testID}: SpendRulePageBa
<AccessOrNotFoundWrapper
policyID={policyID}
featureName={CONST.POLICY.MORE_FEATURES.ARE_RULES_ENABLED}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.PAID, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.PAID]}
shouldBeBlocked={!!policy?.id && !canWriteSpendRules}
>
<ScreenWrapper
Expand Down
Loading