From e6d5cba20cd2a938963f3be8edc34799e2345ccc Mon Sep 17 00:00:00 2001 From: OSBotify Date: Thu, 2 Jan 2025 12:55:12 +0000 Subject: [PATCH 1/3] Update version to 9.0.80-5 (cherry picked from commit c2a37a28cb4e3bbccea526ba89e75396a5fcce6d) --- android/app/build.gradle | 4 ++-- ios/NewExpensify/Info.plist | 2 +- ios/NewExpensifyTests/Info.plist | 2 +- ios/NotificationServiceExtension/Info.plist | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 28af56bb533e..7e16782ff441 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009008004 - versionName "9.0.80-4" + versionCode 1009008005 + versionName "9.0.80-5" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 87132775f94a..0de8a61e5abf 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.80.4 + 9.0.80.5 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 81ec881a0cb7..6e80e2e5120a 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.80.4 + 9.0.80.5 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 0154e4399073..0959ffc8873c 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.80 CFBundleVersion - 9.0.80.4 + 9.0.80.5 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index df2458b7751a..e8bf241b038f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.80-4", + "version": "9.0.80-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.80-4", + "version": "9.0.80-5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index e518e1decced..541005caaebc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.80-4", + "version": "9.0.80-5", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", From 1f9eb85c83c393a00a38e73b1586e3e9f5a89b1c Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Thu, 2 Jan 2025 14:36:24 +0200 Subject: [PATCH 2/3] Merge pull request #54729 from Expensify/revert-52322-intl-bank-account Revert "Implemented International Bank Account flow" (cherry picked from commit a2c8842a42f7c005e453b75f32eaf469ebc33dc2) (CP triggered by Beamanator) --- src/CONST.ts | 45 --- src/ONYXKEYS.ts | 3 - src/ROUTES.ts | 1 - src/SCREENS.ts | 1 - src/components/CurrencyPicker.tsx | 86 ----- src/components/Form/FormProvider.tsx | 9 +- src/components/Form/FormWrapper.tsx | 7 +- .../SelectionList/BaseSelectionList.tsx | 4 +- src/components/SelectionList/types.ts | 3 - src/components/TextPicker/index.tsx | 12 +- src/components/TextPicker/types.ts | 2 +- .../useInternationalBankAccountFormSubmit.ts | 27 -- src/hooks/useSubStep/index.ts | 49 +-- src/hooks/useSubStep/types.ts | 6 - src/languages/en.ts | 9 - src/languages/es.ts | 9 - .../BankAccountCreateCorpayParams.ts | 2 +- .../GetCorpayBankAccountFieldsParams.ts | 6 +- .../VerifyIdentityForBankAccountParams.ts | 2 +- src/libs/API/parameters/index.ts | 4 +- src/libs/API/types.ts | 4 - .../ModalStackNavigators/index.tsx | 3 +- src/libs/Navigation/linkingConfig/config.ts | 4 - src/libs/Navigation/types.ts | 1 - src/libs/PaymentUtils.ts | 6 +- src/libs/actions/BankAccounts.ts | 68 +--- .../ReimbursementAccount/BankAccountStep.tsx | 4 +- .../NonUSD/BankInfo/types.ts | 6 +- .../RequestorOnfidoStep.tsx | 97 ++++++ .../InternationalDepositAccountContent.tsx | 140 --------- .../InternationalDepositAccount/index.tsx | 36 --- .../substeps/AccountHolderInformation.tsx | 130 -------- .../substeps/AccountType.tsx | 75 ----- .../substeps/BankAccountDetails.tsx | 114 ------- .../substeps/BankInformation.tsx | 118 ------- .../substeps/Confirmation.tsx | 186 ----------- .../substeps/CountrySelection.tsx | 91 ------ .../substeps/Success.tsx | 22 -- .../InternationalDepositAccount/types.ts | 20 -- .../InternationalDepositAccount/utils.ts | 150 --------- .../settings/Wallet/PaymentMethodList.tsx | 6 +- .../settings/Wallet/WalletPage/WalletPage.tsx | 11 +- .../form/InternationalBankAccountForm.ts | 6 - src/types/form/index.ts | 5 + src/types/onyx/BankAccount.ts | 6 - src/types/onyx/CorpayFields.ts | 43 +-- src/types/onyx/index.ts | 5 +- tests/unit/useSubStepTest.tsx | 297 +++--------------- 48 files changed, 204 insertions(+), 1737 deletions(-) delete mode 100644 src/components/CurrencyPicker.tsx delete mode 100644 src/hooks/useInternationalBankAccountFormSubmit.ts create mode 100644 src/pages/ReimbursementAccount/RequestorOnfidoStep.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/index.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountHolderInformation.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountType.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/substeps/Confirmation.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/substeps/CountrySelection.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/substeps/Success.tsx delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/types.ts delete mode 100644 src/pages/settings/Wallet/InternationalDepositAccount/utils.ts delete mode 100644 src/types/form/InternationalBankAccountForm.ts diff --git a/src/CONST.ts b/src/CONST.ts index 9aa0841092ad..f295a375e1a6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -939,7 +939,6 @@ const CONST = { CONFIGURE_REIMBURSEMENT_SETTINGS_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/workspaces/Configure-Reimbursement-Settings', COPILOT_HELP_URL: 'https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Assign-or-remove-a-Copilot', 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', // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', @@ -6441,50 +6440,6 @@ const CONST = { }, }, - CORPAY_FIELDS: { - BANK_ACCOUNT_DETAILS_FIELDS: ['accountNumber', 'localAccountNumber', 'routingCode', 'localRoutingCode', 'swiftBicCode'] as string[], - ACCOUNT_TYPE_KEY: 'BeneficiaryAccountType', - BANK_INFORMATION_FIELDS: ['bankName', 'bankAddressLine1', 'bankAddressLine2', 'bankCity', 'bankRegion', 'bankPostal', 'BeneficiaryBankBranchName'] as string[], - ACCOUNT_HOLDER_FIELDS: [ - 'accountHolderName', - 'accountHolderAddress1', - 'accountHolderAddress2', - 'accountHolderCity', - 'accountHolderRegion', - 'accountHolderCountry', - 'accountHolderPostal', - 'accountHolderPhoneNumber', - 'accountHolderEmail', - 'ContactName', - 'BeneficiaryCPF', - 'BeneficiaryRUT', - 'BeneficiaryCedulaID', - 'BeneficiaryTaxID', - ] as string[], - SPECIAL_LIST_REGION_KEYS: ['bankRegion', 'accountHolderRegion'] as string[], - SPECIAL_LIST_ADDRESS_KEYS: ['bankAddressLine1', 'accountHolderAddress1'] as string[], - STEPS_NAME: { - COUNTRY_SELECTOR: 'CountrySelector', - BANK_ACCOUNT_DETAILS: 'BankAccountDetails', - ACCOUNT_TYPE: 'AccountType', - BANK_INFORMATION: 'BankInformation', - ACCOUNT_HOLDER_INFORMATION: 'AccountHolderInformation', - CONFIRMATION: 'Confirmation', - SUCCESS: 'Success', - }, - INDEXES: { - MAPPING: { - COUNTRY_SELECTOR: 0, - BANK_ACCOUNT_DETAILS: 1, - ACCOUNT_TYPE: 2, - BANK_INFORMATION: 3, - ACCOUNT_HOLDER_INFORMATION: 4, - CONFIRMATION: 5, - SUCCESS: 6, - }, - }, - }, - HYBRID_APP: { REORDERING_REACT_NATIVE_ACTIVITY_TO_FRONT: 'reorderingReactNativeActivityToFront', }, diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index cfb0afd8b33f..b45a32821065 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -600,8 +600,6 @@ const ONYXKEYS = { HOME_ADDRESS_FORM_DRAFT: 'homeAddressFormDraft', PERSONAL_DETAILS_FORM: 'personalDetailsForm', PERSONAL_DETAILS_FORM_DRAFT: 'personalDetailsFormDraft', - INTERNATIONAL_BANK_ACCOUNT_FORM: 'internationalBankAccountForm', - INTERNATIONAL_BANK_ACCOUNT_FORM_DRAFT: 'internationalBankAccountFormDraft', NEW_ROOM_FORM: 'newRoomForm', NEW_ROOM_FORM_DRAFT: 'newRoomFormDraft', ROOM_SETTINGS_FORM: 'roomSettingsForm', @@ -821,7 +819,6 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AGE_FORM]: FormTypes.RulesMaxExpenseAgeForm; [ONYXKEYS.FORMS.SEARCH_SAVED_SEARCH_RENAME_FORM]: FormTypes.SearchSavedSearchRenameForm; [ONYXKEYS.FORMS.DEBUG_DETAILS_FORM]: FormTypes.DebugReportForm | FormTypes.DebugReportActionForm | FormTypes.DebugTransactionForm | FormTypes.DebugTransactionViolationForm; - [ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM]: FormTypes.InternationalBankAccountForm; [ONYXKEYS.FORMS.WORKSPACE_PER_DIEM_FORM]: FormTypes.WorkspacePerDiemForm; }; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 57b4f65a5bc6..909f847fd75d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -200,7 +200,6 @@ const ROUTES = { }, SETTINGS_ADD_DEBIT_CARD: 'settings/wallet/add-debit-card', SETTINGS_ADD_BANK_ACCOUNT: 'settings/wallet/add-bank-account', - SETTINGS_ADD_US_BANK_ACCOUNT: 'settings/wallet/add-us-bank-account', SETTINGS_ENABLE_PAYMENTS: 'settings/wallet/enable-payments', SETTINGS_WALLET_CARD_DIGITAL_DETAILS_UPDATE_ADDRESS: { route: 'settings/wallet/card/:domain/digital-details/update-address', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index d0dc80d3e9d9..6274be1044b4 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -69,7 +69,6 @@ const SCREENS = { ADD_DEBIT_CARD: 'Settings_Add_Debit_Card', ADD_PAYMENT_CARD_CHANGE_CURRENCY: 'Settings_Add_Payment_Card_Change_Currency', ADD_BANK_ACCOUNT: 'Settings_Add_Bank_Account', - ADD_US_BANK_ACCOUNT: 'Settings_Add_US_Bank_Account', CLOSE: 'Settings_Close', TWO_FACTOR_AUTH: 'Settings_TwoFactorAuth', REPORT_CARD_LOST_OR_DAMAGED: 'Settings_ReportCardLostOrDamaged', diff --git a/src/components/CurrencyPicker.tsx b/src/components/CurrencyPicker.tsx deleted file mode 100644 index 6d2f4826fbc5..000000000000 --- a/src/components/CurrencyPicker.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import type {ReactNode} from 'react'; -import React, {useState} from 'react'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; -import Navigation from '@libs/Navigation/Navigation'; -import CONST from '@src/CONST'; -import CurrencySelectionList from './CurrencySelectionList'; -import type {CurrencyListItem} from './CurrencySelectionList/types'; -import HeaderWithBackButton from './HeaderWithBackButton'; -import MenuItemWithTopDescription from './MenuItemWithTopDescription'; -import Modal from './Modal'; -import ScreenWrapper from './ScreenWrapper'; - -type CurrencyPickerProps = { - /** Current value of the selected item */ - value?: string; - - /** Custom content to display in the header */ - headerContent?: ReactNode; - - /** Callback when the list item is selected */ - onInputChange?: (value: string, key?: string) => void; - - /** Form Error description */ - errorText?: string; -}; - -function CurrencyPicker({value, errorText, headerContent, onInputChange = () => {}}: CurrencyPickerProps) { - const {translate} = useLocalize(); - const [isPickerVisible, setIsPickerVisible] = useState(false); - const styles = useThemeStyles(); - - const hidePickerModal = () => { - setIsPickerVisible(false); - }; - - const updateInput = (item: CurrencyListItem) => { - onInputChange?.(item.currencyCode); - hidePickerModal(); - }; - - return ( - <> - setIsPickerVisible(true)} - brickRoadIndicator={errorText ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} - errorText={errorText} - /> - - - - {!!headerContent && headerContent} - - - - - ); -} - -CurrencyPicker.displayName = 'CurrencyPicker'; -export default CurrencyPicker; diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index d9dfbca277fc..2731d6bd1f98 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -69,9 +69,6 @@ type FormProviderProps = FormProps, @@ -193,7 +189,7 @@ function FormProvider( const submit = useDebounceNonReactive( useCallback(() => { // Return early if the form is already submitting to avoid duplicate submission - if (!!formState?.isLoading || isLoading) { + if (formState?.isLoading) { return; } @@ -214,7 +210,7 @@ function FormProvider( } KeyboardUtils.dismiss().then(() => onSubmit(trimmedStringValues)); - }, [enabledWhenOffline, formState?.isLoading, inputValues, isLoading, network?.isOffline, onSubmit, onValidate, shouldTrimValues]), + }, [enabledWhenOffline, formState?.isLoading, inputValues, network?.isOffline, onSubmit, onValidate, shouldTrimValues]), 1000, {leading: true, trailing: false}, ); @@ -410,7 +406,6 @@ function FormProvider( onSubmit={submit} inputRefs={inputRefs} errors={errors} - isLoading={isLoading} enabledWhenOffline={enabledWhenOffline} > {typeof children === 'function' ? children({inputValues}) : children} diff --git a/src/components/Form/FormWrapper.tsx b/src/components/Form/FormWrapper.tsx index 7e3662e0d8d5..64bb2173f5b0 100644 --- a/src/components/Form/FormWrapper.tsx +++ b/src/components/Form/FormWrapper.tsx @@ -36,9 +36,6 @@ type FormWrapperProps = ChildrenProps & /** Callback to submit the form */ onSubmit: () => void; - - /** Whether the form is loading */ - isLoading?: boolean; }; function FormWrapper({ @@ -60,7 +57,6 @@ function FormWrapper({ shouldHideFixErrorsAlert = false, disablePressOnEnter = false, isSubmitDisabled = false, - isLoading = false, }: FormWrapperProps) { const styles = useThemeStyles(); const {paddingBottom: safeAreaInsetPaddingBottom} = useStyledSafeAreaInsets(); @@ -116,7 +112,7 @@ function FormWrapper({ buttonText={submitButtonText} isDisabled={isSubmitDisabled} isAlertVisible={((!isEmptyObject(errors) || !isEmptyObject(formState?.errorFields)) && !shouldHideFixErrorsAlert) || !!errorMessage} - isLoading={!!formState?.isLoading || isLoading} + isLoading={!!formState?.isLoading} message={isEmptyObject(formState?.errorFields) ? errorMessage : undefined} onSubmit={onSubmit} footerContent={footerContent} @@ -147,7 +143,6 @@ function FormWrapper({ formState?.isLoading, shouldHideFixErrorsAlert, errorMessage, - isLoading, onSubmit, footerContent, onFixTheErrorsLinkPressed, diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 78d4afe0e805..dabcaf90e4b2 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -67,7 +67,6 @@ function BaseSelectionList( showScrollIndicator = true, showLoadingPlaceholder = false, showConfirmButton = false, - isConfirmButtonDisabled = false, shouldUseDefaultTheme = false, shouldPreventDefaultFocusOnSelectRow = false, containerStyle, @@ -766,7 +765,7 @@ function BaseSelectionList( { captureOnInputs: true, shouldBubble: !flattenedSections.allOptions.at(focusedIndex) || focusedIndex === -1, - isActive: !disableKeyboardShortcuts && isFocused && !isConfirmButtonDisabled, + isActive: !disableKeyboardShortcuts && isFocused, }, ); @@ -849,7 +848,6 @@ function BaseSelectionList( onPress={onConfirm} pressOnEnter enterKeyEventListenerPriority={1} - isDisabled={isConfirmButtonDisabled} /> )} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 5c16543e25ef..3774821ce35f 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -485,9 +485,6 @@ type BaseSelectionListProps = Partial & { /** Whether to show the default confirm button */ showConfirmButton?: boolean; - /** Whether to show the default confirm button disabled */ - isConfirmButtonDisabled?: boolean; - /** Whether to use the default theme for the confirm button */ shouldUseDefaultTheme?: boolean; diff --git a/src/components/TextPicker/index.tsx b/src/components/TextPicker/index.tsx index 38125f5129ed..968338391aaa 100644 --- a/src/components/TextPicker/index.tsx +++ b/src/components/TextPicker/index.tsx @@ -7,17 +7,11 @@ import CONST from '@src/CONST'; import TextSelectorModal from './TextSelectorModal'; import type {TextPickerProps} from './types'; -function TextPicker( - {value, description, placeholder = '', errorText = '', onInputChange, furtherDetails, rightLabel, disabled = false, interactive = true, ...rest}: TextPickerProps, - forwardedRef: ForwardedRef, -) { +function TextPicker({value, description, placeholder = '', errorText = '', onInputChange, furtherDetails, rightLabel, ...rest}: TextPickerProps, forwardedRef: ForwardedRef) { const styles = useThemeStyles(); const [isPickerVisible, setIsPickerVisible] = useState(false); const showPickerModal = () => { - if (disabled) { - return; - } setIsPickerVisible(true); }; @@ -36,7 +30,7 @@ function TextPicker( diff --git a/src/components/TextPicker/types.ts b/src/components/TextPicker/types.ts index dded73952f1f..e260a478e4c2 100644 --- a/src/components/TextPicker/types.ts +++ b/src/components/TextPicker/types.ts @@ -42,7 +42,7 @@ type TextPickerProps = { /** Whether to show the tooltip text */ shouldShowTooltips?: boolean; -} & Pick & +} & Pick & TextProps; export type {TextSelectorModalProps, TextPickerProps}; diff --git a/src/hooks/useInternationalBankAccountFormSubmit.ts b/src/hooks/useInternationalBankAccountFormSubmit.ts deleted file mode 100644 index 6042bd165070..000000000000 --- a/src/hooks/useInternationalBankAccountFormSubmit.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type {FormOnyxKeys} from '@components/Form/types'; -import type {OnyxFormKey} from '@src/ONYXKEYS'; -import ONYXKEYS from '@src/ONYXKEYS'; -import useStepFormSubmit from './useStepFormSubmit'; -import type {SubStepProps} from './useSubStep/types'; - -type UseInternationalBankAccountFormSubmitParams = Pick & { - formId?: OnyxFormKey; - fieldIds: Array>; - shouldSaveDraft: boolean; -}; - -/** - * Hook for handling submit method in Missing Personal Details substeps. - * When user is in editing mode, we should save values only when user confirms the change - * @param onNext - callback - * @param fieldIds - field IDs for particular step - * @param shouldSaveDraft - if we should save draft values - */ -export default function useInternationalBankAccountFormSubmit({onNext, fieldIds, shouldSaveDraft}: UseInternationalBankAccountFormSubmitParams) { - return useStepFormSubmit({ - formId: ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM, - onNext, - fieldIds, - shouldSaveDraft, - }); -} diff --git a/src/hooks/useSubStep/index.ts b/src/hooks/useSubStep/index.ts index cc1c79d593d9..e59e18cf85b5 100644 --- a/src/hooks/useSubStep/index.ts +++ b/src/hooks/useSubStep/index.ts @@ -1,72 +1,48 @@ import type {ComponentType} from 'react'; -import {useCallback, useMemo, useRef, useState} from 'react'; +import {useCallback, useRef, useState} from 'react'; import type {SubStepProps, UseSubStep} from './types'; -function calculateLastIndex(bodyContentLength: number, skipSteps: number[] = []) { - let lastIndex = bodyContentLength - 1; - while (skipSteps.includes(lastIndex)) { - lastIndex -= 1; - } - - return lastIndex; -} - /** * This hook ensures uniform handling of components across different screens, enabling seamless integration and navigation through sub steps of the VBBA flow. * @param bodyContent - array of components to display in particular step * @param onFinished - callback triggered after finish last step * @param startFrom - initial index for bodyContent array * @param onNextSubStep - callback triggered after finish each step - * @param skipSteps - array of indexes to skip */ -export default function useSubStep({bodyContent, onFinished, startFrom = 0, skipSteps = [], onNextSubStep = () => {}}: UseSubStep) { +export default function useSubStep({bodyContent, onFinished, startFrom = 0, onNextSubStep = () => {}}: UseSubStep) { const [screenIndex, setScreenIndex] = useState(startFrom); const isEditing = useRef(false); - if (bodyContent.length === skipSteps.length) { - throw new Error('All steps are skipped'); - } - - const lastScreenIndex = useMemo(() => calculateLastIndex(bodyContent.length, skipSteps), [bodyContent.length, skipSteps]); - const prevScreen = useCallback(() => { - let decrementNumber = 1; - while (screenIndex - decrementNumber >= 0 && skipSteps.includes(screenIndex - decrementNumber)) { - decrementNumber += 1; - } - const prevScreenIndex = screenIndex - decrementNumber; + const prevScreenIndex = screenIndex - 1; if (prevScreenIndex < 0) { return; } setScreenIndex(prevScreenIndex); - }, [screenIndex, skipSteps]); + }, [screenIndex]); const nextScreen = useCallback( (finishData?: unknown) => { if (isEditing.current) { isEditing.current = false; - setScreenIndex(lastScreenIndex); + setScreenIndex(bodyContent.length - 1); return; } - let incrementNumber = 1; - while (screenIndex + incrementNumber < lastScreenIndex && skipSteps.includes(screenIndex + incrementNumber)) { - incrementNumber += 1; - } - const nextScreenIndex = screenIndex + incrementNumber; + const nextScreenIndex = screenIndex + 1; - if (nextScreenIndex === lastScreenIndex + 1) { + if (nextScreenIndex === bodyContent.length) { onFinished(finishData); } else { onNextSubStep(); setScreenIndex(nextScreenIndex); } }, - [screenIndex, lastScreenIndex, skipSteps, onFinished, onNextSubStep], + [screenIndex, bodyContent.length, onFinished, onNextSubStep], ); const moveTo = useCallback((step: number) => { @@ -74,15 +50,14 @@ export default function useSubStep({bodyContent, on setScreenIndex(step); }, []); - const resetScreenIndex = useCallback((newScreenIndex = 0) => { - isEditing.current = false; - setScreenIndex(newScreenIndex); + const resetScreenIndex = useCallback(() => { + setScreenIndex(0); }, []); const goToTheLastStep = useCallback(() => { isEditing.current = false; - setScreenIndex(lastScreenIndex); - }, [lastScreenIndex]); + setScreenIndex(bodyContent.length - 1); + }, [bodyContent]); // eslint-disable-next-line react-compiler/react-compiler return { diff --git a/src/hooks/useSubStep/types.ts b/src/hooks/useSubStep/types.ts index a4d28265b7f3..603534e68c15 100644 --- a/src/hooks/useSubStep/types.ts +++ b/src/hooks/useSubStep/types.ts @@ -15,9 +15,6 @@ type SubStepProps = { /** moves user to previous sub step */ prevScreen?: () => void; - - /** resets screen index to passed value */ - resetScreenIndex?: (index?: number) => void; }; type UseSubStep = { @@ -32,9 +29,6 @@ type UseSubStep = { /** index of initial sub step to display */ startFrom?: number; - - /** array of indexes to skip */ - skipSteps?: number[]; }; export type {SubStepProps, UseSubStep}; diff --git a/src/languages/en.ts b/src/languages/en.ts index a7723c8b9d39..f689b1a2ff18 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1987,15 +1987,6 @@ const translations = { ownershipPercentage: 'Please enter a valid percentage number.', }, }, - addPersonalBankAccount: { - countrySelectionStepHeader: "Where's your bank account located?", - accountDetailsStepHeader: 'What are your account details?', - accountTypeStepHeader: 'What type of account is this?', - bankInformationStepHeader: 'What are your bank details?', - accountHolderInformationStepHeader: 'What are the account holder details?', - howDoWeProtectYourData: 'How do we protect your data?', - currencyHeader: "What's your bank account's currency?", - }, addPersonalBankAccountPage: { enterPassword: 'Enter Expensify password', alreadyAdded: 'This account has already been added.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 01c50ff8b24c..fcaa8e861d59 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2009,15 +2009,6 @@ const translations = { ownershipPercentage: 'Por favor, ingrese un número de porcentaje válido.', }, }, - addPersonalBankAccount: { - countrySelectionStepHeader: '¿Dónde está ubicada tu cuenta bancaria?', - accountDetailsStepHeader: '¿Cuáles son los detalles de tu cuenta?', - accountTypeStepHeader: '¿Qué tipo de cuenta es esta?', - bankInformationStepHeader: '¿Cuáles son los detalles de tu banco?', - accountHolderInformationStepHeader: '¿Cuáles son los detalles del titular de la cuenta?', - howDoWeProtectYourData: '¿Cómo protegemos tus datos?', - currencyHeader: '¿Cuál es la moneda de tu cuenta bancaria?', - }, addPersonalBankAccountPage: { enterPassword: 'Escribe tu contraseña de Expensify', alreadyAdded: 'Esta cuenta ya ha sido añadida.', diff --git a/src/libs/API/parameters/BankAccountCreateCorpayParams.ts b/src/libs/API/parameters/BankAccountCreateCorpayParams.ts index c753d4c4ffb2..3c617d326009 100644 --- a/src/libs/API/parameters/BankAccountCreateCorpayParams.ts +++ b/src/libs/API/parameters/BankAccountCreateCorpayParams.ts @@ -1,5 +1,5 @@ type BankAccountCreateCorpayParams = { - type?: number; + type: number; isSavings: boolean; isWithdrawal: boolean; inputs: string; diff --git a/src/libs/API/parameters/GetCorpayBankAccountFieldsParams.ts b/src/libs/API/parameters/GetCorpayBankAccountFieldsParams.ts index a1228a023abe..3e02b57f9e12 100644 --- a/src/libs/API/parameters/GetCorpayBankAccountFieldsParams.ts +++ b/src/libs/API/parameters/GetCorpayBankAccountFieldsParams.ts @@ -1,8 +1,8 @@ type GetCorpayBankAccountFieldsParams = { countryISO: string; - currency?: string; - isWithdrawal?: boolean; - isBusinessBankAccount?: boolean; + currency: string; + isWithdrawal: boolean; + isBusinessBankAccount: boolean; }; export default GetCorpayBankAccountFieldsParams; diff --git a/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts b/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts index 6ef6b3712439..5b7a221a8702 100644 --- a/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts +++ b/src/libs/API/parameters/VerifyIdentityForBankAccountParams.ts @@ -1,6 +1,6 @@ type VerifyIdentityForBankAccountParams = { bankAccountID: number; onfidoData: string; - policyID: string; + policyID?: string; }; export default VerifyIdentityForBankAccountParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 7b1f8a203ffc..ea2d9893cf24 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -8,6 +8,7 @@ export type {default as RestartBankAccountSetupParams} from './RestartBankAccoun export type {default as AddSchoolPrincipalParams} from './AddSchoolPrincipalParams'; export type {default as AuthenticatePusherParams} from './AuthenticatePusherParams'; export type {default as BankAccountHandlePlaidErrorParams} from './BankAccountHandlePlaidErrorParams'; +export type {default as BankAccountCreateCorpayParams} from './BankAccountCreateCorpayParams'; export type {default as BeginAppleSignInParams} from './BeginAppleSignInParams'; export type {default as BeginGoogleSignInParams} from './BeginGoogleSignInParams'; export type {default as BeginSignInParams} from './BeginSignInParams'; @@ -29,6 +30,7 @@ export type {default as ExpandURLPreviewParams} from './ExpandURLPreviewParams'; export type {default as GetMissingOnyxMessagesParams} from './GetMissingOnyxMessagesParams'; export type {default as GetNewerActionsParams} from './GetNewerActionsParams'; export type {default as GetOlderActionsParams} from './GetOlderActionsParams'; +export type {default as GetCorpayBankAccountFieldsParams} from './GetCorpayBankAccountFieldsParams'; export type {default as GetPolicyCategoriesParams} from './GetPolicyCategories'; export type {default as GetReportPrivateNoteParams} from './GetReportPrivateNoteParams'; export type {default as GetRouteParams} from './GetRouteParams'; @@ -353,8 +355,6 @@ export type {default as UpdateQuickbooksDesktopCompanyCardExpenseAccountTypePara export type {default as TogglePolicyPerDiemParams} from './TogglePolicyPerDiemParams'; export type {default as OpenPolicyPerDiemRatesPageParams} from './OpenPolicyPerDiemRatesPageParams'; export type {default as TogglePlatformMuteParams} from './TogglePlatformMuteParams'; -export type {default as GetCorpayBankAccountFieldsParams} from './GetCorpayBankAccountFieldsParams'; -export type {default as BankAccountCreateCorpayParams} from './BankAccountCreateCorpayParams'; export type {default as JoinAccessiblePolicyParams} from './JoinAccessiblePolicyParams'; export type {default as ImportPerDiemRatesParams} from './ImportPerDiemRatesParams'; export type {default as ExportPerDiemCSVParams} from './ExportPerDiemCSVParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 2980e0d822ab..7b8c6df92a22 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -440,7 +440,6 @@ const WRITE_COMMANDS = { SELF_TOUR_VIEWED: 'SelfTourViewed', UPDATE_INVOICE_COMPANY_NAME: 'UpdateInvoiceCompanyName', UPDATE_INVOICE_COMPANY_WEBSITE: 'UpdateInvoiceCompanyWebsite', - GET_CORPAY_BANK_ACCOUNT_FIELDS: 'GetCorpayBankAccountFields', BANK_ACCOUNT_CREATE_CORPAY: 'BankAccount_CreateCorpay', UPDATE_WORKSPACE_CUSTOM_UNIT: 'UpdateWorkspaceCustomUnit', VALIDATE_USER_AND_GET_ACCESSIBLE_POLICIES: 'ValidateUserAndGetAccessiblePolicies', @@ -770,7 +769,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_SUBSCRIPTION_ADD_NEW_USERS_AUTOMATICALLY]: Parameters.UpdateSubscriptionAddNewUsersAutomaticallyParams; [WRITE_COMMANDS.UPDATE_SUBSCRIPTION_SIZE]: Parameters.UpdateSubscriptionSizeParams; [WRITE_COMMANDS.REQUEST_TAX_EXEMPTION]: null; - [WRITE_COMMANDS.GET_CORPAY_BANK_ACCOUNT_FIELDS]: Parameters.GetCorpayBankAccountFieldsParams; [WRITE_COMMANDS.UPDATE_WORKSPACE_CUSTOM_UNIT]: Parameters.UpdateWorkspaceCustomUnitParams; [WRITE_COMMANDS.DELETE_MONEY_REQUEST_ON_SEARCH]: Parameters.DeleteMoneyRequestOnSearchParams; @@ -1049,7 +1047,6 @@ const SIDE_EFFECT_REQUEST_COMMANDS = { DISCONNECT_AS_DELEGATE: 'DisconnectAsDelegate', COMPLETE_HYBRID_APP_ONBOARDING: 'CompleteHybridAppOnboarding', CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP: 'ConnectPolicyToQuickbooksDesktop', - BANK_ACCOUNT_CREATE_CORPAY: 'BankAccount_CreateCorpay', // PayMoneyRequestOnSearch only works online (pattern C) and we need to play the success sound only when the request is successful PAY_MONEY_REQUEST_ON_SEARCH: 'PayMoneyRequestOnSearch', @@ -1073,7 +1070,6 @@ type SideEffectRequestCommandParameters = { [SIDE_EFFECT_REQUEST_COMMANDS.DISCONNECT_AS_DELEGATE]: EmptyObject; [SIDE_EFFECT_REQUEST_COMMANDS.COMPLETE_HYBRID_APP_ONBOARDING]: EmptyObject; [SIDE_EFFECT_REQUEST_COMMANDS.CONNECT_POLICY_TO_QUICKBOOKS_DESKTOP]: Parameters.ConnectPolicyToQuickBooksDesktopParams; - [SIDE_EFFECT_REQUEST_COMMANDS.BANK_ACCOUNT_CREATE_CORPAY]: Parameters.BankAccountCreateCorpayParams; [SIDE_EFFECT_REQUEST_COMMANDS.PAY_MONEY_REQUEST_ON_SEARCH]: Parameters.PayMoneyRequestOnSearchParams; }; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 058a0844a5ba..7e5ac879cf60 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -247,8 +247,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/EnablePayments/EnablePayments').default, [SCREENS.SETTINGS.WALLET.VERIFY_ACCOUNT]: () => require('../../../../pages/settings/Wallet/VerifyAccountPage').default, [SCREENS.SETTINGS.ADD_DEBIT_CARD]: () => require('../../../../pages/settings/Wallet/AddDebitCardPage').default, - [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: () => require('../../../../pages/settings/Wallet/InternationalDepositAccount').default, - [SCREENS.SETTINGS.ADD_US_BANK_ACCOUNT]: () => require('../../../../pages/AddPersonalBankAccountPage').default, + [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: () => require('../../../../pages/AddPersonalBankAccountPage').default, [SCREENS.SETTINGS.PROFILE.STATUS]: () => require('../../../../pages/settings/Profile/CustomStatus/StatusPage').default, [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER]: () => require('../../../../pages/settings/Profile/CustomStatus/StatusClearAfterPage').default, [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE]: () => require('../../../../pages/settings/Profile/CustomStatus/SetDatePage').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 9d4d917311e4..04ed0261a225 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -242,10 +242,6 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT, exact: true, }, - [SCREENS.SETTINGS.ADD_US_BANK_ACCOUNT]: { - path: ROUTES.SETTINGS_ADD_US_BANK_ACCOUNT, - exact: true, - }, [SCREENS.SETTINGS.PROFILE.PRONOUNS]: { path: ROUTES.SETTINGS_PRONOUNS, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 7c6c568a5359..9869f4e39f94 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -175,7 +175,6 @@ type SettingsNavigatorParamList = { }; [SCREENS.SETTINGS.ADD_DEBIT_CARD]: undefined; [SCREENS.SETTINGS.ADD_BANK_ACCOUNT]: undefined; - [SCREENS.SETTINGS.ADD_US_BANK_ACCOUNT]: undefined; [SCREENS.SETTINGS.PROFILE.STATUS]: undefined; [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER]: undefined; [SCREENS.SETTINGS.PROFILE.STATUS_CLEAR_AFTER_DATE]: undefined; diff --git a/src/libs/PaymentUtils.ts b/src/libs/PaymentUtils.ts index 95fc334b906c..c18ebd217406 100644 --- a/src/libs/PaymentUtils.ts +++ b/src/libs/PaymentUtils.ts @@ -27,10 +27,10 @@ function hasExpensifyPaymentMethod(fundList: Record, bankAccountLi return validBankAccount || (shouldIncludeDebitCard && validDebitCard); } -function getPaymentMethodDescription(accountType: AccountType, account: BankAccount['accountData'] | Fund['accountData'] | ACHAccount, bankCurrency?: string): string { +function getPaymentMethodDescription(accountType: AccountType, account: BankAccount['accountData'] | Fund['accountData'] | ACHAccount): string { if (account) { if (accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && 'accountNumber' in account) { - return `${bankCurrency} ${CONST.DOT_SEPARATOR} ${Localize.translateLocal('paymentMethodList.accountLastFour')} ${account.accountNumber?.slice(-4)}`; + return `${Localize.translateLocal('paymentMethodList.accountLastFour')} ${account.accountNumber?.slice(-4)}`; } if (accountType === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT && 'accountNumber' in account) { return `${Localize.translateLocal('paymentMethodList.accountLastFour')} ${account.accountNumber?.slice(-4)}`; @@ -61,7 +61,7 @@ function formatPaymentMethods(bankAccountList: Record, fund }); combinedPaymentMethods.push({ ...bankAccount, - description: getPaymentMethodDescription(bankAccount?.accountType, bankAccount.accountData, bankAccount.bankCurrency), + description: getPaymentMethodDescription(bankAccount?.accountType, bankAccount.accountData), icon, iconSize, iconHeight, diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts index 40e2b188d33f..09a1f0b4f8fd 100644 --- a/src/libs/actions/BankAccounts.ts +++ b/src/libs/actions/BankAccounts.ts @@ -11,7 +11,7 @@ import type { ValidateBankAccountWithTransactionsParams, VerifyIdentityForBankAccountParams, } from '@libs/API/parameters'; -import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as ErrorUtils from '@libs/ErrorUtils'; import * as Localize from '@libs/Localize'; import Navigation from '@libs/Navigation/Navigation'; @@ -19,7 +19,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; -import type {InternationalBankAccountForm, PersonalBankAccountForm} from '@src/types/form'; +import type {PersonalBankAccountForm} from '@src/types/form'; import type {ACHContractStepProps, BeneficialOwnersStepProps, CompanyStepProps, ReimbursementAccountForm, RequestorStepProps} from '@src/types/form/ReimbursementAccountForm'; import type PlaidBankAccount from '@src/types/onyx/PlaidBankAccount'; import type {BankAccountStep, ReimbursementAccountStep, ReimbursementAccountSubStep} from '@src/types/onyx/ReimbursementAccount'; @@ -62,12 +62,6 @@ function clearPlaid(): Promise { return Onyx.set(ONYXKEYS.PLAID_DATA, CONST.PLAID.DEFAULT_DATA); } -function clearInternationalBankAccount() { - return clearPlaid() - .then(() => Onyx.set(ONYXKEYS.CORPAY_FIELDS, null)) - .then(() => Onyx.set(ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM_DRAFT, null)); -} - function openPlaidView() { clearPlaid().then(() => ReimbursementAccount.setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID)); } @@ -80,7 +74,7 @@ function setPlaidEvent(eventName: string | null) { * Open the personal bank account setup flow, with an optional exitReportID to redirect to once the flow is finished. */ function openPersonalBankAccountSetupView(exitReportID?: string, isUserValidated = true) { - clearInternationalBankAccount().then(() => { + clearPlaid().then(() => { if (exitReportID) { Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {exitReportID}); } @@ -569,7 +563,7 @@ function connectBankAccountManually(bankAccountID: number, bankAccount: PlaidBan /** * Verify the user's identity via Onfido */ -function verifyIdentityForBankAccount(bankAccountID: number, onfidoData: OnfidoDataWithApplicantID, policyID: string) { +function verifyIdentityForBankAccount(bankAccountID: number, onfidoData: OnfidoDataWithApplicantID, policyID?: string) { const parameters: VerifyIdentityForBankAccountParams = { bankAccountID, onfidoData: JSON.stringify(onfidoData), @@ -645,56 +639,6 @@ function validatePlaidSelection(values: FormOnyxValues): Form return errorFields; } -function fetchCorpayFields(bankCountry: string, bankCurrency?: string, isWithdrawal?: boolean, isBusinessBankAccount?: boolean) { - API.write( - WRITE_COMMANDS.GET_CORPAY_BANK_ACCOUNT_FIELDS, - {countryISO: bankCountry, currency: bankCurrency, isWithdrawal, isBusinessBankAccount}, - { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_BANK_ACCOUNT, - value: { - isLoading: true, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM_DRAFT, - value: { - bankCountry, - bankCurrency: bankCurrency ?? null, - }, - }, - ], - finallyData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_BANK_ACCOUNT, - value: { - isLoading: false, - }, - }, - ], - }, - ); -} - -function createCorpayBankAccountForWalletFlow(data: InternationalBankAccountForm, classification: string, destinationCountry: string, preferredMethod: string) { - const inputData = { - ...data, - classification, - destinationCountry, - preferredMethod, - setupType: 'manual', - fieldsType: 'international', - country: data.bankCountry, - currency: data.bankCurrency, - }; - // eslint-disable-next-line rulesdir/no-api-side-effects-method - return API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.BANK_ACCOUNT_CREATE_CORPAY, {isWithdrawal: false, isSavings: true, inputs: JSON.stringify(inputData)}); -} - export { acceptACHContractForBankAccount, addBusinessWebsiteForDraft, @@ -724,10 +668,8 @@ export { updateAddPersonalBankAccountDraft, clearPersonalBankAccountSetupType, validatePlaidSelection, - fetchCorpayFields, - clearReimbursementAccountBankCreation, getCorpayBankAccountFields, - createCorpayBankAccountForWalletFlow, + clearReimbursementAccountBankCreation, }; export type {BusinessAddress, PersonalAddress}; diff --git a/src/pages/ReimbursementAccount/BankAccountStep.tsx b/src/pages/ReimbursementAccount/BankAccountStep.tsx index 120cc14b9afb..95d8f2c39663 100644 --- a/src/pages/ReimbursementAccount/BankAccountStep.tsx +++ b/src/pages/ReimbursementAccount/BankAccountStep.tsx @@ -213,11 +213,11 @@ function BankAccountStep({ {translate('common.privacy')} Link.openExternalLink(CONST.ENCRYPTION_AND_SECURITY_HELP_URL)} + onPress={() => Link.openExternalLink('https://help.expensify.com/articles/new-expensify/settings/Encryption-and-Data-Security/')} style={[styles.flexRow, styles.alignItemsCenter]} accessibilityLabel={translate('bankAccount.yourDataIsSecure')} > - {translate('bankAccount.yourDataIsSecure')} + {translate('bankAccount.yourDataIsSecure')} ; + + /** The application ID for our Onfido instance */ + onfidoApplicantID: OnyxEntry; +}; + +type RequestorOnfidoStepProps = RequestorOnfidoStepOnyxProps & { + /** The bank account currently in setup */ + reimbursementAccount: ReimbursementAccount; + + /** Goes to the previous step */ + onBackButtonPress: () => void; +}; + +const HEADER_STEP_COUNTER = {step: 3, total: 5}; +const ONFIDO_ERROR_DISPLAY_DURATION = 10000; + +function RequestorOnfidoStep({onBackButtonPress, reimbursementAccount, onfidoToken, onfidoApplicantID}: RequestorOnfidoStepProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const submitOnfidoData = (onfidoData: OnfidoData) => { + BankAccounts.verifyIdentityForBankAccount(reimbursementAccount.achData?.bankAccountID ?? -1, { + ...onfidoData, + applicantID: onfidoApplicantID ?? '-1', + }); + BankAccounts.updateReimbursementAccountDraft({isOnfidoSetupComplete: true}); + }; + + const handleOnfidoError = () => { + // In case of any unexpected error we log it to the server, show a growl, and return the user back to the requestor step so they can try again. + Growl.error(translate('onfidoStep.genericError'), ONFIDO_ERROR_DISPLAY_DURATION); + BankAccounts.clearOnfidoToken(); + BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); + }; + + const handleOnfidoUserExit = () => { + BankAccounts.clearOnfidoToken(); + BankAccounts.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.REQUESTOR); + }; + + return ( + + + + + + + + + ); +} + +RequestorOnfidoStep.displayName = 'RequestorOnfidoStep'; + +export default withOnyx({ + onfidoToken: { + key: ONYXKEYS.ONFIDO_TOKEN, + }, + onfidoApplicantID: { + key: ONYXKEYS.ONFIDO_APPLICANT_ID, + }, +})(RequestorOnfidoStep); diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx deleted file mode 100644 index f859bb5838cb..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/InternationalDepositAccountContent.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import React, {useCallback, useMemo} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useSubStep from '@hooks/useSubStep'; -import * as FormActions from '@libs/actions/FormActions'; -import Navigation from '@libs/Navigation/Navigation'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {InternationalBankAccountForm} from '@src/types/form'; -import type {BankAccountList, CorpayFields, PrivatePersonalDetails} from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import AccountHolderInformation from './substeps/AccountHolderInformation'; -import AccountType from './substeps/AccountType'; -import BankAccountDetails from './substeps/BankAccountDetails'; -import BankInformation from './substeps/BankInformation'; -import Confirmation from './substeps/Confirmation'; -import CountrySelection from './substeps/CountrySelection'; -import Success from './substeps/Success'; -import type {CustomSubStepProps} from './types'; -import {getFieldsMap, getInitialPersonalDetailsValues, getInitialSubstep, getSubstepValues, testValidation} from './utils'; - -type InternationalDepositAccountContentProps = { - privatePersonalDetails: OnyxEntry; - corpayFields: OnyxEntry; - bankAccountList: OnyxEntry; - draftValues: OnyxEntry; - country: OnyxEntry; - isAccountLoading: boolean; -}; - -const formSteps = [CountrySelection, BankAccountDetails, AccountType, BankInformation, AccountHolderInformation, Confirmation, Success]; - -function getSkippedSteps(skipAccountTypeStep: boolean, skipAccountHolderInformationStep: boolean) { - const skippedSteps = []; - if (skipAccountTypeStep) { - skippedSteps.push(CONST.CORPAY_FIELDS.INDEXES.MAPPING.ACCOUNT_TYPE); - } - if (skipAccountHolderInformationStep) { - skippedSteps.push(CONST.CORPAY_FIELDS.INDEXES.MAPPING.ACCOUNT_HOLDER_INFORMATION); - } - return skippedSteps; -} - -function InternationalDepositAccountContent({privatePersonalDetails, corpayFields, bankAccountList, draftValues, country, isAccountLoading}: InternationalDepositAccountContentProps) { - const {translate} = useLocalize(); - - const fieldsMap = useMemo(() => getFieldsMap(corpayFields), [corpayFields]); - - const values = useMemo( - () => getSubstepValues(privatePersonalDetails, corpayFields, bankAccountList, draftValues, country, fieldsMap), - [privatePersonalDetails, corpayFields, bankAccountList, draftValues, country, fieldsMap], - ); - - const initialAccountHolderDetailsValues = useMemo(() => getInitialPersonalDetailsValues(privatePersonalDetails), [privatePersonalDetails]); - - const startFrom = useMemo(() => getInitialSubstep(values, fieldsMap), [fieldsMap, values]); - - const skipAccountTypeStep = isEmptyObject(fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_TYPE]); - - const skipAccountHolderInformationStep = testValidation(initialAccountHolderDetailsValues, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION]); - - const skippedSteps = getSkippedSteps(skipAccountTypeStep, skipAccountHolderInformationStep); - - const handleFinishStep = useCallback(() => { - FormActions.clearDraftValues(ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM); - Navigation.goBack(); - }, []); - - const { - componentToRender: SubStep, - isEditing, - nextScreen, - prevScreen, - screenIndex, - moveTo, - resetScreenIndex, - } = useSubStep({bodyContent: formSteps, startFrom, onFinished: handleFinishStep, skipSteps: skippedSteps}); - - const handleBackButtonPress = () => { - if (isEditing) { - resetScreenIndex(CONST.CORPAY_FIELDS.INDEXES.MAPPING.CONFIRMATION); - return; - } - - // Clicking back on the first screen should dismiss the modal - if (screenIndex === CONST.CORPAY_FIELDS.INDEXES.MAPPING.COUNTRY_SELECTOR) { - FormActions.clearDraftValues(ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM); - Navigation.goBack(); - return; - } - - // Clicking back on the success screen should dismiss the modal - if (screenIndex === CONST.CORPAY_FIELDS.INDEXES.MAPPING.SUCCESS) { - FormActions.clearDraftValues(ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM); - Navigation.goBack(); - return; - } - prevScreen(); - }; - - const handleNextScreen = useCallback(() => { - if (isEditing) { - resetScreenIndex(CONST.CORPAY_FIELDS.INDEXES.MAPPING.CONFIRMATION); - return; - } - nextScreen(); - }, [resetScreenIndex, isEditing, nextScreen]); - - if (isAccountLoading) { - return ; - } - - return ( - - - - - ); -} - -InternationalDepositAccountContent.displayName = 'InternationalDepositAccountContent'; - -export default InternationalDepositAccountContent; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/index.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/index.tsx deleted file mode 100644 index 2f992147613a..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import {useOnyx} from 'react-native-onyx'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import ONYXKEYS from '@src/ONYXKEYS'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -import InternationalDepositAccountContent from './InternationalDepositAccountContent'; - -function InternationalDepositAccount() { - const [privatePersonalDetails, privatePersonalDetailsMetadata] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS); - const [corpayFields, corpayFieldsMetadata] = useOnyx(ONYXKEYS.CORPAY_FIELDS); - const [bankAccountList, bankAccountListMetadata] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); - const [draftValues, draftValuesMetadata] = useOnyx(ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM_DRAFT); - const [country, countryMetadata] = useOnyx(ONYXKEYS.COUNTRY); - const [isAccountLoading, isLoadingMetadata] = useOnyx(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {selector: (personalBankAccount) => personalBankAccount?.isLoading}); - - const isLoading = isLoadingOnyxValue(privatePersonalDetailsMetadata, corpayFieldsMetadata, bankAccountListMetadata, draftValuesMetadata, countryMetadata, isLoadingMetadata); - - if (isLoading) { - return ; - } - - return ( - - ); -} - -InternationalDepositAccount.displayName = 'InternationalDepositAccount'; - -export default InternationalDepositAccount; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountHolderInformation.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountHolderInformation.tsx deleted file mode 100644 index aacf04e2ebc4..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountHolderInformation.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, {useCallback} from 'react'; -import {View} from 'react-native'; -import AddressSearch from '@components/AddressSearch'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; -import TextInput from '@components/TextInput'; -import TextPicker from '@components/TextPicker'; -import ValuePicker from '@components/ValuePicker'; -import useInternationalBankAccountFormSubmit from '@hooks/useInternationalBankAccountFormSubmit'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type {CustomSubStepProps} from '@pages/settings/Wallet/InternationalDepositAccount/types'; -import {getValidationErrors} from '@pages/settings/Wallet/InternationalDepositAccount/utils'; -import Text from '@src/components/Text'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {CorpayFormField} from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; - -const ACCOUNT_HOLDER_COUNTRY = 'accountHolderCountry'; - -function getInputComponent(field: CorpayFormField) { - if ((field.valueSet ?? []).length > 0) { - return ValuePicker; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_REGION_KEYS.includes(field.id)) { - return ValuePicker; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_ADDRESS_KEYS.includes(field.id)) { - return AddressSearch; - } - if (field.id === ACCOUNT_HOLDER_COUNTRY) { - return TextPicker; - } - return TextInput; -} - -function getItems(field: CorpayFormField) { - if ((field.valueSet ?? []).length > 0) { - return (field.valueSet ?? []).map(({id, text}) => ({value: id, label: text})); - } - return (field.links?.[0]?.content.regions ?? []).map(({name, code}) => ({value: code, label: name})); -} - -function AccountHolderInformation({isEditing, onNext, formValues, fieldsMap}: CustomSubStepProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); - - const handleSubmit = useInternationalBankAccountFormSubmit({ - fieldIds: Object.keys(fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION]), - onNext, - shouldSaveDraft: true, - }); - - const validate = useCallback( - (values: FormOnyxValues): FormInputErrors => { - return getValidationErrors(values, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION], translate); - }, - [fieldsMap, translate], - ); - - const getStyle = useCallback( - (field: CorpayFormField, index: number) => { - if ((field.valueSet ?? []).length > 0) { - return [styles.mhn5, index === 0 ? styles.pb1 : styles.pv1]; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_REGION_KEYS.includes(field.id)) { - return [styles.mhn5, index === 0 ? styles.pb1 : styles.pv1]; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_ADDRESS_KEYS.includes(field.id)) { - return [index === 0 ? styles.pb2 : styles.pv2]; - } - if (field.id === ACCOUNT_HOLDER_COUNTRY) { - return [styles.mhn5, index === 0 ? styles.pb1 : styles.pv1]; - } - return [index === 0 ? styles.pb2 : styles.pv2]; - }, - [styles.mhn5, styles.pb1, styles.pb2, styles.pv1, styles.pv2], - ); - - return ( - - - {translate('addPersonalBankAccount.accountHolderInformationStepHeader')} - {Object.values(fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION]) - .sort((a, b) => CONST.CORPAY_FIELDS.ACCOUNT_HOLDER_FIELDS.indexOf(a.id) - CONST.CORPAY_FIELDS.ACCOUNT_HOLDER_FIELDS.indexOf(b.id)) - .map((field, index) => ( - - - - ))} - - - ); -} - -AccountHolderInformation.displayName = 'AccountHolderInformation'; - -export default AccountHolderInformation; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountType.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountType.tsx deleted file mode 100644 index 013683a38176..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/AccountType.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, {useCallback, useMemo, useState} from 'react'; -import {View} from 'react-native'; -import SelectionList from '@components/SelectionList'; -import RadioListItem from '@components/SelectionList/RadioListItem'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type {Option} from '@libs/searchOptions'; -import type {CustomSubStepProps} from '@pages/settings/Wallet/InternationalDepositAccount/types'; -import * as FormActions from '@userActions/FormActions'; -import Text from '@src/components/Text'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; - -function AccountType({isEditing, onNext, formValues, fieldsMap}: CustomSubStepProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); - const [currentAccountType, setCurrentAccountType] = useState(formValues[CONST.CORPAY_FIELDS.ACCOUNT_TYPE_KEY]); - - const fieldData = fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_TYPE]?.[CONST.CORPAY_FIELDS.ACCOUNT_TYPE_KEY] ?? {}; - - const onAccountTypeSelected = useCallback(() => { - if (isEditing && formValues[CONST.CORPAY_FIELDS.ACCOUNT_TYPE_KEY] === currentAccountType) { - onNext(); - return; - } - if (fieldData.isRequired && !currentAccountType) { - return; - } - FormActions.setDraftValues(ONYXKEYS.FORMS.INTERNATIONAL_BANK_ACCOUNT_FORM, {[CONST.CORPAY_FIELDS.ACCOUNT_TYPE_KEY]: currentAccountType}); - onNext(); - }, [currentAccountType, fieldData.isRequired, formValues, isEditing, onNext]); - - const onSelectionChange = useCallback((country: Option) => { - setCurrentAccountType(country.value); - }, []); - - const options = useMemo( - () => - (fieldData.valueSet ?? []).map((item) => { - return { - value: item.id, - keyForList: item.id, - text: item.text, - isSelected: currentAccountType === item.id, - searchValue: item.text, - }; - }), - [fieldData.valueSet, currentAccountType], - ); - - return ( - <> - - {translate('addPersonalBankAccount.accountTypeStepHeader')} - - - - ); -} - -AccountType.displayName = 'AccountType'; - -export default AccountType; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx deleted file mode 100644 index 782a4eafb091..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankAccountDetails.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, {useCallback} from 'react'; -import {View} from 'react-native'; -import CurrencyPicker from '@components/CurrencyPicker'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; -import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; -import TextInput from '@components/TextInput'; -import TextLink from '@components/TextLink'; -import ValuePicker from '@components/ValuePicker'; -import useInternationalBankAccountFormSubmit from '@hooks/useInternationalBankAccountFormSubmit'; -import useLocalize from '@hooks/useLocalize'; -import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type {CustomSubStepProps} from '@pages/settings/Wallet/InternationalDepositAccount/types'; -import {getValidationErrors} from '@pages/settings/Wallet/InternationalDepositAccount/utils'; -import * as BankAccounts from '@userActions/BankAccounts'; -import Text from '@src/components/Text'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; - -function BankAccountDetails({isEditing, onNext, resetScreenIndex, formValues, fieldsMap}: CustomSubStepProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); - const theme = useTheme(); - - const handleSubmit = useInternationalBankAccountFormSubmit({ - fieldIds: Object.keys(fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS] ?? {}), - onNext, - shouldSaveDraft: true, - }); - - const onCurrencySelected = useCallback( - (value: string) => { - if (formValues.bankCurrency === value) { - return; - } - BankAccounts.fetchCorpayFields(formValues.bankCountry, value); - resetScreenIndex?.(CONST.CORPAY_FIELDS.INDEXES.MAPPING.BANK_ACCOUNT_DETAILS); - }, - [formValues.bankCountry, formValues.bankCurrency, resetScreenIndex], - ); - - const validate = useCallback( - (values: FormOnyxValues): FormInputErrors => { - return getValidationErrors(values, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS], translate); - }, - [fieldsMap, translate], - ); - - const currencyHeaderContent = ( - - {translate('addPersonalBankAccount.currencyHeader')} - - ); - - return ( - - - {translate('addPersonalBankAccount.accountDetailsStepHeader')} - - - - {Object.values(fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS] ?? {}).map((field) => ( - 0 ? [styles.mhn5, styles.pv1] : [styles.pv2]} - key={field.id} - > - 0 ? ValuePicker : TextInput} - inputID={field.id} - defaultValue={formValues[field.id]} - label={field.label + (field.isRequired ? '' : ` (${translate('common.optional')})`)} - items={(field.valueSet ?? []).map(({id, text}) => ({value: id, label: text}))} - /> - - ))} - - - - - {translate('addPersonalBankAccount.howDoWeProtectYourData')} - - - - - - ); -} - -BankAccountDetails.displayName = 'BankAccountDetails'; - -export default BankAccountDetails; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx deleted file mode 100644 index f4998062ed32..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/BankInformation.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React, {useCallback} from 'react'; -import {View} from 'react-native'; -import AddressSearch from '@components/AddressSearch'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; -import TextInput from '@components/TextInput'; -import ValuePicker from '@components/ValuePicker'; -import useInternationalBankAccountFormSubmit from '@hooks/useInternationalBankAccountFormSubmit'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type {CustomSubStepProps} from '@pages/settings/Wallet/InternationalDepositAccount/types'; -import {getValidationErrors} from '@pages/settings/Wallet/InternationalDepositAccount/utils'; -import Text from '@src/components/Text'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {CorpayFormField} from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; - -function getInputComponent(field: CorpayFormField) { - if ((field.valueSet ?? []).length > 0) { - return ValuePicker; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_REGION_KEYS.includes(field.id)) { - return ValuePicker; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_ADDRESS_KEYS.includes(field.id)) { - return AddressSearch; - } - return TextInput; -} - -function getItems(field: CorpayFormField) { - if ((field.valueSet ?? []).length > 0) { - return (field.valueSet ?? []).map(({id, text}) => ({value: id, label: text})); - } - return (field.links?.[0]?.content.regions ?? []).map(({name, code}) => ({value: code, label: name})); -} - -function BankInformation({isEditing, onNext, formValues, fieldsMap}: CustomSubStepProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); - - const handleSubmit = useInternationalBankAccountFormSubmit({ - fieldIds: Object.keys(fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION]), - onNext, - shouldSaveDraft: true, - }); - - const validate = useCallback( - (values: FormOnyxValues): FormInputErrors => { - return getValidationErrors(values, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION], translate); - }, - [fieldsMap, translate], - ); - - const getStyle = useCallback( - (field: CorpayFormField, index: number) => { - if ((field.valueSet ?? []).length > 0) { - return [styles.mhn5, index === 0 ? styles.pb1 : styles.pv1]; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_REGION_KEYS.includes(field.id)) { - return [styles.mhn5, index === 0 ? styles.pb1 : styles.pv1]; - } - if (CONST.CORPAY_FIELDS.SPECIAL_LIST_ADDRESS_KEYS.includes(field.id)) { - return [index === 0 ? styles.pb2 : styles.pv2]; - } - return [index === 0 ? styles.pb2 : styles.pv2]; - }, - [styles.mhn5, styles.pb1, styles.pb2, styles.pv1, styles.pv2], - ); - - return ( - - - {translate('addPersonalBankAccount.bankInformationStepHeader')} - {Object.values(fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION]) - .sort((a, b) => CONST.CORPAY_FIELDS.BANK_INFORMATION_FIELDS.indexOf(a.id) - CONST.CORPAY_FIELDS.BANK_INFORMATION_FIELDS.indexOf(b.id)) - .map((field, index) => ( - - - - ))} - - - ); -} - -BankInformation.displayName = 'BankInformation'; - -export default BankInformation; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/Confirmation.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/Confirmation.tsx deleted file mode 100644 index c049a7545b74..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/Confirmation.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import React, {useCallback, useState} from 'react'; -import {useOnyx} from 'react-native-onyx'; -import CheckboxWithLabel from '@components/CheckboxWithLabel'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; -import FormHelpMessage from '@components/FormHelpMessage'; -import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import ScrollView from '@components/ScrollView'; -import Text from '@components/Text'; -import TextLink from '@components/TextLink'; -import useLocalize from '@hooks/useLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type {CustomSubStepProps} from '@pages/settings/Wallet/InternationalDepositAccount/types'; -import * as BankAccounts from '@userActions/BankAccounts'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; - -const STEP_INDEXES = CONST.CORPAY_FIELDS.INDEXES.MAPPING; - -function TermsAndConditionsLabel() { - const {translate} = useLocalize(); - return ( - - {translate('common.iAcceptThe')} - {`${translate('common.addCardTermsOfService')}`} - - ); -} - -function Confirmation({onNext, onMove, formValues, fieldsMap}: CustomSubStepProps) { - const {translate} = useLocalize(); - const styles = useThemeStyles(); - const [isSubmitting, setIsSubmitting] = useState(false); - const [error, setError] = useState(''); - const [corpayFields] = useOnyx(ONYXKEYS.CORPAY_FIELDS); - - const getDataAndGoToNextStep = (values: FormOnyxValues) => { - setError(''); - setIsSubmitting(true); - BankAccounts.createCorpayBankAccountForWalletFlow( - {...formValues, ...values}, - corpayFields?.classification ?? '', - corpayFields?.destinationCountry ?? '', - corpayFields?.preferredMethod ?? '', - ).then((response) => { - setIsSubmitting(false); - if (response?.jsonCode) { - if (response.jsonCode === CONST.JSON_CODE.SUCCESS) { - onNext(); - } else { - setError(response.message ?? ''); - } - } - }); - }; - - const summaryItems = [ - { - description: translate('common.country'), - title: formValues.bankCountry, - shouldShowRightIcon: true, - onPress: () => { - onMove(STEP_INDEXES.COUNTRY_SELECTOR); - }, - }, - { - description: translate('common.currency'), - title: formValues.bankCurrency, - shouldShowRightIcon: true, - onPress: () => { - onMove(STEP_INDEXES.BANK_ACCOUNT_DETAILS); - }, - }, - ]; - - // eslint-disable-next-line guard-for-in - for (const fieldName in fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS]) { - summaryItems.push({ - description: - fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS][fieldName].label + - (fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS][fieldName].isRequired ? '' : ` (${translate('common.optional')})`), - title: formValues[fieldName], - shouldShowRightIcon: true, - onPress: () => { - onMove(STEP_INDEXES.BANK_ACCOUNT_DETAILS); - }, - }); - } - - // eslint-disable-next-line guard-for-in - for (const fieldName in fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_TYPE]) { - summaryItems.push({ - description: - fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_TYPE][fieldName].label + - (fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_TYPE][fieldName].isRequired ? '' : ` (${translate('common.optional')})`), - title: formValues[fieldName], - shouldShowRightIcon: true, - onPress: () => { - onMove(STEP_INDEXES.ACCOUNT_TYPE); - }, - }); - } - - // eslint-disable-next-line guard-for-in - for (const fieldName in fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION]) { - summaryItems.push({ - description: - fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION][fieldName].label + - (fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION][fieldName].isRequired ? '' : ` (${translate('common.optional')})`), - title: formValues[fieldName], - shouldShowRightIcon: true, - onPress: () => { - onMove(STEP_INDEXES.BANK_INFORMATION); - }, - }); - } - - // eslint-disable-next-line guard-for-in - for (const fieldName in fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION]) { - summaryItems.push({ - description: - fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION][fieldName].label + - (fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION][fieldName].isRequired ? '' : ` (${translate('common.optional')})`), - title: formValues[fieldName], - shouldShowRightIcon: true, - onPress: () => { - onMove(STEP_INDEXES.ACCOUNT_HOLDER_INFORMATION); - }, - }); - } - - const validate = useCallback( - (values: FormOnyxValues): FormInputErrors => { - const errors: FormInputErrors = {}; - if (!values.acceptTerms) { - errors.acceptTerms = translate('common.error.acceptTerms'); - } - return errors; - }, - [translate], - ); - - return ( - - {translate('personalInfoStep.letsDoubleCheck')} - {summaryItems.map(({description, title, shouldShowRightIcon, onPress}) => ( - - ))} - - - - - - ); -} - -Confirmation.displayName = 'Confirmation'; - -export default Confirmation; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/CountrySelection.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/CountrySelection.tsx deleted file mode 100644 index dc16737a5dbc..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/CountrySelection.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, {useCallback, useMemo, useState} from 'react'; -import {View} from 'react-native'; -import SelectionList from '@components/SelectionList'; -import RadioListItem from '@components/SelectionList/RadioListItem'; -import useDebouncedState from '@hooks/useDebouncedState'; -import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; -import searchOptions from '@libs/searchOptions'; -import type {Option} from '@libs/searchOptions'; -import StringUtils from '@libs/StringUtils'; -import type {CustomSubStepProps} from '@pages/settings/Wallet/InternationalDepositAccount/types'; -import * as BankAccounts from '@userActions/BankAccounts'; -import Text from '@src/components/Text'; -import CONST from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; -import ROUTES from '@src/ROUTES'; - -function CountrySelection({isEditing, onNext, formValues, resetScreenIndex}: CustomSubStepProps) { - const {translate} = useLocalize(); - const {isOffline} = useNetwork(); - const styles = useThemeStyles(); - const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); - const [currentCountry, setCurrentCountry] = useState(formValues.bankCountry); - - const onCountrySelected = useCallback(() => { - if (currentCountry === CONST.COUNTRY.US) { - Navigation.navigate(ROUTES.SETTINGS_ADD_US_BANK_ACCOUNT); - return; - } - if (isEditing && formValues.bankCountry === currentCountry) { - onNext(); - return; - } - BankAccounts.fetchCorpayFields(currentCountry, formValues.bankCurrency); - resetScreenIndex?.(CONST.CORPAY_FIELDS.INDEXES.MAPPING.BANK_ACCOUNT_DETAILS); - }, [currentCountry, formValues.bankCountry, formValues.bankCurrency, isEditing, onNext, resetScreenIndex]); - - const onSelectionChange = useCallback((country: Option) => { - setCurrentCountry(country.value); - }, []); - - const countries = useMemo( - () => - Object.keys(CONST.ALL_COUNTRIES).map((countryISO) => { - const countryName = translate(`allCountries.${countryISO}` as TranslationPaths); - return { - value: countryISO, - keyForList: countryISO, - text: countryName, - isSelected: currentCountry === countryISO, - searchValue: StringUtils.sanitizeString(`${countryISO}${countryName}`), - }; - }), - [translate, currentCountry], - ); - - const searchResults = searchOptions(debouncedSearchValue, countries); - const headerMessage = debouncedSearchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : ''; - - return ( - <> - - {translate('addPersonalBankAccount.countrySelectionStepHeader')} - - - - ); -} - -CountrySelection.displayName = 'CountrySelection'; - -export default CountrySelection; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/Success.tsx b/src/pages/settings/Wallet/InternationalDepositAccount/substeps/Success.tsx deleted file mode 100644 index 3a0731010c82..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/substeps/Success.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import ConfirmationPage from '@components/ConfirmationPage'; -import useLocalize from '@hooks/useLocalize'; -import type {CustomSubStepProps} from '@pages/settings/Wallet/InternationalDepositAccount/types'; - -function Confirmation({onNext}: CustomSubStepProps) { - const {translate} = useLocalize(); - - return ( - - ); -} - -Confirmation.displayName = 'Confirmation'; - -export default Confirmation; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/types.ts b/src/pages/settings/Wallet/InternationalDepositAccount/types.ts deleted file mode 100644 index 2adfc7774c37..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type {ValueOf} from 'type-fest'; -import type {SubStepProps} from '@hooks/useSubStep/types'; -import type CONST from '@src/CONST'; -import type {InternationalBankAccountForm} from '@src/types/form'; -import type {CorpayFieldsMap} from '@src/types/onyx/CorpayFields'; - -type CustomSubStepProps = SubStepProps & { - /** User's form values */ - formValues: InternationalBankAccountForm; - - /** Fields map for the step rendering */ - fieldsMap: Record, CorpayFieldsMap>; -}; - -type CountryZipRegex = { - regex?: RegExp; - samples?: string; -}; - -export type {CustomSubStepProps, CountryZipRegex}; diff --git a/src/pages/settings/Wallet/InternationalDepositAccount/utils.ts b/src/pages/settings/Wallet/InternationalDepositAccount/utils.ts deleted file mode 100644 index 5f3370693403..000000000000 --- a/src/pages/settings/Wallet/InternationalDepositAccount/utils.ts +++ /dev/null @@ -1,150 +0,0 @@ -import lodashSortBy from 'lodash/sortBy'; -import type {OnyxEntry} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; -import type {FormOnyxValues} from '@components/Form/types'; -import type {LocaleContextProps} from '@components/LocaleContextProvider'; -import * as ErrorUtils from '@libs/ErrorUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; -import CONST from '@src/CONST'; -import type ONYXKEYS from '@src/ONYXKEYS'; -import type {InternationalBankAccountForm} from '@src/types/form'; -import type {BankAccount, BankAccountList, CorpayFields, PrivatePersonalDetails} from '@src/types/onyx'; -import type {CorpayFieldsMap} from '@src/types/onyx/CorpayFields'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; - -function getFieldsMap(corpayFields: OnyxEntry): Record, CorpayFieldsMap> { - return (corpayFields?.formFields ?? []).reduce((acc, field) => { - if (!field.id) { - return acc; - } - if (field.id === CONST.CORPAY_FIELDS.ACCOUNT_TYPE_KEY) { - acc[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_TYPE] = {[field.id]: field}; - } else if (CONST.CORPAY_FIELDS.ACCOUNT_HOLDER_FIELDS.includes(field.id)) { - acc[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION] = acc[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION] ?? {}; - acc[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION][field.id] = field; - } else if (CONST.CORPAY_FIELDS.BANK_INFORMATION_FIELDS.includes(field.id)) { - acc[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION] = acc[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION] ?? {}; - acc[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION][field.id] = field; - } else { - acc[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS] = acc[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS] ?? {}; - acc[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS][field.id] = field; - } - return acc; - }, {} as Record, CorpayFieldsMap>); -} - -function getLatestCreatedBankAccount(bankAccountList: OnyxEntry): BankAccount | undefined { - return lodashSortBy(Object.values(bankAccountList ?? {}), 'accountData.created').pop(); -} - -function getSubstepValues( - privatePersonalDetails: OnyxEntry, - corpayFields: OnyxEntry, - bankAccountList: OnyxEntry, - internationalBankAccountDraft: OnyxEntry, - country: OnyxEntry, - fieldsMap: Record, CorpayFieldsMap>, -): InternationalBankAccountForm { - const address = PersonalDetailsUtils.getCurrentAddress(privatePersonalDetails); - const personalDetailsFieldMap = fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION]; - const {street} = address ?? {}; - const [street1, street2] = street ? street.split('\n') : [undefined, undefined]; - const firstName = privatePersonalDetails?.legalFirstName ?? ''; - const lastName = privatePersonalDetails?.legalLastName ?? ''; - const fullName = `${firstName} ${lastName}`.trim() ? `${firstName} ${lastName}`.trim() : undefined; - const latestBankAccount = getLatestCreatedBankAccount(bankAccountList); - return { - ...internationalBankAccountDraft, - bankCountry: internationalBankAccountDraft?.bankCountry ?? corpayFields?.bankCountry ?? address?.country ?? latestBankAccount?.bankCountry ?? country ?? '', - bankCurrency: internationalBankAccountDraft?.bankCurrency ?? corpayFields?.bankCurrency, - accountHolderName: !isEmptyObject(personalDetailsFieldMap?.accountHolderName) ? internationalBankAccountDraft?.accountHolderName ?? fullName : undefined, - accountHolderAddress1: !isEmptyObject(personalDetailsFieldMap?.accountHolderAddress1) ? internationalBankAccountDraft?.accountHolderAddress1 ?? street1 : undefined, - accountHolderAddress2: !isEmptyObject(personalDetailsFieldMap?.accountHolderAddress2) ? internationalBankAccountDraft?.accountHolderAddress2 ?? street2 : undefined, - accountHolderCity: !isEmptyObject(personalDetailsFieldMap?.accountHolderCity) ? internationalBankAccountDraft?.accountHolderCity ?? address?.city : undefined, - accountHolderCountry: !isEmptyObject(personalDetailsFieldMap?.accountHolderCountry) - ? internationalBankAccountDraft?.accountHolderCountry ?? corpayFields?.bankCountry ?? address?.country ?? latestBankAccount?.bankCountry ?? country ?? '' - : undefined, - accountHolderPostal: !isEmptyObject(personalDetailsFieldMap?.accountHolderPostal) ? internationalBankAccountDraft?.accountHolderPostal ?? address?.zip : undefined, - accountHolderPhoneNumber: !isEmptyObject(personalDetailsFieldMap?.accountHolderPhoneNumber) - ? internationalBankAccountDraft?.accountHolderPhoneNumber ?? privatePersonalDetails?.phoneNumber - : undefined, - } as unknown as InternationalBankAccountForm; -} - -function getInitialPersonalDetailsValues(privatePersonalDetails: OnyxEntry): InternationalBankAccountForm { - const address = PersonalDetailsUtils.getCurrentAddress(privatePersonalDetails); - const {street} = address ?? {}; - const [street1, street2] = street ? street.split('\n') : [undefined, undefined]; - const firstName = privatePersonalDetails?.legalFirstName ?? ''; - const lastName = privatePersonalDetails?.legalLastName ?? ''; - const fullName = `${firstName} ${lastName}`.trim(); - return { - accountHolderName: fullName, - accountHolderAddress1: street1 ?? '', - accountHolderAddress2: street2 ?? '', - accountHolderCity: address?.city ?? '', - accountHolderCountry: address?.country ?? '', - accountHolderPostal: address?.zip ?? '', - accountHolderPhoneNumber: privatePersonalDetails?.phoneNumber ?? '', - } as InternationalBankAccountForm; -} - -function testValidation(values: InternationalBankAccountForm, fieldsMap: CorpayFieldsMap = {}) { - for (const fieldName in fieldsMap) { - if (!fieldName) { - // eslint-disable-next-line no-continue - continue; - } - if (fieldsMap[fieldName].isRequired && (values[fieldName] ?? '') === '') { - return false; - } - fieldsMap[fieldName].validationRules.forEach((rule) => { - const regExpCheck = new RegExp(rule.regEx); - if (!regExpCheck.test(values[fieldName] ?? '')) { - return false; - } - }); - } - return true; -} - -function getInitialSubstep(values: InternationalBankAccountForm, fieldsMap: Record, CorpayFieldsMap>) { - if (values.bankCountry === '' || isEmptyObject(fieldsMap)) { - return CONST.CORPAY_FIELDS.INDEXES.MAPPING.COUNTRY_SELECTOR; - } - if (values.bankCurrency === '' || !testValidation(values, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_ACCOUNT_DETAILS])) { - return CONST.CORPAY_FIELDS.INDEXES.MAPPING.BANK_ACCOUNT_DETAILS; - } - if (!testValidation(values, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_TYPE])) { - return CONST.CORPAY_FIELDS.INDEXES.MAPPING.ACCOUNT_TYPE; - } - if (!testValidation(values, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.BANK_INFORMATION])) { - return CONST.CORPAY_FIELDS.INDEXES.MAPPING.BANK_INFORMATION; - } - if (!testValidation(values, fieldsMap[CONST.CORPAY_FIELDS.STEPS_NAME.ACCOUNT_HOLDER_INFORMATION])) { - return CONST.CORPAY_FIELDS.INDEXES.MAPPING.ACCOUNT_HOLDER_INFORMATION; - } - return CONST.CORPAY_FIELDS.INDEXES.MAPPING.CONFIRMATION; -} - -function getValidationErrors(values: FormOnyxValues, fieldsMap: CorpayFieldsMap, translate: LocaleContextProps['translate']) { - const errors = {}; - for (const fieldName in fieldsMap) { - if (!fieldName) { - // eslint-disable-next-line no-continue - continue; - } - if (fieldsMap[fieldName].isRequired && values[fieldName] === '') { - ErrorUtils.addErrorMessage(errors, fieldName, translate('common.error.fieldRequired')); - } - fieldsMap[fieldName].validationRules.forEach((rule) => { - const regExpCheck = new RegExp(rule.regEx); - if (!regExpCheck.test(values[fieldName])) { - ErrorUtils.addErrorMessage(errors, fieldName, rule.errorMessage); - } - }); - } - return errors; -} - -export {getFieldsMap, getSubstepValues, getInitialPersonalDetailsValues, getInitialSubstep, testValidation, getValidationErrors}; diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index be0942e4716d..a16bad99d2cc 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -96,7 +96,6 @@ type PaymentMethodListProps = { icon?: FormattedSelectedPaymentMethodIcon, isDefault?: boolean, methodID?: number, - description?: string, ) => void; /** The policy invoice's transfer bank accountID */ @@ -128,7 +127,7 @@ function dismissError(item: PaymentMethodItem) { const isBankAccount = item.accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT; const paymentList = isBankAccount ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.FUND_LIST; - const paymentID = isBankAccount ? item.accountData?.bankAccountID : item.accountData?.fundID; + const paymentID = isBankAccount ? item.accountData?.bankAccountID ?? '' : item.accountData?.fundID ?? ''; if (!paymentID) { Log.info('Unable to clear payment method error: ', undefined, item); @@ -334,7 +333,6 @@ function PaymentMethodList({ }, paymentMethod.isDefault, paymentMethod.methodID, - paymentMethod.description, ), wrapperStyle: isMethodActive ? [StyleUtils.getButtonBackgroundColorStyle(CONST.BUTTON_STATES.PRESSED)] : null, disabled: paymentMethod.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, @@ -423,7 +421,7 @@ function PaymentMethodList({ shouldShowDefaultBadge( filteredPaymentMethods, item, - userWallet?.walletLinkedAccountID ?? CONST.DEFAULT_NUMBER_ID, + userWallet?.walletLinkedAccountID ?? 0, invoiceTransferBankAccountID ? invoiceTransferBankAccountID === item.methodID : item.isDefault, ) ? translate('paymentMethodList.defaultPaymentMethod') diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index be3aa2b5327a..39db6739bf1c 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -140,7 +140,6 @@ function WalletPage({shouldListenForResize = false}: WalletPageProps) { icon?: FormattedSelectedPaymentMethodIcon, isDefault?: boolean, methodID?: string | number, - description?: string, ) => { if (shouldShowAddPaymentMenu) { setShouldShowAddPaymentMenu(false); @@ -162,14 +161,14 @@ function WalletPage({shouldListenForResize = false}: WalletPageProps) { formattedSelectedPaymentMethod = { title: account?.addressName ?? '', icon, - description: description ?? PaymentUtils.getPaymentMethodDescription(accountType, account), + description: PaymentUtils.getPaymentMethodDescription(accountType, account), type: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, }; } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { formattedSelectedPaymentMethod = { title: account?.addressName ?? '', icon, - description: description ?? PaymentUtils.getPaymentMethodDescription(accountType, account), + description: PaymentUtils.getPaymentMethodDescription(accountType, account), type: CONST.PAYMENT_METHODS.DEBIT_CARD, }; } @@ -178,7 +177,7 @@ function WalletPage({shouldListenForResize = false}: WalletPageProps) { selectedPaymentMethod: account ?? {}, selectedPaymentMethodType: accountType, formattedSelectedPaymentMethod, - methodID: methodID ?? CONST.DEFAULT_NUMBER_ID, + methodID: methodID ?? '-1', }); setShouldShowDefaultDeleteMenu(true); setMenuPosition(); @@ -233,9 +232,9 @@ function WalletPage({shouldListenForResize = false}: WalletPageProps) { const previousPaymentMethod = paymentMethods.find((method) => !!method.isDefault); const currentPaymentMethod = paymentMethods.find((method) => method.methodID === paymentMethod.methodID); if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { - PaymentMethods.makeDefaultPaymentMethod(paymentMethod.selectedPaymentMethod.bankAccountID ?? CONST.DEFAULT_NUMBER_ID, 0, previousPaymentMethod, currentPaymentMethod); + PaymentMethods.makeDefaultPaymentMethod(paymentMethod.selectedPaymentMethod.bankAccountID ?? -1, 0, previousPaymentMethod, currentPaymentMethod); } else if (paymentMethod.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.makeDefaultPaymentMethod(0, paymentMethod.selectedPaymentMethod.fundID ?? CONST.DEFAULT_NUMBER_ID, previousPaymentMethod, currentPaymentMethod); + PaymentMethods.makeDefaultPaymentMethod(0, paymentMethod.selectedPaymentMethod.fundID ?? -1, previousPaymentMethod, currentPaymentMethod); } }, [ paymentMethod.methodID, diff --git a/src/types/form/InternationalBankAccountForm.ts b/src/types/form/InternationalBankAccountForm.ts deleted file mode 100644 index f7c981b6a5c4..000000000000 --- a/src/types/form/InternationalBankAccountForm.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type {BaseForm} from './Form'; - -type InternationalBankAccountForm = BaseForm & Record; - -// eslint-disable-next-line import/prefer-default-export -export type {InternationalBankAccountForm}; diff --git a/src/types/form/index.ts b/src/types/form/index.ts index aced149ddc24..e2854be1ecd6 100644 --- a/src/types/form/index.ts +++ b/src/types/form/index.ts @@ -87,5 +87,10 @@ export type {WorkspaceCompanyCardFeedName} from './WorkspaceCompanyCardFeedName' export type {SearchSavedSearchRenameForm} from './SearchSavedSearchRenameForm'; export type {WorkspaceCompanyCardEditName} from './WorkspaceCompanyCardEditName'; export type {PersonalDetailsForm} from './PersonalDetailsForm'; +<<<<<<< HEAD export type {InternationalBankAccountForm} from './InternationalBankAccountForm'; +======= +export type {MoneyRequestTimeForm} from './MoneyRequestTimeForm'; +export type {MoneyRequestSubrateForm} from './MoneyRequestSubrateForm'; +>>>>>>> a2c8842 (Merge pull request #54729 from Expensify/revert-52322-intl-bank-account) export type {WorkspacePerDiemForm} from './WorkspacePerDiemForm'; diff --git a/src/types/onyx/BankAccount.ts b/src/types/onyx/BankAccount.ts index cd44e6256db6..6da03440fb2e 100644 --- a/src/types/onyx/BankAccount.ts +++ b/src/types/onyx/BankAccount.ts @@ -59,12 +59,6 @@ type BankAccount = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** All data related to the bank account */ accountData?: AccountData; - /** Currency code related to the bank account */ - bankCurrency: string; - - /** Country code related to the bank account */ - bankCountry: string; - /** Any additional error message to show */ errors?: OnyxCommon.Errors; }>; diff --git a/src/types/onyx/CorpayFields.ts b/src/types/onyx/CorpayFields.ts index 2fa2961c41d2..9a1d8bf4d2ab 100644 --- a/src/types/onyx/CorpayFields.ts +++ b/src/types/onyx/CorpayFields.ts @@ -21,38 +21,10 @@ type CorpayFormField = { /** Regular expression for the validation rule */ regEx: string; }>; - /** Contains possible list of values for dropdown field */ - valueSet?: Array<{ - /** Unique identifier for the form field value */ - id: string; - /** Label for the form field value */ - text: string; - }>; - /** Contains possible list of values for dropdown field (only for Canada region fields) */ - links?: Array<{ - /** Contains possible list of values for dropdown field (only for Canada region fields) */ - content: { - /** Whether the list of values complete */ - isCompleteList: boolean; - /** The list of regions */ - regions: Array<{ - /** Region code */ - code: string; - /** Region country code */ - country: string; - /** Region country name */ - countryName: string; - /** Unique Region identifier */ - id: string; - /** Region name */ - name: string; - }>; - }; - }>; }; -/** CorpayFields */ -type CorpayFields = { +/** CorpayFormFields */ +type CorpayFormFields = { /** Country of the bank */ bankCountry: string; /** Currency of the bank */ @@ -61,19 +33,16 @@ type CorpayFields = { classification: string; /** Destination country of the bank */ destinationCountry: string; - /** Possible payment methods */ - paymentMethods: string[]; - /** Preferred method for the bank */ - preferredMethod: string; /** Form fields for the Corpay form */ formFields: CorpayFormField[]; + /** Preferred method for the bank */ + preferredMethod: string; /** Indicates if the fields are loading */ isLoading: boolean; /** Indicates if the fields loaded successfully */ isSuccess: boolean; }; -/** CorpayFieldsMap */ -type CorpayFieldsMap = Record; +export default CorpayFormFields; -export type {CorpayFields, CorpayFormField, CorpayFieldsMap}; +export type {CorpayFormField}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index be43bd8a42ae..06d0e2cd8946 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -16,7 +16,7 @@ import type CardFeeds from './CardFeeds'; import type {AddNewCompanyCardFeed, CompanyCardFeed} from './CardFeeds'; import type CardOnWaitlist from './CardOnWaitlist'; import type {CapturedLogs, Log} from './Console'; -import type {CorpayFields, CorpayFormField} from './CorpayFields'; +import type CorpayFields from './CorpayFields'; import type Credentials from './Credentials'; import type Currency from './Currency'; import type {CurrencyList} from './Currency'; @@ -126,6 +126,7 @@ export type { CardList, CardOnWaitlist, Credentials, + CorpayFields, Currency, CurrencyList, CustomStatusDraft, @@ -242,8 +243,6 @@ export type { Onboarding, OnboardingPurpose, ValidateMagicCodeAction, - CorpayFields, - CorpayFormField, JoinablePolicies, DismissedProductTraining, }; diff --git a/tests/unit/useSubStepTest.tsx b/tests/unit/useSubStepTest.tsx index c4765cca315f..7a5577005d4a 100644 --- a/tests/unit/useSubStepTest.tsx +++ b/tests/unit/useSubStepTest.tsx @@ -9,290 +9,87 @@ function MockSubStepComponent({screenIndex}: SubStepProps) { function MockSubStepComponent2({screenIndex}: SubStepProps) { return {screenIndex}; } -function MockSubStepComponent3({screenIndex}: SubStepProps) { - return {screenIndex}; -} -function MockSubStepComponent4({screenIndex}: SubStepProps) { - return {screenIndex}; -} const mockOnFinished = jest.fn(); -const mockOnFinished2 = jest.fn(); describe('useSubStep hook', () => { - describe('given skipSteps as empty array', () => { - it('returns componentToRender, isEditing, currentIndex, prevScreen, nextScreen, moveTo', () => { - const {result} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent], onFinished: mockOnFinished, startFrom: 0})); - - const {componentToRender, isEditing, moveTo, nextScreen, prevScreen, screenIndex} = result.current; - - expect(componentToRender).toBe(MockSubStepComponent); - expect(isEditing).toBe(false); - expect(screenIndex).toBe(0); - expect(typeof prevScreen).toBe('function'); - expect(typeof nextScreen).toBe('function'); - expect(typeof moveTo).toBe('function'); - }); - - it('calls onFinished when it is the last step', () => { - const {result} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent], onFinished: mockOnFinished, startFrom: 0})); - - const {nextScreen} = result.current; - - act(() => { - nextScreen(); - }); - - expect(mockOnFinished).toHaveBeenCalledTimes(1); - }); - - it('returns component at requested substep when calling moveTo', () => { - const {result, rerender} = renderHook(() => - useSubStep({bodyContent: [MockSubStepComponent2, MockSubStepComponent, MockSubStepComponent], onFinished: mockOnFinished, startFrom: 2}), - ); - - const {moveTo} = result.current; - - act(() => { - moveTo(0); - }); - - rerender({}); - - const {componentToRender} = result.current; + it('returns componentToRender, isEditing, currentIndex, prevScreen, nextScreen, moveTo', () => { + const {result} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent], onFinished: mockOnFinished, startFrom: 0})); - expect(componentToRender).toBe(MockSubStepComponent2); - }); - - it('returns substep component at the previous index when calling prevScreen (if possible)', () => { - const {result, rerender} = renderHook(() => - useSubStep({bodyContent: [MockSubStepComponent2, MockSubStepComponent, MockSubStepComponent], onFinished: mockOnFinished, startFrom: 1}), - ); - - const {prevScreen, screenIndex} = result.current; - - expect(screenIndex).toBe(1); - - act(() => { - prevScreen(); - }); - - rerender({}); - - const {componentToRender, screenIndex: newScreenIndex} = result.current; - expect(newScreenIndex).toBe(0); - - expect(componentToRender).toBe(MockSubStepComponent2); - }); + const {componentToRender, isEditing, moveTo, nextScreen, prevScreen, screenIndex} = result.current; - it('stays on the first substep component when calling prevScreen on the first screen', () => { - const {result, rerender} = renderHook(() => - useSubStep({bodyContent: [MockSubStepComponent2, MockSubStepComponent, MockSubStepComponent], onFinished: mockOnFinished, startFrom: 0}), - ); - - const {componentToRender, prevScreen, screenIndex} = result.current; - - expect(screenIndex).toBe(0); - expect(componentToRender).toBe(MockSubStepComponent2); - - act(() => { - prevScreen(); - }); - - rerender({}); - - const {componentToRender: newComponentToRender, screenIndex: newScreenIndex} = result.current; - - expect(newScreenIndex).toBe(0); - expect(newComponentToRender).toBe(MockSubStepComponent2); - }); + expect(componentToRender).toBe(MockSubStepComponent); + expect(isEditing).toBe(false); + expect(screenIndex).toBe(0); + expect(typeof prevScreen).toBe('function'); + expect(typeof nextScreen).toBe('function'); + expect(typeof moveTo).toBe('function'); }); - describe('given skipSteps as non-empty array', () => { - it('calls onFinished when it is the second last step (last step is skipped)', () => { - const {result} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent, MockSubStepComponent2], onFinished: mockOnFinished2, startFrom: 0, skipSteps: [1]})); - - const {nextScreen} = result.current; + it('calls onFinished when it is the last step', () => { + const {result} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent], onFinished: mockOnFinished, startFrom: 0})); - act(() => { - nextScreen(); - }); + const {nextScreen} = result.current; - expect(mockOnFinished2).toHaveBeenCalledTimes(1); + act(() => { + nextScreen(); }); - it('returns component at requested substep when calling moveTo even though the step is marked as skipped', () => { - const {result, rerender} = renderHook(() => - useSubStep({bodyContent: [MockSubStepComponent2, MockSubStepComponent3, MockSubStepComponent], onFinished: mockOnFinished, startFrom: 2, skipSteps: [1]}), - ); - - const {moveTo} = result.current; - - act(() => { - moveTo(1); - }); - - rerender({}); - - const {componentToRender} = result.current; - - expect(componentToRender).toBe(MockSubStepComponent3); - }); - - it('returns substep component at the previous index when calling prevScreen (if possible)', () => { - const {result, rerender} = renderHook(() => - useSubStep({ - bodyContent: [MockSubStepComponent, MockSubStepComponent2, MockSubStepComponent3, MockSubStepComponent4], - onFinished: mockOnFinished, - startFrom: 3, - skipSteps: [0, 2], - }), - ); - - const {prevScreen, screenIndex} = result.current; - - expect(screenIndex).toBe(3); - - act(() => { - prevScreen(); - }); - - rerender({}); - - const {componentToRender, screenIndex: newScreenIndex} = result.current; - expect(newScreenIndex).toBe(1); - - expect(componentToRender).toBe(MockSubStepComponent2); - }); - - it('stays on the first substep component when calling prevScreen on the second screen if the first screen is skipped', () => { - const {result, rerender} = renderHook(() => - useSubStep({bodyContent: [MockSubStepComponent, MockSubStepComponent2, MockSubStepComponent3], onFinished: mockOnFinished, startFrom: 1, skipSteps: [0]}), - ); - - const {componentToRender, prevScreen, screenIndex} = result.current; - - expect(screenIndex).toBe(1); - expect(componentToRender).toBe(MockSubStepComponent2); - - act(() => { - prevScreen(); - }); - - rerender({}); - - const {componentToRender: newComponentToRender, screenIndex: newScreenIndex} = result.current; - - expect(newScreenIndex).toBe(1); - expect(newComponentToRender).toBe(MockSubStepComponent2); - }); - - it('skips step which are marked as skipped when using nextScreen', () => { - const {result, rerender} = renderHook(() => - useSubStep({ - bodyContent: [MockSubStepComponent, MockSubStepComponent2, MockSubStepComponent3, MockSubStepComponent4], - onFinished: mockOnFinished, - startFrom: 0, - skipSteps: [1, 2], - }), - ); - - const {componentToRender, nextScreen, screenIndex} = result.current; - - expect(screenIndex).toBe(0); - expect(componentToRender).toBe(MockSubStepComponent); - - act(() => { - nextScreen(); - }); + expect(mockOnFinished).toHaveBeenCalledTimes(1); + }); - rerender({}); + it('returns component at requested substep when calling moveTo', () => { + const {result, rerender} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent2, MockSubStepComponent, MockSubStepComponent], onFinished: mockOnFinished, startFrom: 2})); - const {componentToRender: newComponentToRender, screenIndex: newScreenIndex} = result.current; + const {moveTo} = result.current; - expect(newScreenIndex).toBe(3); - expect(newComponentToRender).toBe(MockSubStepComponent4); + act(() => { + moveTo(0); }); - it('nextScreen works correctly when called from skipped screen', () => { - const {result, rerender} = renderHook(() => - useSubStep({ - bodyContent: [MockSubStepComponent, MockSubStepComponent2, MockSubStepComponent3, MockSubStepComponent4], - onFinished: mockOnFinished, - startFrom: 1, - skipSteps: [1, 2], - }), - ); + rerender({}); - const {componentToRender, nextScreen, screenIndex} = result.current; + const {componentToRender} = result.current; - expect(screenIndex).toBe(1); - expect(componentToRender).toBe(MockSubStepComponent2); + expect(componentToRender).toBe(MockSubStepComponent2); + }); - act(() => { - nextScreen(); - }); + it('returns substep component at the previous index when calling prevScreen (if possible)', () => { + const {result, rerender} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent2, MockSubStepComponent, MockSubStepComponent], onFinished: mockOnFinished, startFrom: 1})); - rerender({}); + const {prevScreen, screenIndex} = result.current; - const {componentToRender: newComponentToRender, screenIndex: newScreenIndex} = result.current; + expect(screenIndex).toBe(1); - expect(newScreenIndex).toBe(3); - expect(newComponentToRender).toBe(MockSubStepComponent4); + act(() => { + prevScreen(); }); - it('skips step which are marked as skipped when using prevScreen', () => { - const {result, rerender} = renderHook(() => - useSubStep({ - bodyContent: [MockSubStepComponent, MockSubStepComponent2, MockSubStepComponent3, MockSubStepComponent4], - onFinished: mockOnFinished, - startFrom: 3, - skipSteps: [1, 2], - }), - ); + rerender({}); - const {componentToRender, prevScreen, screenIndex} = result.current; + const {componentToRender, screenIndex: newScreenIndex} = result.current; + expect(newScreenIndex).toBe(0); - expect(screenIndex).toBe(3); - expect(componentToRender).toBe(MockSubStepComponent4); + expect(componentToRender).toBe(MockSubStepComponent2); + }); - act(() => { - prevScreen(); - }); + it('stays on the first substep component when calling prevScreen on the first screen', () => { + const {result, rerender} = renderHook(() => useSubStep({bodyContent: [MockSubStepComponent2, MockSubStepComponent, MockSubStepComponent], onFinished: mockOnFinished, startFrom: 0})); - rerender({}); + const {componentToRender, prevScreen, screenIndex} = result.current; - const {componentToRender: newComponentToRender, screenIndex: newScreenIndex} = result.current; + expect(screenIndex).toBe(0); + expect(componentToRender).toBe(MockSubStepComponent2); - expect(newScreenIndex).toBe(0); - expect(newComponentToRender).toBe(MockSubStepComponent); + act(() => { + prevScreen(); }); - it('prevScreen works correctly when called from skipped screen', () => { - const {result, rerender} = renderHook(() => - useSubStep({ - bodyContent: [MockSubStepComponent, MockSubStepComponent2, MockSubStepComponent3, MockSubStepComponent4], - onFinished: mockOnFinished, - startFrom: 2, - skipSteps: [1, 2], - }), - ); - - const {componentToRender, prevScreen, screenIndex} = result.current; + rerender({}); - expect(screenIndex).toBe(2); - expect(componentToRender).toBe(MockSubStepComponent3); + const {componentToRender: newComponentToRender, screenIndex: newScreenIndex} = result.current; - act(() => { - prevScreen(); - }); - - rerender({}); - - const {componentToRender: newComponentToRender, screenIndex: newScreenIndex} = result.current; - - expect(newScreenIndex).toBe(0); - expect(newComponentToRender).toBe(MockSubStepComponent); - }); + expect(newScreenIndex).toBe(0); + expect(newComponentToRender).toBe(MockSubStepComponent2); }); }); From 1e3e67681cddb214b8dfa7ad12dca9b8e979ef64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Jasikowski?= Date: Thu, 2 Jan 2025 14:18:27 +0100 Subject: [PATCH 3/3] Resolved merge conflicts --- src/types/form/index.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/types/form/index.ts b/src/types/form/index.ts index e2854be1ecd6..3c9d90ba3c2d 100644 --- a/src/types/form/index.ts +++ b/src/types/form/index.ts @@ -87,10 +87,4 @@ export type {WorkspaceCompanyCardFeedName} from './WorkspaceCompanyCardFeedName' export type {SearchSavedSearchRenameForm} from './SearchSavedSearchRenameForm'; export type {WorkspaceCompanyCardEditName} from './WorkspaceCompanyCardEditName'; export type {PersonalDetailsForm} from './PersonalDetailsForm'; -<<<<<<< HEAD -export type {InternationalBankAccountForm} from './InternationalBankAccountForm'; -======= -export type {MoneyRequestTimeForm} from './MoneyRequestTimeForm'; -export type {MoneyRequestSubrateForm} from './MoneyRequestSubrateForm'; ->>>>>>> a2c8842 (Merge pull request #54729 from Expensify/revert-52322-intl-bank-account) export type {WorkspacePerDiemForm} from './WorkspacePerDiemForm';