Skip to content

Commit 819aede

Browse files
authored
Merge pull request Expensify#76241 from software-mansion-labs/feat/migrate-issue-new-card-confirm-page-to-use-navigation
2 parents 0e02ed0 + d44e37f commit 819aede

9 files changed

Lines changed: 107 additions & 64 deletions

File tree

src/ROUTES.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,6 +2239,12 @@ const ROUTES = {
22392239
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
22402240
getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/issue-new`, backTo),
22412241
},
2242+
WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE: {
2243+
route: 'workspaces/:policyID/expensify-card/issue-new/confirm-magic-code',
2244+
2245+
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
2246+
getRoute: (policyID: string, backTo?: string) => getUrlWithBackToParam(`workspaces/${policyID}/expensify-card/issue-new/confirm-magic-code`, backTo),
2247+
},
22422248
WORKSPACE_EXPENSIFY_CARD_BANK_ACCOUNT: {
22432249
route: 'workspaces/:policyID/expensify-card/choose-bank-account',
22442250
getRoute: (policyID: string | undefined) => {

src/SCREENS.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ const SCREENS = {
598598
EXPENSIFY_CARD_DETAILS: 'Workspace_ExpensifyCard_Details',
599599
EXPENSIFY_CARD_LIMIT: 'Workspace_ExpensifyCard_Limit',
600600
EXPENSIFY_CARD_ISSUE_NEW: 'Workspace_ExpensifyCard_New',
601+
EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE: 'Workspace_ExpensifyCard_New_Confirm_Magic_Code',
601602
EXPENSIFY_CARD_NAME: 'Workspace_ExpensifyCard_Name',
602603
EXPENSIFY_CARD_SELECT_FEED: 'Workspace_ExpensifyCard_Select_Feed',
603604
EXPENSIFY_CARD_LIMIT_TYPE: 'Workspace_ExpensifyCard_LimitType',

src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,8 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
766766
[SCREENS.WORKSPACE.COMPANY_CARD_NAME]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage').default,
767767
[SCREENS.WORKSPACE.COMPANY_CARD_EXPORT]: () => require<ReactComponentModule>('../../../../pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage').default,
768768
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/issueNew/IssueNewCardPage').default,
769+
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE]: () =>
770+
require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/issueNew/IssueNewCardConfirmMagicCodePage').default,
769771
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceCardSettingsPage').default,
770772
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceSettlementAccountPage').default,
771773
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_FREQUENCY]: () => require<ReactComponentModule>('../../../../pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage').default,

src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ const WORKSPACE_TO_RHP: Partial<Record<keyof WorkspaceSplitNavigatorParamList, s
246246
],
247247
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: [
248248
SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW,
249+
SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE,
249250
SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT,
250251
SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS,
251252
SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT,

src/libs/Navigation/linkingConfig/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,9 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
722722
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: {
723723
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.route,
724724
},
725+
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE]: {
726+
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE.route,
727+
},
725728
[SCREENS.WORKSPACE.EXPENSIFY_CARD_NAME]: {
726729
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_NAME.route,
727730
},

src/libs/Navigation/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,11 @@ type SettingsNavigatorParamList = {
11691169
// eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md
11701170
backTo?: Routes;
11711171
};
1172+
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE]: {
1173+
policyID: string;
1174+
// eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md
1175+
backTo?: Routes;
1176+
};
11721177
[SCREENS.WORKSPACE.EXPENSIFY_CARD_BANK_ACCOUNT]: {
11731178
policyID: string;
11741179
};

src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx

Lines changed: 11 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,44 @@
1-
import React, {useCallback, useEffect, useRef, useState} from 'react';
1+
import React, {useEffect, useRef} from 'react';
22
import {View} from 'react-native';
33
import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton';
44
import InteractiveStepWrapper from '@components/InteractiveStepWrapper';
55
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
66
import ScrollView from '@components/ScrollView';
77
import Text from '@components/Text';
8-
import ValidateCodeActionModal from '@components/ValidateCodeActionModal';
9-
import useDefaultFundID from '@hooks/useDefaultFundID';
108
import useLocalize from '@hooks/useLocalize';
119
import useNetwork from '@hooks/useNetwork';
1210
import useOnyx from '@hooks/useOnyx';
13-
import usePermissions from '@hooks/usePermissions';
1411
import useThemeStyles from '@hooks/useThemeStyles';
15-
import {clearIssueNewCardError, clearIssueNewCardFlow, issueExpensifyCard, setIssueNewCardStepAndData} from '@libs/actions/Card';
16-
import {requestValidateCodeAction, resetValidateActionCodeSent} from '@libs/actions/User';
12+
import {setIssueNewCardStepAndData} from '@libs/actions/Card';
13+
import {resetValidateActionCodeSent} from '@libs/actions/User';
1714
import {getTranslationKeyForLimitType} from '@libs/CardUtils';
1815
import {convertToShortDisplayString} from '@libs/CurrencyUtils';
19-
import {getLatestErrorMessage, getLatestErrorMessageField} from '@libs/ErrorUtils';
16+
import {getLatestErrorMessage} from '@libs/ErrorUtils';
2017
import {getUserNameByEmail} from '@libs/PersonalDetailsUtils';
2118
import Navigation from '@navigation/Navigation';
2219
import CONST from '@src/CONST';
2320
import ONYXKEYS from '@src/ONYXKEYS';
2421
import ROUTES from '@src/ROUTES';
25-
import type {Route} from '@src/ROUTES';
2622
import type {IssueNewCardStep} from '@src/types/onyx/Card';
2723

2824
type ConfirmationStepProps = {
2925
/** ID of the policy that the card will be issued under */
3026
policyID: string | undefined;
3127

32-
/** Route to navigate to */
33-
backTo?: Route;
34-
3528
/** Array of step names */
3629
stepNames: readonly string[];
3730

3831
/** Start from step index */
3932
startStepIndex: number;
4033
};
4134

42-
function ConfirmationStep({policyID, backTo, stepNames, startStepIndex}: ConfirmationStepProps) {
35+
function ConfirmationStep({policyID, stepNames, startStepIndex}: ConfirmationStepProps) {
4336
const {translate} = useLocalize();
4437
const styles = useThemeStyles();
4538
const {isOffline} = useNetwork();
46-
const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: true});
4739
const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true});
4840
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: true});
49-
const validateError = getLatestErrorMessageField(issueNewCard);
50-
const [isValidateCodeActionModalVisible, setIsValidateCodeActionModalVisible] = useState(false);
5141
const data = issueNewCard?.data;
52-
const isSuccessful = issueNewCard?.isSuccessful;
53-
const defaultFundID = useDefaultFundID(policyID);
54-
const {isBetaEnabled} = usePermissions();
5542
const hasApprovalError = !!policy?.errorFields?.approvalMode;
5643
const isAddApprovalEnabled = policy?.approvalMode !== CONST.POLICY.APPROVAL_MODE.OPTIONAL && !hasApprovalError;
5744
const shouldDisableSubmitButton = !isAddApprovalEnabled && data?.limitType === CONST.EXPENSIFY_CARD.LIMIT_TYPES.SMART;
@@ -63,18 +50,6 @@ function ConfirmationStep({policyID, backTo, stepNames, startStepIndex}: Confirm
6350
resetValidateActionCodeSent();
6451
}, []);
6552

66-
useEffect(() => {
67-
if (!isSuccessful) {
68-
return;
69-
}
70-
setIsValidateCodeActionModalVisible(false);
71-
}, [isSuccessful]);
72-
73-
const submit = (validateCode: string) => {
74-
// NOTE: For Expensify Card UK/EU, the backend will automatically detect the correct feedCountry to use
75-
issueExpensifyCard(defaultFundID, policyID, isBetaEnabled(CONST.BETAS.EXPENSIFY_CARD_EU_UK) ? '' : CONST.COUNTRY.US, validateCode, data);
76-
};
77-
7853
const errorMessage = getLatestErrorMessage(issueNewCard) || (shouldDisableSubmitButton ? translate('workspace.card.issueNewCard.disabledApprovalForSmartLimitError') : '');
7954

8055
const editStep = (step: IssueNewCardStep) => {
@@ -87,19 +62,6 @@ function ConfirmationStep({policyID, backTo, stepNames, startStepIndex}: Confirm
8762

8863
const translationForLimitType = getTranslationKeyForLimitType(data?.limitType);
8964

90-
const onRedirect = useCallback(() => {
91-
if (!isSuccessful) {
92-
return;
93-
}
94-
if (backTo) {
95-
Navigation.goBack(backTo);
96-
} else {
97-
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID));
98-
}
99-
100-
clearIssueNewCardFlow(policyID);
101-
}, [backTo, policyID, isSuccessful]);
102-
10365
return (
10466
<InteractiveStepWrapper
10567
wrapperID={ConfirmationStep.displayName}
@@ -158,27 +120,16 @@ function ConfirmationStep({policyID, backTo, stepNames, startStepIndex}: Confirm
158120
isDisabled={isOffline || shouldDisableSubmitButton}
159121
isMessageHtml={shouldDisableSubmitButton}
160122
isLoading={issueNewCard?.isLoading}
161-
onSubmit={() => setIsValidateCodeActionModalVisible(true)}
123+
onSubmit={() => {
124+
if (!policyID) {
125+
return;
126+
}
127+
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE.getRoute(policyID, ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID)));
128+
}}
162129
buttonText={translate('workspace.card.issueCard')}
163130
/>
164131
</View>
165132
</ScrollView>
166-
{!!issueNewCard && (
167-
<ValidateCodeActionModal
168-
handleSubmitForm={submit}
169-
isLoading={issueNewCard?.isLoading}
170-
sendValidateCode={requestValidateCodeAction}
171-
validateCodeActionErrorField={data?.cardType === CONST.EXPENSIFY_CARD.CARD_TYPE.PHYSICAL ? 'createExpensifyCard' : 'createAdminIssuedVirtualCard'}
172-
validateError={validateError}
173-
clearError={() => clearIssueNewCardError(policyID)}
174-
onClose={() => setIsValidateCodeActionModalVisible(false)}
175-
isVisible={isValidateCodeActionModalVisible}
176-
title={translate('cardPage.validateCardTitle')}
177-
descriptionPrimary={translate('cardPage.enterMagicCode', {contactMethod: account?.primaryLogin ?? ''})}
178-
onModalHide={onRedirect}
179-
disableAnimation
180-
/>
181-
)}
182133
</InteractiveStepWrapper>
183134
);
184135
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React, {useCallback, useEffect} from 'react';
2+
import ValidateCodeActionContent from '@components/ValidateCodeActionModal/ValidateCodeActionContent';
3+
import useDefaultFundID from '@hooks/useDefaultFundID';
4+
import useInitial from '@hooks/useInitial';
5+
import useLocalize from '@hooks/useLocalize';
6+
import useOnyx from '@hooks/useOnyx';
7+
import usePermissions from '@hooks/usePermissions';
8+
import {clearIssueNewCardError, clearIssueNewCardFlow, issueExpensifyCard} from '@libs/actions/Card';
9+
import {requestValidateCodeAction, resetValidateActionCodeSent} from '@libs/actions/User';
10+
import {getLatestErrorMessageField} from '@libs/ErrorUtils';
11+
import Navigation from '@libs/Navigation/Navigation';
12+
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
13+
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
14+
import CONST from '@src/CONST';
15+
import ONYXKEYS from '@src/ONYXKEYS';
16+
import ROUTES from '@src/ROUTES';
17+
import type SCREENS from '@src/SCREENS';
18+
19+
type IssueNewCardConfirmMagicCodePageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW_CONFIRM_MAGIC_CODE>;
20+
21+
function IssueNewCardConfirmMagicCodePage({route}: IssueNewCardConfirmMagicCodePageProps) {
22+
const {translate} = useLocalize();
23+
const policyID = route.params.policyID;
24+
const backTo = route.params.backTo;
25+
const [account] = useOnyx(ONYXKEYS.ACCOUNT, {canBeMissing: false});
26+
const primaryLogin = account?.primaryLogin ?? '';
27+
const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true});
28+
const validateError = getLatestErrorMessageField(issueNewCard);
29+
const data = issueNewCard?.data;
30+
const isSuccessful = issueNewCard?.isSuccessful;
31+
const defaultFundID = useDefaultFundID(policyID);
32+
const {isBetaEnabled} = usePermissions();
33+
const firstAssigneeEmail = useInitial(issueNewCard?.data?.assigneeEmail);
34+
const shouldUseBackToParam = !firstAssigneeEmail || firstAssigneeEmail === issueNewCard?.data?.assigneeEmail;
35+
36+
useEffect(() => {
37+
if (!isSuccessful) {
38+
return;
39+
}
40+
if (backTo && shouldUseBackToParam) {
41+
Navigation.goBack(backTo);
42+
} else {
43+
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID), {forceReplace: true});
44+
}
45+
clearIssueNewCardFlow(policyID);
46+
}, [backTo, isSuccessful, policyID, shouldUseBackToParam]);
47+
48+
const handleSubmit = useCallback(
49+
(validateCode: string) => {
50+
// NOTE: For Expensify Card UK/EU, the backend will automatically detect the correct feedCountry to use
51+
issueExpensifyCard(defaultFundID, policyID, isBetaEnabled(CONST.BETAS.EXPENSIFY_CARD_EU_UK) ? '' : CONST.COUNTRY.US, validateCode, data);
52+
},
53+
[isBetaEnabled, data, defaultFundID, policyID],
54+
);
55+
56+
const handleClose = useCallback(() => {
57+
resetValidateActionCodeSent();
58+
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_ISSUE_NEW.getRoute(policyID, backTo));
59+
}, [policyID, backTo]);
60+
61+
return (
62+
<ValidateCodeActionContent
63+
isLoading={issueNewCard?.isLoading}
64+
title={translate('cardPage.validateCardTitle')}
65+
descriptionPrimary={translate('cardPage.enterMagicCode', {contactMethod: primaryLogin})}
66+
sendValidateCode={() => requestValidateCodeAction()}
67+
validateCodeActionErrorField={data?.cardType === CONST.EXPENSIFY_CARD.CARD_TYPE.PHYSICAL ? 'createExpensifyCard' : 'createAdminIssuedVirtualCard'}
68+
handleSubmitForm={handleSubmit}
69+
validateError={validateError}
70+
clearError={() => clearIssueNewCardError(policyID)}
71+
onClose={handleClose}
72+
/>
73+
);
74+
}
75+
76+
IssueNewCardConfirmMagicCodePage.displayName = 'ExpensifyCardVerifyAccountPage';
77+
78+
export default IssueNewCardConfirmMagicCodePage;

src/pages/workspace/expensifyCard/issueNew/IssueNewCardPage.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import React, {useEffect, useMemo} from 'react';
33
import type {OnyxEntry} from 'react-native-onyx';
44
import DelegateNoAccessWrapper from '@components/DelegateNoAccessWrapper';
55
import ScreenWrapper from '@components/ScreenWrapper';
6-
import useInitial from '@hooks/useInitial';
76
import useOnyx from '@hooks/useOnyx';
87
import {startIssueNewCardFlow} from '@libs/actions/Card';
98
import Navigation from '@libs/Navigation/Navigation';
@@ -50,8 +49,6 @@ function IssueNewCardPage({policy, route}: IssueNewCardPageProps) {
5049
const [issueNewCard] = useOnyx(`${ONYXKEYS.COLLECTION.ISSUE_NEW_EXPENSIFY_CARD}${policyID}`, {canBeMissing: true});
5150
const {currentStep} = issueNewCard ?? {};
5251
const backTo = route?.params?.backTo;
53-
const firstAssigneeEmail = useInitial(issueNewCard?.data?.assigneeEmail);
54-
const shouldUseBackToParam = !firstAssigneeEmail || firstAssigneeEmail === issueNewCard?.data?.assigneeEmail;
5552
const [isActingAsDelegate] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isActingAsDelegateSelector, canBeMissing: true});
5653
const stepNames = issueNewCard?.isChangeAssigneeDisabled ? CONST.EXPENSIFY_CARD.ASSIGNEE_EXCLUDED_STEP_NAMES : CONST.EXPENSIFY_CARD.STEP_NAMES;
5754
const startStepIndex = useMemo(() => getStartStepIndex(issueNewCard), [issueNewCard]);
@@ -107,7 +104,6 @@ function IssueNewCardPage({policy, route}: IssueNewCardPageProps) {
107104
return (
108105
<ConfirmationStep
109106
policyID={policyID}
110-
backTo={shouldUseBackToParam ? backTo : undefined}
111107
stepNames={stepNames}
112108
startStepIndex={startStepIndex}
113109
/>

0 commit comments

Comments
 (0)