Skip to content

Commit 32c95be

Browse files
authored
Merge pull request #59911 from allroundexperts/feat-47073-1
Merge account feature fixes - Part 1
2 parents 2289975 + 33caa59 commit 32c95be

File tree

9 files changed

+92
-46
lines changed

9 files changed

+92
-46
lines changed

src/CONST.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,7 @@ const CONST = {
10841084
DELAYED_SUBMISSION_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/reports/Automatically-submit-employee-reports',
10851085
ENCRYPTION_AND_SECURITY_HELP_URL: 'https://help.expensify.com/articles/new-expensify/settings/Encryption-and-Data-Security',
10861086
PLAN_TYPES_AND_PRICING_HELP_URL: 'https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Plan-types-and-pricing',
1087-
MERGE_ACCOUNT_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/settings/Merge-accounts',
1087+
MERGE_ACCOUNT_HELP_URL: 'https://help.expensify.com/articles/new-expensify/settings/Merge-Accounts',
10881088
CONNECT_A_BUSINESS_BANK_ACCOUNT_HELP_URL: 'https://help.expensify.com/articles/new-expensify/expenses-&-payments/Connect-a-Business-Bank-Account',
10891089
TEST_RECEIPT_URL: `${CLOUDFRONT_URL}/images/fake-receipt__tacotodds.png`,
10901090
// Use Environment.getEnvironmentURL to get the complete URL with port number

src/components/ConfirmationPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function ConfirmationPage({
106106
</View>
107107
)}
108108
<Text style={[styles.textHeadline, styles.textAlignCenter, styles.mv2, headingStyle]}>{heading}</Text>
109-
<Text style={[styles.textAlignCenter, descriptionStyle]}>{description}</Text>
109+
<Text style={[styles.textAlignCenter, descriptionStyle, styles.w100]}>{description}</Text>
110110
{cta ? <Text style={[styles.textAlignCenter, ctaStyle]}>{cta}</Text> : null}
111111
</View>
112112
{(shouldShowSecondaryButton || shouldShowButton) && (

src/languages/en.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,10 @@ const translations = {
14281428
confirmMerge: 'Are you sure you want to merge accounts?',
14291429
lossOfUnsubmittedData: `Merging your accounts is irreversible and will result in the loss of any unsubmitted expenses for `,
14301430
enterMagicCode: `To continue, please enter the magic code sent to `,
1431+
errors: {
1432+
incorrect2fa: 'Incorrect two-factor authentication code. Please try again.',
1433+
fallback: 'Something went wrong. Please try again later.',
1434+
},
14311435
},
14321436
mergeSuccess: {
14331437
accountsMerged: 'Accounts merged!',

src/languages/es.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,10 @@ const translations = {
14271427
confirmMerge: '¿Estás seguro de que deseas fusionar cuentas?',
14281428
lossOfUnsubmittedData: `Fusionar tus cuentas es irreversible y resultará en la pérdida de cualquier gasto no enviado de `,
14291429
enterMagicCode: `Para continuar, por favor introduce el código mágico enviado a `,
1430+
errors: {
1431+
incorrect2fa: 'Código de autenticación de dos factores incorrecto. Por favor, inténtalo de nuevo.',
1432+
fallback: 'Ha ocurrido un error. Por favor, inténtalo mas tarde.',
1433+
},
14301434
},
14311435
mergeSuccess: {
14321436
accountsMerged: '¡Cuentas fusionadas!',

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial<Record<keyof SettingsSplitNavigatorPa
5252
SCREENS.SETTINGS.DELEGATE.DELEGATE_ROLE,
5353
SCREENS.SETTINGS.DELEGATE.UPDATE_DELEGATE_ROLE,
5454
SCREENS.SETTINGS.DELEGATE.DELEGATE_CONFIRM,
55+
SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_DETAILS,
56+
SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_VALIDATE,
57+
SCREENS.SETTINGS.MERGE_ACCOUNTS.MERGE_RESULT,
5558
],
5659
[SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS],
5760
[SCREENS.SETTINGS.SAVE_THE_WORLD]: [SCREENS.I_KNOW_A_TEACHER, SCREENS.INTRO_SCHOOL_PRINCIPAL, SCREENS.I_AM_A_TEACHER],

src/libs/actions/MergeAccounts.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Onyx.connect({
1212
callback: (value) => (session = value ?? {}),
1313
});
1414

15-
function requestValidationCodeForAccountMerge(email: string) {
15+
function requestValidationCodeForAccountMerge(email: string, validateCodeResent = false) {
1616
const optimisticData: OnyxUpdate[] = [
1717
{
1818
onyxMethod: Onyx.METHOD.MERGE,
@@ -21,6 +21,7 @@ function requestValidationCodeForAccountMerge(email: string) {
2121
getValidateCodeForAccountMerge: {
2222
isLoading: true,
2323
validateCodeSent: false,
24+
validateCodeResent: false,
2425
errors: null,
2526
},
2627
},
@@ -34,7 +35,8 @@ function requestValidationCodeForAccountMerge(email: string) {
3435
value: {
3536
getValidateCodeForAccountMerge: {
3637
isLoading: false,
37-
validateCodeSent: true,
38+
validateCodeSent: !validateCodeResent,
39+
validateCodeResent,
3840
errors: null,
3941
},
4042
},
@@ -49,6 +51,7 @@ function requestValidationCodeForAccountMerge(email: string) {
4951
getValidateCodeForAccountMerge: {
5052
isLoading: false,
5153
validateCodeSent: false,
54+
validateCodeResent: false,
5255
},
5356
},
5457
},
@@ -67,6 +70,7 @@ function clearGetValidateCodeForAccountMerge() {
6770
getValidateCodeForAccountMerge: {
6871
errors: null,
6972
validateCodeSent: false,
73+
validateCodeResent: false,
7074
isLoading: false,
7175
},
7276
});

src/pages/settings/Security/MergeAccounts/AccountDetailsPage.tsx

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import {useFocusEffect, useRoute} from '@react-navigation/native';
1+
import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native';
22
import {Str} from 'expensify-common';
3-
import React, {useCallback, useRef, useState} from 'react';
4-
import {View} from 'react-native';
3+
import React, {useCallback, useEffect, useRef, useState} from 'react';
4+
import {InteractionManager, View} from 'react-native';
55
import {useOnyx} from 'react-native-onyx';
66
import type {ValueOf} from 'type-fest';
77
import CheckboxWithLabel from '@components/CheckboxWithLabel';
@@ -16,11 +16,10 @@ import TextInput from '@components/TextInput';
1616
import useLocalize from '@hooks/useLocalize';
1717
import useThemeStyles from '@hooks/useThemeStyles';
1818
import {addErrorMessage, getLatestErrorMessage} from '@libs/ErrorUtils';
19-
import {appendCountryCode, getPhoneNumberWithoutSpecialChars} from '@libs/LoginUtils';
19+
import {getPhoneLogin, validateNumber} from '@libs/LoginUtils';
2020
import Navigation from '@libs/Navigation/Navigation';
2121
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
2222
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
23-
import {parsePhoneNumber} from '@libs/PhoneNumber';
2423
import {isNumericWithSpecialChars} from '@libs/ValidationUtils';
2524
import {clearGetValidateCodeForAccountMerge, requestValidationCodeForAccountMerge} from '@userActions/MergeAccounts';
2625
import CONST from '@src/CONST';
@@ -52,6 +51,7 @@ const getValidateCodeErrorKey = (err: string): ValueOf<typeof CONST.MERGE_ACCOUN
5251

5352
function AccountDetailsPage() {
5453
const formRef = useRef<FormRef>(null);
54+
const navigation = useNavigation();
5555
const [userEmailOrPhone] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email, canBeMissing: true});
5656
const [getValidateCodeForAccountMerge] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.getValidateCodeForAccountMerge, canBeMissing: true});
5757
const {params} = useRoute<PlatformStackRouteProp<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_DETAILS>>();
@@ -67,30 +67,38 @@ function AccountDetailsPage() {
6767

6868
useFocusEffect(
6969
useCallback(() => {
70-
if (!validateCodeSent || !email) {
71-
return;
72-
}
70+
const task = InteractionManager.runAfterInteractions(() => {
71+
if (!validateCodeSent || !email) {
72+
return;
73+
}
74+
75+
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_MAGIC_CODE.getRoute(email));
76+
});
7377

74-
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_MAGIC_CODE.getRoute(email));
78+
return () => task.cancel();
7579
}, [validateCodeSent, email]),
7680
);
7781

7882
useFocusEffect(
7983
useCallback(() => {
80-
if (!errorKey || !email) {
81-
return;
82-
}
83-
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorKey));
84+
const task = InteractionManager.runAfterInteractions(() => {
85+
if (!errorKey || !email) {
86+
return;
87+
}
88+
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorKey));
89+
});
90+
91+
return () => task.cancel();
8492
}, [errorKey, email]),
8593
);
8694

