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
2 changes: 1 addition & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,7 @@ const CONST = {
DELAYED_SUBMISSION_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/reports/Automatically-submit-employee-reports',
ENCRYPTION_AND_SECURITY_HELP_URL: 'https://help.expensify.com/articles/new-expensify/settings/Encryption-and-Data-Security',
PLAN_TYPES_AND_PRICING_HELP_URL: 'https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Plan-types-and-pricing',
MERGE_ACCOUNT_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/settings/Merge-accounts',
MERGE_ACCOUNT_HELP_URL: 'https://help.expensify.com/articles/new-expensify/settings/Merge-Accounts',
CONNECT_A_BUSINESS_BANK_ACCOUNT_HELP_URL: 'https://help.expensify.com/articles/new-expensify/expenses-&-payments/Connect-a-Business-Bank-Account',
TEST_RECEIPT_URL: `${CLOUDFRONT_URL}/images/fake-receipt__tacotodds.png`,
// Use Environment.getEnvironmentURL to get the complete URL with port number
Expand Down
2 changes: 1 addition & 1 deletion src/components/ConfirmationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function ConfirmationPage({
</View>
)}
<Text style={[styles.textHeadline, styles.textAlignCenter, styles.mv2, headingStyle]}>{heading}</Text>
<Text style={[styles.textAlignCenter, descriptionStyle]}>{description}</Text>
<Text style={[styles.textAlignCenter, descriptionStyle, styles.w100]}>{description}</Text>
Comment thread
allroundexperts marked this conversation as resolved.
{cta ? <Text style={[styles.textAlignCenter, ctaStyle]}>{cta}</Text> : null}
</View>
{(shouldShowSecondaryButton || shouldShowButton) && (
Expand Down
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,10 @@ const translations = {
confirmMerge: 'Are you sure you want to merge accounts?',
lossOfUnsubmittedData: `Merging your accounts is irreversible and will result in the loss of any unsubmitted expenses for `,
enterMagicCode: `To continue, please enter the magic code sent to `,
errors: {
incorrect2fa: 'Incorrect two-factor authentication code. Please try again.',
fallback: 'Something went wrong. Please try again later.',
},
},
mergeSuccess: {
accountsMerged: 'Accounts merged!',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,10 @@ const translations = {
confirmMerge: '¿Estás seguro de que deseas fusionar cuentas?',
lossOfUnsubmittedData: `Fusionar tus cuentas es irreversible y resultará en la pérdida de cualquier gasto no enviado de `,
enterMagicCode: `Para continuar, por favor introduce el código mágico enviado a `,
errors: {
incorrect2fa: 'Código de autenticación de dos factores incorrecto. Por favor, inténtalo de nuevo.',
fallback: 'Ha ocurrido un error. Por favor, inténtalo mas tarde.',
},
},
mergeSuccess: {
accountsMerged: '¡Cuentas fusionadas!',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial<Record<keyof SettingsSplitNavigatorPa
SCREENS.SETTINGS.DELEGATE.DELEGATE_ROLE,
SCREENS.SETTINGS.DELEGATE.UPDATE_DELEGATE_ROLE,
SCREENS.SETTINGS.DELEGATE.DELEGATE_CONFIRM,
SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_DETAILS,
SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_VALIDATE,
SCREENS.SETTINGS.MERGE_ACCOUNTS.MERGE_RESULT,
],
[SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS],
[SCREENS.SETTINGS.SAVE_THE_WORLD]: [SCREENS.I_KNOW_A_TEACHER, SCREENS.INTRO_SCHOOL_PRINCIPAL, SCREENS.I_AM_A_TEACHER],
Expand Down
8 changes: 6 additions & 2 deletions src/libs/actions/MergeAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Onyx.connect({
callback: (value) => (session = value ?? {}),
});

function requestValidationCodeForAccountMerge(email: string) {
function requestValidationCodeForAccountMerge(email: string, validateCodeResent = false) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
Expand All @@ -21,6 +21,7 @@ function requestValidationCodeForAccountMerge(email: string) {
getValidateCodeForAccountMerge: {
isLoading: true,
validateCodeSent: false,
validateCodeResent: false,
errors: null,
},
},
Expand All @@ -34,7 +35,8 @@ function requestValidationCodeForAccountMerge(email: string) {
value: {
getValidateCodeForAccountMerge: {
isLoading: false,
validateCodeSent: true,
validateCodeSent: !validateCodeResent,
validateCodeResent,
errors: null,
},
},
Expand All @@ -49,6 +51,7 @@ function requestValidationCodeForAccountMerge(email: string) {
getValidateCodeForAccountMerge: {
isLoading: false,
validateCodeSent: false,
validateCodeResent: false,
},
},
},
Expand All @@ -67,6 +70,7 @@ function clearGetValidateCodeForAccountMerge() {
getValidateCodeForAccountMerge: {
errors: null,
validateCodeSent: false,
validateCodeResent: false,
isLoading: false,
},
});
Expand Down
56 changes: 33 additions & 23 deletions src/pages/settings/Security/MergeAccounts/AccountDetailsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useFocusEffect, useRoute} from '@react-navigation/native';
import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native';
import {Str} from 'expensify-common';
import React, {useCallback, useRef, useState} from 'react';
import {View} from 'react-native';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {InteractionManager, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import CheckboxWithLabel from '@components/CheckboxWithLabel';
Expand All @@ -16,11 +16,10 @@ import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {addErrorMessage, getLatestErrorMessage} from '@libs/ErrorUtils';
import {appendCountryCode, getPhoneNumberWithoutSpecialChars} from '@libs/LoginUtils';
import {getPhoneLogin, validateNumber} from '@libs/LoginUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import {parsePhoneNumber} from '@libs/PhoneNumber';
import {isNumericWithSpecialChars} from '@libs/ValidationUtils';
import {clearGetValidateCodeForAccountMerge, requestValidationCodeForAccountMerge} from '@userActions/MergeAccounts';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -52,6 +51,7 @@ const getValidateCodeErrorKey = (err: string): ValueOf<typeof CONST.MERGE_ACCOUN

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

useFocusEffect(
useCallback(() => {
if (!validateCodeSent || !email) {
return;
}
const task = InteractionManager.runAfterInteractions(() => {
if (!validateCodeSent || !email) {
return;
}

return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_MAGIC_CODE.getRoute(email));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Came from this issue
It is better to trim the email to avoid this issue

});

return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_MAGIC_CODE.getRoute(email));
return () => task.cancel();
Comment thread
allroundexperts marked this conversation as resolved.
}, [validateCodeSent, email]),
);

useFocusEffect(
useCallback(() => {
if (!errorKey || !email) {
return;
}
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorKey));
const task = InteractionManager.runAfterInteractions(() => {
if (!errorKey || !email) {
return;
}
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorKey));
});

return () => task.cancel();
}, [errorKey, email]),
);

