From 61859a628652201881635cdcc890cc8d86b71cea Mon Sep 17 00:00:00 2001 From: Nicolay Arefyeu Date: Wed, 12 Jun 2024 16:24:47 +0300 Subject: [PATCH 01/25] Add billing currency --- src/CONST.ts | 1 + src/ONYXKEYS.ts | 3 + src/ROUTES.ts | 2 + src/SCREENS.ts | 2 + .../PaymentCardChangeCurrencyForm.tsx | 113 ++++++++++++++++++ .../AddPaymentCard/PaymentCardForm.tsx | 33 ++--- src/languages/en.ts | 11 ++ src/languages/es.ts | 11 ++ .../parameters/UpdateBillingCurrencyParams.ts | 9 ++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + .../ModalStackNavigators/index.tsx | 3 + .../CENTRAL_PANE_TO_RHP_MAPPING.ts | 7 +- .../FULL_SCREEN_TO_RHP_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 8 ++ src/libs/Navigation/types.ts | 1 + src/libs/actions/PaymentMethods.ts | 70 ++++++++++- .../PaymentCard/ChangeCurrency/index.tsx | 44 +++++++ .../CardSection/CardSectionActions/index.tsx | 4 +- .../CardSectionDataEmpty/index.tsx | 10 +- .../ChangeBillingCurrency/index.tsx | 45 +++++++ src/types/form/AddDebitCardForm.ts | 3 + src/types/form/ChangeBillingCurrencyForm.ts | 20 ++++ src/types/form/index.ts | 1 + src/types/onyx/Fund.ts | 3 +- 25 files changed, 379 insertions(+), 29 deletions(-) create mode 100644 src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx create mode 100644 src/libs/API/parameters/UpdateBillingCurrencyParams.ts create mode 100644 src/pages/settings/PaymentCard/ChangeCurrency/index.tsx create mode 100644 src/pages/settings/Subscription/PaymentCard/ChangeBillingCurrency/index.tsx create mode 100644 src/types/form/ChangeBillingCurrencyForm.ts diff --git a/src/CONST.ts b/src/CONST.ts index b0e3ab8c3af4b..c26d9cbf21deb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -590,6 +590,7 @@ const CONST = { ONFIDO_TERMS_OF_SERVICE_URL: 'https://onfido.com/terms-of-service/', LIST_OF_RESTRICTED_BUSINESSES: 'https://community.expensify.com/discussion/6191/list-of-restricted-businesses', TRAVEL_TERMS_URL: `${USE_EXPENSIFY_URL}/travelterms`, + PRICING: `https://www.expensify.com/pricing`, // Use Environment.getEnvironmentURL to get the complete URL with port number DEV_NEW_EXPENSIFY_URL: 'https://dev.new.expensify.com:', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0d22d3714fe6c..084a1fbccd07f 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -450,6 +450,8 @@ const ONYXKEYS = { SETTINGS_STATUS_SET_CLEAR_AFTER_FORM_DRAFT: 'settingsStatusSetClearAfterFormDraft', SETTINGS_STATUS_CLEAR_DATE_FORM: 'settingsStatusClearDateForm', SETTINGS_STATUS_CLEAR_DATE_FORM_DRAFT: 'settingsStatusClearDateFormDraft', + CHANGE_BILLING_CURRENCY_FORM: 'changeBillingCurrencyForm', + CHANGE_BILLING_CURRENCY_FORM_DRAFT: 'changeBillingCurrencyFormDraft', PRIVATE_NOTES_FORM: 'privateNotesForm', PRIVATE_NOTES_FORM_DRAFT: 'privateNotesFormDraft', I_KNOW_A_TEACHER_FORM: 'iKnowTeacherForm', @@ -524,6 +526,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.WAYPOINT_FORM]: FormTypes.WaypointForm; [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM]: FormTypes.SettingsStatusSetForm; [ONYXKEYS.FORMS.SETTINGS_STATUS_CLEAR_DATE_FORM]: FormTypes.SettingsStatusClearDateForm; + [ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM]: FormTypes.ChangeBillingCurrencyForm; [ONYXKEYS.FORMS.SETTINGS_STATUS_SET_CLEAR_AFTER_FORM]: FormTypes.SettingsStatusSetClearAfterForm; [ONYXKEYS.FORMS.PRIVATE_NOTES_FORM]: FormTypes.PrivateNotesForm; [ONYXKEYS.FORMS.I_KNOW_A_TEACHER_FORM]: FormTypes.IKnowTeacherForm; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ed20d388bb876..85a320ac2e06c 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -95,6 +95,7 @@ const ROUTES = { WORKSPACE_SWITCHER: 'workspace-switcher', SETTINGS: 'settings', SETTINGS_PROFILE: 'settings/profile', + SETTINGS_CHANGE_CURRENCY: 'settings/add-payment-card/change-currency', SETTINGS_SHARE_CODE: 'settings/shareCode', SETTINGS_DISPLAY_NAME: 'settings/profile/display-name', SETTINGS_TIMEZONE: 'settings/profile/timezone', @@ -104,6 +105,7 @@ const ROUTES = { SETTINGS_SUBSCRIPTION: 'settings/subscription', SETTINGS_SUBSCRIPTION_SIZE: 'settings/subscription/subscription-size', SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD: 'settings/subscription/add-payment-card', + SETTINGS_SUBSCRIPTION_CHANGE_BILLING_CURRENCY: 'settings/subscription/change-billing-currency', SETTINGS_SUBSCRIPTION_DISABLE_AUTO_RENEW_SURVEY: 'settings/subscription/disable-auto-renew-survey', SETTINGS_PRIORITY_MODE: 'settings/preferences/priority-mode', SETTINGS_LANGUAGE: 'settings/preferences/language', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 9f7277a0ad0f3..a8e2023f5cd9c 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -41,6 +41,7 @@ const SCREENS = { SAVE_THE_WORLD: 'Settings_TeachersUnite', APP_DOWNLOAD_LINKS: 'Settings_App_Download_Links', 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_BANK_ACCOUNT_REFACTOR: 'Settings_Add_Bank_Account_Refactor', CLOSE: 'Settings_Close', @@ -109,6 +110,7 @@ const SCREENS = { SIZE: 'Settings_Subscription_Size', ADD_PAYMENT_CARD: 'Settings_Subscription_Add_Payment_Card', DISABLE_AUTO_RENEW_SURVEY: 'Settings_Subscription_DisableAutoRenewSurvey', + CHANGE_BILLING_CURRENCY: 'Settings_Subscription_Change_Billing_Currency', }, }, SAVE_THE_WORLD: { diff --git a/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx b/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx new file mode 100644 index 0000000000000..d0c291225802a --- /dev/null +++ b/src/components/AddPaymentCard/PaymentCardChangeCurrencyForm.tsx @@ -0,0 +1,113 @@ +import React, {useCallback, useState} from 'react'; +import type {ValueOf} from 'type-fest'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import Hoverable from '@components/Hoverable'; +import * as Expensicons from '@components/Icon/Expensicons'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import TextLink from '@components/TextLink'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import INPUT_IDS from '@src/types/form/ChangeBillingCurrencyForm'; +import PaymentCardCurrencyModal from './PaymentCardCurrencyModal'; + +type PaymentCardFormProps = { + initialCurrency?: ValueOf; + isSecurityCodeRequired?: boolean; + changeBillingCurrency: (currency?: ValueOf, values?: FormOnyxValues) => void; +}; + +const REQUIRED_FIELDS = [INPUT_IDS.SECURITY_CODE]; + +function PaymentCardChangeCurrencyForm({changeBillingCurrency, isSecurityCodeRequired, initialCurrency}: PaymentCardFormProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const [isCurrencyModalVisible, setIsCurrencyModalVisible] = useState(false); + const [currency, setCurrency] = useState>(initialCurrency ?? CONST.CURRENCY.USD); + + const validate = (formValues: FormOnyxValues): FormInputErrors => { + const errors = ValidationUtils.getFieldRequiredErrors(formValues, REQUIRED_FIELDS); + + if (formValues.securityCode && !ValidationUtils.isValidSecurityCode(formValues.securityCode)) { + errors.securityCode = 'billingCurrency.error.securityCode'; + } + + return errors; + }; + + const showCurrenciesModal = useCallback(() => { + setIsCurrencyModalVisible(true); + }, []); + + const changeCurrency = useCallback((newCurrency: keyof typeof CONST.CURRENCY) => { + setCurrency(newCurrency); + setIsCurrencyModalVisible(false); + }, []); + + return ( + changeBillingCurrency(currency, formData)} + submitButtonText={translate('common.save')} + scrollContextEnabled + style={[styles.mh5, styles.flexGrow1]} + > + + {(isHovered) => ( + + )} + + + {!!isSecurityCodeRequired && ( + + )} + + } + currentCurrency={currency} + onCurrencyChange={changeCurrency} + onClose={() => setIsCurrencyModalVisible(false)} + /> + + {`${translate('billingCurrency.note')}`}{' '} + {`${translate('billingCurrency.noteLink')}`}{' '} + {`${translate('billingCurrency.notDetails')}`} + + + ); +} + +PaymentCardChangeCurrencyForm.displayName = 'PaymentCardChangeCurrencyForm'; + +export default PaymentCardChangeCurrencyForm; diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 61e9a2d1860a3..5fcf3c2dd6cfc 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -1,7 +1,8 @@ import {useRoute} from '@react-navigation/native'; -import React, {useCallback, useRef, useState} from 'react'; +import React, {useCallback, useRef} from 'react'; import type {ReactNode} from 'react'; import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import AddressSearch from '@components/AddressSearch'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; @@ -18,13 +19,13 @@ import TextLink from '@components/TextLink'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ValidationUtils from '@libs/ValidationUtils'; +import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/AddDebitCardForm'; -import PaymentCardCurrencyModal from './PaymentCardCurrencyModal'; type PaymentCardFormProps = { shouldShowPaymentCardForm?: boolean; @@ -129,15 +130,14 @@ function PaymentCardForm({ headerContent, }: PaymentCardFormProps) { const styles = useThemeStyles(); + const [data] = useOnyx(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM); + const {translate} = useLocalize(); const route = useRoute(); const label = CARD_LABELS[isDebitCard ? CARD_TYPES.DEBIT_CARD : CARD_TYPES.PAYMENT_CARD]; const cardNumberRef = useRef(null); - const [isCurrencyModalVisible, setIsCurrencyModalVisible] = useState(false); - const [currency, setCurrency] = useState(CONST.CURRENCY.USD); - const validate = (formValues: FormOnyxValues): FormInputErrors => { const errors = ValidationUtils.getFieldRequiredErrors(formValues, REQUIRED_FIELDS); @@ -172,13 +172,8 @@ function PaymentCardForm({ return errors; }; - const showCurrenciesModal = useCallback(() => { - setIsCurrencyModalVisible(true); - }, []); - - const changeCurrency = useCallback((newCurrency: keyof typeof CONST.CURRENCY) => { - setCurrency(newCurrency); - setIsCurrencyModalVisible(false); + const openCurrenciesSelectScreen = useCallback(() => { + Navigation.navigate(ROUTES.SETTINGS_CHANGE_CURRENCY); }, []); if (!shouldShowPaymentCardForm) { @@ -191,7 +186,7 @@ function PaymentCardForm({ addPaymentCard(formData, currency)} + onSubmit={addPaymentCard} submitButtonText={submitButtonText} scrollContextEnabled style={[styles.mh5, styles.flexGrow1]} @@ -279,8 +274,8 @@ function PaymentCardForm({ aria-label={translate('common.currency')} role={CONST.ROLE.COMBOBOX} icon={Expensicons.ArrowRight} - onPress={showCurrenciesModal} - value={currency} + onPress={openCurrenciesSelectScreen} + value={data?.currency ?? CONST.CURRENCY.USD} containerStyles={[styles.mt5]} inputStyle={isHovered && styles.cursorPointer} hideFocusedState @@ -302,14 +297,6 @@ function PaymentCardForm({ /> )} - - } - currentCurrency={currency} - onCurrencyChange={changeCurrency} - onClose={() => setIsCurrencyModalVisible(false)} - /> {footerContent} diff --git a/src/languages/en.ts b/src/languages/en.ts index 76e4d5d5a1431..5d104eebb1895 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1051,6 +1051,17 @@ export default { genericFailureMessage: "Private notes couldn't be saved.", }, }, + billingCurrency: { + error: { + securityCode: 'Please enter a valid security code.', + }, + securityCode: 'Security code', + changePaymentCurrency: 'Change payment currency', + paymentCurrency: 'Payment currency', + note: 'Note: Changing your payment currency can impact how much you’ll pay for Expensify. Refer to our', + noteLink: 'pricing page', + notDetails: 'for full details.', + }, addDebitCardPage: { addADebitCard: 'Add a debit card', nameOnCard: 'Name on card', diff --git a/src/languages/es.ts b/src/languages/es.ts index d03e13d1a9ff0..69b7d6c78b29d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1048,6 +1048,17 @@ export default { genericFailureMessage: 'Las notas privadas no han podido ser guardadas.', }, }, + billingCurrency: { + error: { + securityCode: 'Por favor, introduce un código de seguridad válido.', + }, + securityCode: 'Código de seguridad', + changePaymentCurrency: 'Cambiar la moneda de pago', + paymentCurrency: 'Moneda de pago', + note: 'Nota: Cambiar tu moneda de pago puede afectar cuánto pagarás por Expensify. Consulta nuestra', + noteLink: 'página de precios', + notDetails: 'para conocer todos los detalles.', + }, addDebitCardPage: { addADebitCard: 'Añadir una tarjeta de débito', nameOnCard: 'Nombre en la tarjeta', diff --git a/src/libs/API/parameters/UpdateBillingCurrencyParams.ts b/src/libs/API/parameters/UpdateBillingCurrencyParams.ts new file mode 100644 index 0000000000000..957b377a2e951 --- /dev/null +++ b/src/libs/API/parameters/UpdateBillingCurrencyParams.ts @@ -0,0 +1,9 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type UpdateBillingCurrencyParams = { + currency: ValueOf; + cardCVV: string; +}; + +export default UpdateBillingCurrencyParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index b853f134b315a..17d73538f58d4 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -97,6 +97,7 @@ export type {default as UpdateRoomVisibilityParams} from './UpdateRoomVisibility export type {default as UpdateReportWriteCapabilityParams} from './UpdateReportWriteCapabilityParams'; export type {default as AddWorkspaceRoomParams} from './AddWorkspaceRoomParams'; export type {default as UpdatePolicyRoomNameParams} from './UpdatePolicyRoomNameParams'; +export type {default as UpdateBillingCurrencyParams} from './UpdateBillingCurrencyParams'; export type {default as AddEmojiReactionParams} from './AddEmojiReactionParams'; export type {default as RemoveEmojiReactionParams} from './RemoveEmojiReactionParams'; export type {default as LeaveRoomParams} from './LeaveRoomParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 6b34e04e1937b..8eafacda915cf 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -159,6 +159,7 @@ const WRITE_COMMANDS = { UPDATE_MONEY_REQUEST_DESCRIPTION: 'UpdateMoneyRequestDescription', UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY: 'UpdateMoneyRequestAmountAndCurrency', HOLD_MONEY_REQUEST: 'HoldRequest', + UPDATE_BILLING_CARD_CURRENCY: 'UpdateBillingCardCurrency', UNHOLD_MONEY_REQUEST: 'UnHoldRequest', UPDATE_DISTANCE_REQUEST: 'UpdateDistanceRequest', REQUEST_MONEY: 'RequestMoney', @@ -435,6 +436,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_ENABLED]: Parameters.SetPolicyDistanceRatesEnabledParams; [WRITE_COMMANDS.DELETE_POLICY_DISTANCE_RATES]: Parameters.DeletePolicyDistanceRatesParams; [WRITE_COMMANDS.DISMISS_TRACK_EXPENSE_ACTIONABLE_WHISPER]: Parameters.DismissTrackExpenseActionableWhisperParams; + [WRITE_COMMANDS.UPDATE_BILLING_CARD_CURRENCY]: Parameters.UpdateBillingCurrencyParams; [WRITE_COMMANDS.CONVERT_TRACKED_EXPENSE_TO_REQUEST]: Parameters.ConvertTrackedExpenseToRequestParams; [WRITE_COMMANDS.CATEGORIZE_TRACKED_EXPENSE]: Parameters.CategorizeTrackedExpenseParams; [WRITE_COMMANDS.SHARE_TRACKED_EXPENSE]: Parameters.ShareTrackedExpenseParams; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 807c938e21dda..6b2dd3a9ef2c6 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -29,6 +29,7 @@ import type { TravelNavigatorParamList, WalletStatementNavigatorParamList, } from '@navigation/types'; +import ChangeCurrency from '@pages/settings/PaymentCard/ChangeCurrency'; import type {ThemeStyles} from '@styles/index'; import type {Screen} from '@src/SCREENS'; import SCREENS from '@src/SCREENS'; @@ -331,6 +332,8 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/taxes/WorkspaceCreateTaxPage').default as React.ComponentType, [SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require('../../../../pages/TeachersUnite/SaveTheWorldPage').default as React.ComponentType, [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD]: () => require('../../../../pages/settings/Subscription/PaymentCard/AddPaymentCard').default as React.ComponentType, + [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: () => require('../../../../pages/settings/Subscription/PaymentCard/ChangeBillingCurrency').default as React.ComponentType, + [SCREENS.SETTINGS.ADD_PAYMENT_CARD_CHANGE_CURRENCY]: () => require('../../../../pages/settings/PaymentCard/ChangeCurrency').default as React.ComponentType, }); const EnablePaymentsStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts index c4858d3141f1a..2402dd01e00c4 100755 --- a/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/CENTRAL_PANE_TO_RHP_MAPPING.ts @@ -39,7 +39,12 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial> = [SCREENS.SETTINGS.SAVE_THE_WORLD]: [SCREENS.I_KNOW_A_TEACHER, SCREENS.INTRO_SCHOOL_PRINCIPAL, SCREENS.I_AM_A_TEACHER], [SCREENS.SETTINGS.TROUBLESHOOT]: [SCREENS.SETTINGS.CONSOLE], [SCREENS.SEARCH.CENTRAL_PANE]: [SCREENS.SEARCH.REPORT_RHP], - [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD, SCREENS.SETTINGS.SUBSCRIPTION.SIZE, SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY], + [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [ + SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD, + SCREENS.SETTINGS.SUBSCRIPTION.SIZE, + SCREENS.SETTINGS.SUBSCRIPTION.DISABLE_AUTO_RENEW_SURVEY, + SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY, + ], }; export default CENTRAL_PANE_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index f91d290639ff3..ab8647c5b8ffe 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -11,6 +11,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.OWNER_CHANGE_CHECK, SCREENS.WORKSPACE.OWNER_CHANGE_SUCCESS, SCREENS.WORKSPACE.OWNER_CHANGE_ERROR, + SCREENS.WORKSPACE.OWNER_CHANGE_ERROR, ], [SCREENS.WORKSPACE.WORKFLOWS]: [ SCREENS.WORKSPACE.WORKFLOWS_APPROVER, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index bb002ec2c01f8..a2b7da387112a 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -130,6 +130,14 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD, exact: true, }, + [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: { + path: ROUTES.SETTINGS_SUBSCRIPTION_CHANGE_BILLING_CURRENCY, + exact: true, + }, + [SCREENS.SETTINGS.ADD_PAYMENT_CARD_CHANGE_CURRENCY]: { + path: ROUTES.SETTINGS_CHANGE_CURRENCY, + exact: true, + }, [SCREENS.SETTINGS.PREFERENCES.THEME]: { path: ROUTES.SETTINGS_THEME, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5597e7ce00da1..c100a9355b03f 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -250,6 +250,7 @@ type SettingsNavigatorParamList = { tagName: string; }; [SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD]: undefined; + [SCREENS.SETTINGS.SUBSCRIPTION.CHANGE_BILLING_CURRENCY]: undefined; [SCREENS.WORKSPACE.TAXES_SETTINGS]: { policyID: string; }; diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index c12f7a042659a..f4c330711ae61 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -5,7 +5,14 @@ import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; -import type {AddPaymentCardParams, DeletePaymentCardParams, MakeDefaultPaymentMethodParams, PaymentCardParams, TransferWalletBalanceParams} from '@libs/API/parameters'; +import { + AddPaymentCardParams, + DeletePaymentCardParams, + MakeDefaultPaymentMethodParams, + PaymentCardParams, + TransferWalletBalanceParams, + UpdateBillingCurrencyParams, +} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as CardUtils from '@libs/CardUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -273,6 +280,17 @@ function clearDebitCardFormErrorAndSubmit() { [INPUT_IDS.ADDRESS_ZIP_CODE]: '', [INPUT_IDS.ADDRESS_STATE]: '', [INPUT_IDS.ACCEPT_TERMS]: '', + [INPUT_IDS.CURRENCY]: CONST.CURRENCY.USD, + }); +} + +/** + * Set currency for payments + * + */ +function setPaymentMethodCurrency(currency: ValueOf) { + Onyx.merge(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, { + [INPUT_IDS.CURRENCY]: currency, }); } @@ -425,6 +443,54 @@ function deletePaymentCard(fundID: number) { }); } +/** + * Call the API to change billing currency. + * + */ +function updateBillingCurrency(currency: ValueOf, cardCVV: string) { + const parameters: UpdateBillingCurrencyParams = { + cardCVV, + currency, + }; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: 'merge', + key: ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM, + value: { + isLoading: true, + errors: null, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: 'merge', + key: ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM, + value: { + isLoading: false, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: 'merge', + key: ONYXKEYS.FORMS.CHANGE_BILLING_CURRENCY_FORM, + value: { + isLoading: false, + }, + }, + ]; + + API.write(WRITE_COMMANDS.UPDATE_BILLING_CARD_CURRENCY, parameters, { + optimisticData, + successData, + failureData, + }); +} + export { deletePaymentCard, addPaymentCard, @@ -440,8 +506,10 @@ export { saveWalletTransferAccountTypeAndID, saveWalletTransferMethodType, hasPaymentMethodError, + updateBillingCurrency, clearDeletePaymentMethodError, clearAddPaymentMethodError, clearWalletError, + setPaymentMethodCurrency, clearWalletTermsError, }; diff --git a/src/pages/settings/PaymentCard/ChangeCurrency/index.tsx b/src/pages/settings/PaymentCard/ChangeCurrency/index.tsx new file mode 100644 index 0000000000000..d57bdda4cdfb4 --- /dev/null +++ b/src/pages/settings/PaymentCard/ChangeCurrency/index.tsx @@ -0,0 +1,44 @@ +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import PaymentCardChangeCurrencyForm from '@components/AddPaymentCard/PaymentCardChangeCurrencyForm'; +import type {FormOnyxValues} from '@components/Form/types'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@navigation/Navigation'; +import * as PaymentMethods from '@userActions/PaymentMethods'; +import type CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +function ChangeCurrency() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const [debitCardForm] = useOnyx(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM); + + const changeCurrency = useCallback((currency?: ValueOf) => { + if (currency) { + PaymentMethods.setPaymentMethodCurrency(currency); + } + + Navigation.goBack(); + }, []); + + return ( + + + + + + + ); +} + +ChangeCurrency.displayName = 'ChangeCurrency'; + +export default ChangeCurrency; diff --git a/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx b/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx index eb40977c25a98..0347d88abf523 100644 --- a/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSectionActions/index.tsx @@ -5,8 +5,10 @@ import ThreeDotsMenu from '@components/ThreeDotsMenu'; import type ThreeDotsMenuProps from '@components/ThreeDotsMenu/types'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import Navigation from '@navigation/Navigation'; import type {AnchorPosition} from '@styles/index'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; const anchorAlignment = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, @@ -29,7 +31,7 @@ function CardSectionActions() { { icon: Expensicons.MoneyCircle, text: translate('subscription.cardSection.changeCurrency'), - onSelected: () => {}, // TODO: update with navigation to "change currency" screen (https://github.com/Expensify/App/issues/38621) + onSelected: () => Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_CHANGE_BILLING_CURRENCY), }, ], [translate], diff --git a/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx b/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx index 13131841c53dc..9f51718f0feee 100644 --- a/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSectionDataEmpty/index.tsx @@ -1,16 +1,22 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import Button from '@components/Button'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@navigation/Navigation'; +import ROUTES from '@src/ROUTES'; function CardSectionDataEmpty() { const {translate} = useLocalize(); const styles = useThemeStyles(); + const openAddPaymentCardScreen = useCallback(() => { + Navigation.navigate(ROUTES.SETTINGS_SUBSCRIPTION_ADD_PAYMENT_CARD); + }, []); + return (