Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: requires validate when opening bank account #49230

Merged
merged 16 commits into from
Oct 2, 2024
Merged
Prev Previous commit
Next Next commit
fix: add Verify account page
  • Loading branch information
daledah committed Sep 17, 2024
commit f7310b12002725990f130ebd597f41d13bf1d7aa
1 change: 1 addition & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ const ROUTES = {
SETTINGS_ABOUT: 'settings/about',
SETTINGS_APP_DOWNLOAD_LINKS: 'settings/about/app-download-links',
SETTINGS_WALLET: 'settings/wallet',
SETTINGS_WALLET_VERIFY_ACCOUNT: {route: 'settings/wallet/verify', getRoute: (backTo?: string) => getUrlWithBackToParam('settings/wallet/verify', backTo)},
SETTINGS_WALLET_DOMAINCARD: {
route: 'settings/wallet/card/:cardID?',
getRoute: (cardID: string) => `settings/wallet/card/${cardID}` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ const SCREENS = {
CARD_ACTIVATE: 'Settings_Wallet_Card_Activate',
REPORT_VIRTUAL_CARD_FRAUD: 'Settings_Wallet_ReportVirtualCardFraud',
CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS: 'Settings_Wallet_Cards_Digital_Details_Update_Address',
VERIFY_ACCOUNT: 'Settings_Wallet_Verify_Account',
daledah marked this conversation as resolved.
Show resolved Hide resolved
},

EXIT_SURVEY: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.SETTINGS.WALLET.TRANSFER_BALANCE]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/TransferBalancePage').default,
[SCREENS.SETTINGS.WALLET.CHOOSE_TRANSFER_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/ChooseTransferAccountPage').default,
[SCREENS.SETTINGS.WALLET.ENABLE_PAYMENTS]: () => require<ReactComponentModule>('../../../../pages/EnablePayments/EnablePayments').default,
[SCREENS.SETTINGS.WALLET.VERIFY_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/VerifyAccountPage').default,
[SCREENS.SETTINGS.ADD_DEBIT_CARD]: () => require<ReactComponentModule>('../../../../pages/settings/Wallet/AddDebitCardPage').default,
[SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/AddPersonalBankAccountPage').default,
[SCREENS.SETTINGS.PROFILE.STATUS]: () => require<ReactComponentModule>('../../../../pages/settings/Profile/CustomStatus/StatusPage').default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial<Record<CentralPaneName, string[]>> =
SCREENS.SETTINGS.WALLET.CARD_ACTIVATE,
SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD,
SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS,
SCREENS.SETTINGS.WALLET.VERIFY_ACCOUNT,
],
[SCREENS.SETTINGS.SECURITY]: [
SCREENS.SETTINGS.TWO_FACTOR_AUTH,
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
path: ROUTES.SETTINGS_CLOSE,
exact: true,
},
[SCREENS.SETTINGS.WALLET.VERIFY_ACCOUNT]: {
path: ROUTES.SETTINGS_WALLET_VERIFY_ACCOUNT.route,
exact: true,
},
[SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: {
path: ROUTES.SETTINGS_WALLET_DOMAINCARD.route,
exact: true,
Expand Down
104 changes: 35 additions & 69 deletions src/pages/AddPersonalBankAccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,13 @@ import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import ValidateCodeActionModal from '@components/ValidateCodeActionModal';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import getPlaidOAuthReceivedRedirectURI from '@libs/getPlaidOAuthReceivedRedirectURI';
import Navigation from '@libs/Navigation/Navigation';
import * as BankAccounts from '@userActions/BankAccounts';
import * as PaymentMethods from '@userActions/PaymentMethods';
import * as User from '@userActions/User';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';
import type {PersonalBankAccount, PlaidData} from '@src/types/onyx';

Expand All @@ -35,23 +31,8 @@ function AddPersonalBankAccountPage({personalBankAccount, plaidData}: AddPersona
const {translate} = useLocalize();
const [selectedPlaidAccountId, setSelectedPlaidAccountId] = useState('');
const [isUserValidated] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.validated});
const [isValidateCodeActionModalVisible, setIsValidateCodeActionModalVisible] = useState(!isUserValidated);
const shouldShowSuccess = personalBankAccount?.shouldShowSuccess ?? false;

const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST);
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const primaryLogin = account?.primaryLogin;
const loginData = loginList?.[primaryLogin ?? ''];
const validateLoginError = ErrorUtils.getEarliestErrorField(loginData, 'validateLogin');

const handleSubmitForm = useCallback(
(submitCode: string) => {
User.validateSecondaryLogin(loginList, primaryLogin ?? '', submitCode);
Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT);
},
[loginList, primaryLogin],
);

const submitBankAccountForm = useCallback(() => {
const bankAccounts = plaidData?.bankAccounts ?? [];
const selectedPlaidBankAccount = bankAccounts.find((bankAccount) => bankAccount.plaidAccountID === selectedPlaidAccountId);
Expand Down Expand Up @@ -86,58 +67,43 @@ function AddPersonalBankAccountPage({personalBankAccount, plaidData}: AddPersona
shouldShowOfflineIndicator={false}
testID={AddPersonalBankAccountPage.displayName}
>
{isUserValidated && (
<FullPageNotFoundView>
<HeaderWithBackButton
title={translate('bankAccount.addBankAccount')}
onBackButtonPress={exitFlow}
<FullPageNotFoundView shouldShow={!isUserValidated}>
<HeaderWithBackButton
title={translate('bankAccount.addBankAccount')}
onBackButtonPress={exitFlow}
/>
{shouldShowSuccess ? (
<ConfirmationPage
heading={translate('addPersonalBankAccountPage.successTitle')}
description={translate('addPersonalBankAccountPage.successMessage')}
shouldShowButton
buttonText={translate('common.continue')}
onButtonPress={() => exitFlow(true)}
/>
{shouldShowSuccess ? (
<ConfirmationPage
heading={translate('addPersonalBankAccountPage.successTitle')}
description={translate('addPersonalBankAccountPage.successMessage')}
shouldShowButton
buttonText={translate('common.continue')}
onButtonPress={() => exitFlow(true)}
) : (
<FormProvider
formID={ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_FORM}
isSubmitButtonVisible={(plaidData?.bankAccounts ?? []).length > 0}
submitButtonText={translate('common.saveAndContinue')}
scrollContextEnabled
onSubmit={submitBankAccountForm}
validate={BankAccounts.validatePlaidSelection}
style={[styles.mh5, styles.flex1]}
>
<InputWrapper
inputID={INPUT_IDS.BANK_INFO_STEP.SELECTED_PLAID_ACCOUNT_ID}
InputComponent={AddPlaidBankAccount}
onSelect={setSelectedPlaidAccountId}
text={translate('walletPage.chooseAccountBody')}
plaidData={plaidData}
isDisplayedInWalletFlow
onExitPlaid={() => Navigation.goBack()}
receivedRedirectURI={getPlaidOAuthReceivedRedirectURI()}
selectedPlaidAccountID={selectedPlaidAccountId}
/>
) : (
<FormProvider
formID={ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT_FORM}
isSubmitButtonVisible={(plaidData?.bankAccounts ?? []).length > 0}
submitButtonText={translate('common.saveAndContinue')}
scrollContextEnabled
onSubmit={submitBankAccountForm}
validate={BankAccounts.validatePlaidSelection}
style={[styles.mh5, styles.flex1]}
>
<InputWrapper
inputID={INPUT_IDS.BANK_INFO_STEP.SELECTED_PLAID_ACCOUNT_ID}
InputComponent={AddPlaidBankAccount}
onSelect={setSelectedPlaidAccountId}
text={translate('walletPage.chooseAccountBody')}
plaidData={plaidData}
isDisplayedInWalletFlow
onExitPlaid={() => Navigation.goBack()}
receivedRedirectURI={getPlaidOAuthReceivedRedirectURI()}
selectedPlaidAccountID={selectedPlaidAccountId}
/>
</FormProvider>
)}
</FullPageNotFoundView>
)}
<ValidateCodeActionModal
validatePendingAction={loginList?.[primaryLogin ?? '-1']?.pendingFields?.validateLogin}
validateError={validateLoginError}
isVisible={isValidateCodeActionModalVisible}
title={translate('contacts.validateAccount')}
description={translate('contacts.featureRequiresValidate')}
onClose={() => {
setIsValidateCodeActionModalVisible(false);
exitFlow();
}}
handleSubmitForm={handleSubmitForm}
clearError={() => {}}
/>
</FormProvider>
)}
</FullPageNotFoundView>
</ScreenWrapper>
);
}
Expand Down
11 changes: 2 additions & 9 deletions src/pages/EnablePayments/AddBankAccount/SetupMethod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,8 @@ import * as BankAccounts from '@userActions/BankAccounts';
import * as Link from '@userActions/Link';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {User} from '@src/types/onyx';

type SetupMethodOnyxProps = {
/** The user's data */
user: OnyxEntry<User>;

/** Whether Plaid is disabled */
isPlaidDisabled: OnyxEntry<boolean>;
};
Expand All @@ -29,7 +25,7 @@ type SetupMethodProps = SetupMethodOnyxProps;

const plaidDesktopMessage = getPlaidDesktopMessage();

function SetupMethod({isPlaidDisabled, user}: SetupMethodProps) {
function SetupMethod({isPlaidDisabled}: SetupMethodProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

Expand All @@ -52,7 +48,7 @@ function SetupMethod({isPlaidDisabled, user}: SetupMethodProps) {
icon={Expensicons.Bank}
text={translate('bankAccount.addBankAccount')}
onPress={() => BankAccounts.openPersonalBankAccountSetupWithPlaid()}
isDisabled={!!isPlaidDisabled || !user?.validated}
isDisabled={!!isPlaidDisabled}
style={[styles.mt4, styles.mb2]}
iconStyles={styles.buttonCTAIcon}
shouldShowRightIcon
Expand All @@ -70,7 +66,4 @@ export default withOnyx<SetupMethodProps, SetupMethodOnyxProps>({
isPlaidDisabled: {
key: ONYXKEYS.IS_PLAID_DISABLED,
},
user: {
key: ONYXKEYS.USER,
},
})(SetupMethod);
16 changes: 13 additions & 3 deletions src/pages/settings/Wallet/PaymentMethodList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, {useCallback, useMemo} from 'react';
import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native';
import {FlatList, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import {useOnyx, withOnyx} from 'react-native-onyx';
import type {SvgProps} from 'react-native-svg/lib/typescript/ReactNativeSVG';
import type {ValueOf} from 'type-fest';
import type {RenderSuggestionMenuItemProps} from '@components/AutoCompleteSuggestions/types';
Expand Down Expand Up @@ -199,6 +199,8 @@ function PaymentMethodList({
const {translate} = useLocalize();
const {isOffline} = useNetwork();

const [isUserValidated] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.validated});

const getDescriptionForPolicyDomainCard = (domainName: string): string => {
// A domain name containing a policyID indicates that this is a workspace feed
const policyID = domainName.match(CONST.REGEX.EXPENSIFY_POLICY_DOMAIN_NAME)?.[1];
Expand Down Expand Up @@ -322,17 +324,25 @@ function PaymentMethodList({
*/
const renderListEmptyComponent = () => <Text style={styles.popoverMenuItem}>{translate('paymentMethodList.addFirstPaymentMethod')}</Text>;

const onPressItem = useCallback(() => {
if (!isUserValidated) {
Navigation.navigate(ROUTES.SETTINGS_WALLET_VERIFY_ACCOUNT.getRoute(ROUTES.SETTINGS_ADD_BANK_ACCOUNT));
return;
}
onPress();
}, [isUserValidated, onPress]);

const renderListFooterComponent = useCallback(
() => (
<MenuItem
onPress={onPress}
onPress={onPressItem}
title={translate('walletPage.addBankAccount')}
icon={Expensicons.Plus}
wrapperStyle={[styles.paymentMethod, listItemStyle]}
ref={buttonRef}
/>
),
[onPress, translate, styles.paymentMethod, listItemStyle, buttonRef],
[translate, styles.paymentMethod, listItemStyle, buttonRef, onPressItem],
);

/**
Expand Down
73 changes: 73 additions & 0 deletions src/pages/settings/Wallet/VerifyAccountPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback, useEffect, useRef} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import type {AnimatedTextInputRef} from '@components/RNTextInput';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import ValidateCodeForm from '@components/ValidateCodeActionModal/ValidateCodeForm';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import * as User from '@userActions/User';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';

type VerifyAccountPageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.PROFILE.NEW_CONTACT_METHOD>;

function VerifyAccountPage({route}: VerifyAccountPageProps) {
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST);
const [pendingContactAction] = useOnyx(ONYXKEYS.PENDING_CONTACT_ACTION);
const contactMethod = account?.primaryLogin ?? '';
const themeStyles = useThemeStyles();
const {translate} = useLocalize();
const loginInputRef = useRef<AnimatedTextInputRef>(null);
const loginData = loginList?.[pendingContactAction?.contactMethod ?? contactMethod];
const validateLoginError = ErrorUtils.getEarliestErrorField(loginData, 'validateLogin');

const [validateCodeAction] = useOnyx(ONYXKEYS.VALIDATE_ACTION_CODE);

const navigateBackTo = route?.params?.backTo ?? ROUTES.SETTINGS_WALLET;

useEffect(() => () => User.clearUnvalidatedNewContactMethodAction(), []);

const handleSubmitForm = useCallback(
(submitCode: string) => {
User.validateSecondaryLogin(loginList, contactMethod ?? '', submitCode);
Navigation.navigate(navigateBackTo);
},
[loginList, contactMethod, navigateBackTo],
);

return (
<ScreenWrapper
onEntryTransitionEnd={() => loginInputRef.current?.focus()}
includeSafeAreaPaddingBottom={false}
shouldEnableMaxHeight
testID={VerifyAccountPage.displayName}
>
<HeaderWithBackButton
title={translate('contacts.validateAccount')}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_ADD_BANK_ACCOUNT)}
/>
<View style={[themeStyles.ph5, themeStyles.mt3, themeStyles.mb7]}>
<Text style={[themeStyles.mb3]}>{translate('contacts.featureRequiresValidate')}</Text>
<ValidateCodeForm
validateCodeAction={validateCodeAction}
validateError={validateLoginError}
handleSubmitForm={handleSubmitForm}
clearError={() => {}}
/>
</View>
</ScreenWrapper>
);
}

VerifyAccountPage.displayName = 'VerifyAccountPage';

export default VerifyAccountPage;
Loading