87-
useFocusEffect(
88-
useCallback(() => {
89-
return () => {
90-
clearGetValidateCodeForAccountMerge();
91-
};
92-
}, []),
93-
);
95+
useEffect(() => {
96+
const unsubscribe = navigation.addListener('blur', () => {
97+
clearGetValidateCodeForAccountMerge();
98+
});
99+
100+
return unsubscribe;
101+
}, [navigation]);
94102

95103
const validate = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.MERGE_ACCOUNT_DETAILS_FORM>): Errors => {
96104
const errors = {};
@@ -99,11 +107,13 @@ function AccountDetailsPage() {
99107

100108
if (!login) {
101109
addErrorMessage(errors, INPUT_IDS.PHONE_OR_EMAIL, translate('common.pleaseEnterEmailOrPhoneNumber'));
110+
} else if (login.trim() === userEmailOrPhone) {
111+
addErrorMessage(errors, INPUT_IDS.PHONE_OR_EMAIL, translate('common.error.email'));
102112
} else {
103-
const phoneLogin = appendCountryCode(getPhoneNumberWithoutSpecialChars(login));
104-
const parsedPhoneNumber = parsePhoneNumber(phoneLogin);
113+
const phoneLogin = getPhoneLogin(login);
114+
const validateIfNumber = validateNumber(phoneLogin);
105115

106-
if (!Str.isValidEmail(login) && !parsedPhoneNumber.possible) {
116+
if (!Str.isValidEmail(login) && !validateIfNumber) {
107117
if (isNumericWithSpecialChars(login)) {
108118
addErrorMessage(errors, INPUT_IDS.PHONE_OR_EMAIL, translate('common.error.phoneNumber'));
109119
} else {

src/pages/settings/Security/MergeAccounts/AccountValidatePage.tsx

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {useFocusEffect, useRoute} from '@react-navigation/native';
2-
import React, {useCallback, useRef} from 'react';
1+
import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native';
2+
import React, {useCallback, useEffect, useRef} from 'react';
33
import {View} from 'react-native';
44
import {useOnyx} from 'react-native-onyx';
55
import type {ValueOf} from 'type-fest';
@@ -22,11 +22,12 @@ import {
2222
requestValidationCodeForAccountMerge,
2323
} from '@userActions/MergeAccounts';
2424
import CONST from '@src/CONST';
25+
import type {TranslationPaths} from '@src/languages/types';
2526
import ONYXKEYS from '@src/ONYXKEYS';
2627
import ROUTES from '@src/ROUTES';
2728
import type SCREENS from '@src/SCREENS';
2829

29-
const getMergeErrorKey = (err: string): ValueOf<typeof CONST.MERGE_ACCOUNT_RESULTS> | null => {
30+
const getMergeErrorPage = (err: string): ValueOf<typeof CONST.MERGE_ACCOUNT_RESULTS> | null => {
3031
if (err.includes('403')) {
3132
return CONST.MERGE_ACCOUNT_RESULTS.TOO_MANY_ATTEMPTS;
3233
}
@@ -58,8 +59,22 @@ const getMergeErrorKey = (err: string): ValueOf<typeof CONST.MERGE_ACCOUNT_RESUL
5859
return null;
5960
};
6061

62+
const getAuthenticationErrorKey = (err: string): TranslationPaths | null => {
63+
if (!err) {
64+
return null;
65+
}
66+
67+
if (err.includes('Invalid validateCode')) {
68+
return 'mergeAccountsPage.accountValidate.errors.incorrect2fa';
69+
}
70+
71+
return 'mergeAccountsPage.accountValidate.errors.fallback';
72+
};
73+
6174
function AccountValidatePage() {
6275
const validateCodeFormRef = useRef<ValidateCodeFormHandle>(null);
76+
const navigation = useNavigation();
77+
6378
const [account] = useOnyx(ONYXKEYS.ACCOUNT, {
6479
selector: (data) => ({
6580
mergeWithValidateCode: data?.mergeWithValidateCode,
@@ -78,7 +93,7 @@ function AccountValidatePage() {
7893
const isAccountMerged = mergeWithValidateCode?.isAccountMerged;
7994

8095
const latestError = getLatestErrorMessage(mergeWithValidateCode);
81-
const errorKey = getMergeErrorKey(latestError);
96+
const errorPage = getMergeErrorPage(latestError);
8297

8398
const styles = useThemeStyles();
8499
const {translate} = useLocalize();
@@ -94,21 +109,24 @@ function AccountValidatePage() {
94109

95110
useFocusEffect(
96111
useCallback(() => {
97-
if (!errorKey || !email) {
112+
if (!errorPage || !email) {
98113
return;
99114
}
100-
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorKey), {forceReplace: true});
101-
}, [errorKey, email]),
115+
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorPage), {forceReplace: true});
116+
}, [errorPage, email]),
102117
);
103118

104-
useFocusEffect(
105-
useCallback(() => {
106-
return () => {
107-
clearMergeWithValidateCode();
108-
clearGetValidateCodeForAccountMerge();
109-
};
110-
}, []),
111-
);
119+
useEffect(() => {
120+
const unsubscribe = navigation.addListener('blur', () => {
121+
clearGetValidateCodeForAccountMerge();
122+
clearMergeWithValidateCode();
123+
});
124+
125+
return unsubscribe;
126+
}, [navigation]);
127+
128+
const authenticationErrorKey = getAuthenticationErrorKey(latestError);
129+
const validateCodeError = !errorPage && authenticationErrorKey ? {authError: translate(authenticationErrorKey)} : undefined;
112130

113131
return (
114132
<ScreenWrapper
@@ -131,7 +149,7 @@ function AccountValidatePage() {
131149
descriptionPrimary={translate('mergeAccountsPage.accountValidate.confirmMerge')}
132150
descriptionPrimaryStyles={{...styles.mb8, ...styles.textStrong}}
133151
descriptionSecondary={
134-
<View>
152+
<View style={[styles.w100]}>
135153
<Text style={[styles.mb8]}>
136154
{translate('mergeAccountsPage.accountValidate.lossOfUnsubmittedData')}
137155
<Text style={styles.textStrong}>{email}</Text>.
@@ -147,12 +165,12 @@ function AccountValidatePage() {
147165
mergeWithValidateCodeAction(email, code);
148166
}}
149167
sendValidateCode={() => {
150-
requestValidationCodeForAccountMerge(email);
168+
requestValidationCodeForAccountMerge(email, true);
151169
}}
152170
shouldSkipInitialValidation
153171
clearError={() => clearMergeWithValidateCode()}
154-
validateError={!errorKey ? mergeWithValidateCode?.errors : undefined}
155-
hasMagicCodeBeenSent={getValidateCodeForAccountMerge?.validateCodeSent}
172+
validateError={validateCodeError}
173+
hasMagicCodeBeenSent={getValidateCodeForAccountMerge?.validateCodeResent}
156174
submitButtonText={translate('mergeAccountsPage.mergeAccount')}
157175
forwardedRef={validateCodeFormRef}
158176
isLoading={mergeWithValidateCode?.isLoading}

src/types/onyx/Account.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ type Account = {
183183
/** Whether the user validation code was sent */
184184
validateCodeSent?: boolean;
185185

186+
/** Whether the user validation code was re-sent */
187+
validateCodeResent?: boolean;
188+
186189
/** Errors while requesting the validation code */
187190
errors: OnyxCommon.Errors;
188191
};

0 commit comments

Comments
 (0)