useFocusEffect(
useCallback(() => {
return () => {
clearGetValidateCodeForAccountMerge();
};
}, []),
);
useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
clearGetValidateCodeForAccountMerge();
});

return unsubscribe;
}, [navigation]);

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

if (!login) {
addErrorMessage(errors, INPUT_IDS.PHONE_OR_EMAIL, translate('common.pleaseEnterEmailOrPhoneNumber'));
} else if (login.trim() === userEmailOrPhone) {
addErrorMessage(errors, INPUT_IDS.PHONE_OR_EMAIL, translate('common.error.email'));
Comment thread
allroundexperts marked this conversation as resolved.
Comment thread
allroundexperts marked this conversation as resolved.
} else {
const phoneLogin = appendCountryCode(getPhoneNumberWithoutSpecialChars(login));
const parsedPhoneNumber = parsePhoneNumber(phoneLogin);
const phoneLogin = getPhoneLogin(login);
const validateIfNumber = validateNumber(phoneLogin);

if (!Str.isValidEmail(login) && !parsedPhoneNumber.possible) {
if (!Str.isValidEmail(login) && !validateIfNumber) {
if (isNumericWithSpecialChars(login)) {
addErrorMessage(errors, INPUT_IDS.PHONE_OR_EMAIL, translate('common.error.phoneNumber'));
} else {
Expand Down
56 changes: 37 additions & 19 deletions src/pages/settings/Security/MergeAccounts/AccountValidatePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useFocusEffect, useRoute} from '@react-navigation/native';
import React, {useCallback, useRef} from 'react';
import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native';
import React, {useCallback, useEffect, useRef} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
Expand All @@ -22,11 +22,12 @@ import {
requestValidationCodeForAccountMerge,
} from '@userActions/MergeAccounts';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';

const getMergeErrorKey = (err: string): ValueOf<typeof CONST.MERGE_ACCOUNT_RESULTS> | null => {
const getMergeErrorPage = (err: string): ValueOf<typeof CONST.MERGE_ACCOUNT_RESULTS> | null => {
if (err.includes('403')) {
return CONST.MERGE_ACCOUNT_RESULTS.TOO_MANY_ATTEMPTS;
}
Expand Down Expand Up @@ -58,8 +59,22 @@ const getMergeErrorKey = (err: string): ValueOf<typeof CONST.MERGE_ACCOUNT_RESUL
return null;
};

const getAuthenticationErrorKey = (err: string): TranslationPaths | null => {
if (!err) {
return null;
}

if (err.includes('Invalid validateCode')) {
return 'mergeAccountsPage.accountValidate.errors.incorrect2fa';
}

return 'mergeAccountsPage.accountValidate.errors.fallback';
};

function AccountValidatePage() {
const validateCodeFormRef = useRef<ValidateCodeFormHandle>(null);
const navigation = useNavigation();

const [account] = useOnyx(ONYXKEYS.ACCOUNT, {
selector: (data) => ({
mergeWithValidateCode: data?.mergeWithValidateCode,
Expand All @@ -78,7 +93,7 @@ function AccountValidatePage() {
const isAccountMerged = mergeWithValidateCode?.isAccountMerged;

const latestError = getLatestErrorMessage(mergeWithValidateCode);
const errorKey = getMergeErrorKey(latestError);
const errorPage = getMergeErrorPage(latestError);

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

useFocusEffect(
useCallback(() => {
if (!errorKey || !email) {
if (!errorPage || !email) {
return;
}
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorKey), {forceReplace: true});
}, [errorKey, email]),
return Navigation.navigate(ROUTES.SETTINGS_MERGE_ACCOUNTS_RESULT.getRoute(email, errorPage), {forceReplace: true});
}, [errorPage, email]),
);

useFocusEffect(
useCallback(() => {
return () => {
clearMergeWithValidateCode();
clearGetValidateCodeForAccountMerge();
};
}, []),
);
useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
clearGetValidateCodeForAccountMerge();
clearMergeWithValidateCode();
});

return unsubscribe;
}, [navigation]);

const authenticationErrorKey = getAuthenticationErrorKey(latestError);
const validateCodeError = !errorPage && authenticationErrorKey ? {authError: translate(authenticationErrorKey)} : undefined;

return (
<ScreenWrapper
Expand All @@ -131,7 +149,7 @@ function AccountValidatePage() {
descriptionPrimary={translate('mergeAccountsPage.accountValidate.confirmMerge')}
descriptionPrimaryStyles={{...styles.mb8, ...styles.textStrong}}
descriptionSecondary={
<View>
<View style={[styles.w100]}>
<Text style={[styles.mb8]}>
{translate('mergeAccountsPage.accountValidate.lossOfUnsubmittedData')}
<Text style={styles.textStrong}>{email}</Text>.
Expand All @@ -147,12 +165,12 @@ function AccountValidatePage() {
mergeWithValidateCodeAction(email, code);
}}
sendValidateCode={() => {
requestValidationCodeForAccountMerge(email);
requestValidationCodeForAccountMerge(email, true);
}}
shouldSkipInitialValidation
clearError={() => clearMergeWithValidateCode()}
validateError={!errorKey ? mergeWithValidateCode?.errors : undefined}
hasMagicCodeBeenSent={getValidateCodeForAccountMerge?.validateCodeSent}
validateError={validateCodeError}
hasMagicCodeBeenSent={getValidateCodeForAccountMerge?.validateCodeResent}
submitButtonText={translate('mergeAccountsPage.mergeAccount')}
forwardedRef={validateCodeFormRef}
isLoading={mergeWithValidateCode?.isLoading}
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ type Account = {
/** Whether the user validation code was sent */
validateCodeSent?: boolean;

/** Whether the user validation code was re-sent */
validateCodeResent?: boolean;

/** Errors while requesting the validation code */
errors: OnyxCommon.Errors;
};
Expand Down