diff --git a/Mobile-Expensify b/Mobile-Expensify index 2df28fc86ca4..50e07511c4e7 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 2df28fc86ca48169c9f91e09287bd3bb3364429c +Subproject commit 50e07511c4e77644770026f38c2d70b2c1333b39 diff --git a/android/app/build.gradle b/android/app/build.gradle index 3a52425c6686..8a0ab638be0d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -114,8 +114,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009027304 - versionName "9.2.73-4" + versionCode 1009027305 + versionName "9.2.73-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 b144b6d4433c..79015247ca43 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.2.73.4 + 9.2.73.5 FullStory OrgId diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index eaabcc249233..0bea1fbed6b8 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.2.73 CFBundleVersion - 9.2.73.4 + 9.2.73.5 NSExtension NSExtensionPointIdentifier diff --git a/ios/ShareViewController/Info.plist b/ios/ShareViewController/Info.plist index 7e2ecffeac5a..11c98015cf5a 100644 --- a/ios/ShareViewController/Info.plist +++ b/ios/ShareViewController/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.2.73 CFBundleVersion - 9.2.73.4 + 9.2.73.5 NSExtension NSExtensionAttributes diff --git a/package-lock.json b/package-lock.json index 132cd0e021a4..ad73e4c02bbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.2.73-4", + "version": "9.2.73-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.2.73-4", + "version": "9.2.73-5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 9809bbdec059..bccde6c0d5d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.2.73-4", + "version": "9.2.73-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.", diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ce439c4ad1ec..912a981584e4 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1079,6 +1079,12 @@ const ROUTES = { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getUrlWithBackToParam(`settings/${policyID}/category/${encodeURIComponent(categoryName)}/gl-code` as const, backTo), }, + MONEY_REQUEST_STEP_CURRENCY: { + route: ':action/:iouType/currency/:transactionID/:reportID/:pageIndex?', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex = '', currency = '', backTo = '') => + // eslint-disable-next-line no-restricted-syntax -- Legacy route generation + getUrlWithBackToParam(`${action as string}/${iouType as string}/currency/${transactionID}/${reportID}/${pageIndex}?currency=${currency}`, backTo), + }, MONEY_REQUEST_STEP_DATE: { route: ':action/:iouType/date/:transactionID/:reportID/:reportActionID?', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string | undefined, reportID: string | undefined, backTo = '', reportActionID?: string) => { diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 6c282f03554d..ce5eb617480e 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -289,6 +289,7 @@ const SCREENS = { STEP_UPGRADE: 'Money_Request_Step_Upgrade', STEP_AMOUNT: 'Money_Request_Step_Amount', STEP_CATEGORY: 'Money_Request_Step_Category', + STEP_CURRENCY: 'Money_Request_Step_Currency', STEP_DATE: 'Money_Request_Step_Date', STEP_DESCRIPTION: 'Money_Request_Step_Description', STEP_DISTANCE: 'Money_Request_Step_Distance', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 26144c4bfd26..90df0bc2f3a6 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -208,6 +208,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/iou/request/step/IOURequestStepTaxAmountPage').default, [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: () => require('../../../../pages/iou/request/step/IOURequestStepTaxRatePage').default, [SCREENS.MONEY_REQUEST.STEP_CATEGORY]: () => require('../../../../pages/iou/request/step/IOURequestStepCategory').default, + [SCREENS.MONEY_REQUEST.STEP_CURRENCY]: () => require('../../../../pages/iou/request/step/IOURequestStepCurrency').default, [SCREENS.MONEY_REQUEST.STEP_DATE]: () => require('../../../../pages/iou/request/step/IOURequestStepDate').default, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: () => require('../../../../pages/iou/request/step/IOURequestStepDescription').default, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: () => require('../../../../pages/iou/request/step/IOURequestStepDistance').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index e823b8515d1c..a1678afff018 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1416,6 +1416,7 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.STEP_CATEGORY]: ROUTES.MONEY_REQUEST_STEP_CATEGORY.route, [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.route, [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION_VERIFY_ACCOUNT]: ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.route, + [SCREENS.MONEY_REQUEST.STEP_CURRENCY]: ROUTES.MONEY_REQUEST_STEP_CURRENCY.route, [SCREENS.MONEY_REQUEST.STEP_DATE]: ROUTES.MONEY_REQUEST_STEP_DATE.route, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.route, [SCREENS.MONEY_REQUEST.STEP_DISTANCE]: ROUTES.MONEY_REQUEST_STEP_DISTANCE.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 40248daab723..9b7df56a59db 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1571,6 +1571,7 @@ type MoneyRequestNavigatorParamList = { reportID: string; // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; + currency?: string; }; [SCREENS.MONEY_REQUEST.STEP_TAG]: { action: IOUAction; @@ -1652,6 +1653,7 @@ type MoneyRequestNavigatorParamList = { // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: never; action: never; + currency: never; pageIndex?: string; backToReport?: string; reportActionID?: string; @@ -1670,6 +1672,7 @@ type MoneyRequestNavigatorParamList = { backTo: Routes; action: IOUAction; pageIndex?: string; + currency?: string; backToReport?: string; reportActionID?: string; }; @@ -1714,6 +1717,16 @@ type MoneyRequestNavigatorParamList = { // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes; }; + [SCREENS.MONEY_REQUEST.STEP_CURRENCY]: { + action: IOUAction; + iouType: IOUType; + transactionID: string; + reportID: string; + pageIndex?: string; + // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md + backTo?: Routes; + currency?: string; + }; [SCREENS.MONEY_REQUEST.HOLD]: { /** ID of the transaction the page was opened for */ transactionID: string; @@ -1825,6 +1838,7 @@ type MoneyRequestNavigatorParamList = { // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: never; action: never; + currency: never; pageIndex?: string; backToReport?: string; reportActionID?: string; diff --git a/src/pages/iou/request/DistanceRequestStartPage.tsx b/src/pages/iou/request/DistanceRequestStartPage.tsx index 3fd94bb41edd..8d8fbf39513b 100644 --- a/src/pages/iou/request/DistanceRequestStartPage.tsx +++ b/src/pages/iou/request/DistanceRequestStartPage.tsx @@ -92,6 +92,26 @@ function DistanceRequestStartPage({ Navigation.closeRHPFlow(); }; + // This useEffect is used to initialize the money request, so that currency will be reset to default currency on page reload. + useEffect(() => { + if (transaction?.amount !== 0) { + return; + } + initMoneyRequest({ + reportID, + policy, + isFromGlobalCreate, + currentIouRequestType: transaction?.iouRequestType, + newIouRequestType: transaction?.iouRequestType, + report, + parentReport, + currentDate, + lastSelectedDistanceRates, + currentUserPersonalDetails, + }); + // eslint-disable-next-line + }, []); + const resetIOUTypeIfChanged = useCallback( (newIOUType: IOURequestType) => { Keyboard.dismiss(); diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 0dc475b48dc8..2f470eaaf943 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -137,6 +137,26 @@ function IOURequestStartPage({ Navigation.closeRHPFlow(); }; + // This useEffect is used to initialize the money request, so that currency will be reset to default currency on page reload. + useEffect(() => { + if (transaction?.amount !== 0) { + return; + } + initMoneyRequest({ + reportID, + policy, + isFromGlobalCreate, + currentIouRequestType: transaction?.iouRequestType, + newIouRequestType: transaction?.iouRequestType, + report, + parentReport, + currentDate, + lastSelectedDistanceRates, + currentUserPersonalDetails, + }); + // eslint-disable-next-line + }, []); + const resetIOUTypeIfChanged = useCallback( (newIOUType: IOURequestType) => { Keyboard.dismiss(); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 6a81d8599dfb..bbe392173a88 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -1,9 +1,10 @@ import {useFocusEffect} from '@react-navigation/native'; import reportsSelector from '@selectors/Attributes'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import useDefaultExpensePolicy from '@hooks/useDefaultExpensePolicy'; import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations'; import useLocalize from '@hooks/useLocalize'; @@ -13,7 +14,8 @@ import usePersonalPolicy from '@hooks/usePersonalPolicy'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; import {setTransactionReport} from '@libs/actions/Transaction'; -import {convertToBackendAmount} from '@libs/CurrencyUtils'; +import {createDraftTransaction, removeDraftTransaction} from '@libs/actions/TransactionEdit'; +import {convertToBackendAmount, isValidCurrencyCode} from '@libs/CurrencyUtils'; import {navigateToParticipantPage} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; @@ -43,7 +45,6 @@ import type {SelectedTabRequest} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type Transaction from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import IOURequestStepCurrencyModal from './IOURequestStepCurrencyModal'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; @@ -54,26 +55,26 @@ type AmountParams = { paymentMethod?: PaymentMethodType; }; -type IOURequestStepAmountProps = WithWritableReportOrNotFoundProps & { - /** The transaction object being modified in Onyx */ - transaction: OnyxEntry; +type IOURequestStepAmountProps = WithCurrentUserPersonalDetailsProps & + WithWritableReportOrNotFoundProps & { + /** The transaction object being modified in Onyx */ + transaction: OnyxEntry; - /** Whether the user input should be kept or not */ - shouldKeepUserInput?: boolean; -}; + /** Whether the user input should be kept or not */ + shouldKeepUserInput?: boolean; + }; function IOURequestStepAmount({ report, route: { - params: {iouType, reportID, transactionID = '-1', backTo, action, backToReport, reportActionID}, + params: {iouType, reportID, transactionID = '-1', backTo, pageIndex, action, currency: selectedCurrency = '', backToReport, reportActionID}, }, transaction, + currentUserPersonalDetails, shouldKeepUserInput = false, }: IOURequestStepAmountProps) { const {translate} = useLocalize(); const {isBetaEnabled} = usePermissions(); - const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const [isCurrencyPickerVisible, setIsCurrencyPickerVisible] = useState(false); const textInput = useRef(null); const focusTimeoutRef = useRef(null); const isSaveButtonPressed = useRef(false); @@ -102,7 +103,7 @@ function IOURequestStepAmount({ const disableOppositeConversion = isCreateAction || (isSubmitType && isSubmitAction); const {amount: transactionAmount} = getTransactionDetails(currentTransaction, undefined, undefined, allowNegative, disableOppositeConversion) ?? {amount: 0}; const {currency: originalCurrency} = getTransactionDetails(isEditing && !isEmptyObject(draftTransaction) ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; - const [selectedCurrency, setSelectedCurrency] = useState(originalCurrency); + const currency = isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useShowNotFoundPageInIOUStep(action, iouType, reportActionID, report, transaction); const shouldGenerateTransactionThreadReport = !isBetaEnabled(CONST.BETAS.NO_OPTIMISTIC_TRANSACTION_THREADS); @@ -134,15 +135,32 @@ function IOURequestStepAmount({ }, []), ); - // This useEffect is to update the selected currency when the we came back from confirmation page useEffect(() => { - setSelectedCurrency(originalCurrency); - }, [originalCurrency]); + if (!isEditing) { + return; + } + // A temporary solution to not prevent users from editing the currency + // We create a backup transaction and use it to save the currency and remove this transaction backup if we don't save the amount + // It should be removed after this issue https://github.com/Expensify/App/issues/34607 is fixed + createDraftTransaction(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction); + + return () => { + if (isSaveButtonPressed.current) { + return; + } + removeDraftTransaction(transaction?.transactionID); + }; + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, []); const navigateBack = () => { Navigation.goBack(backTo); }; + const navigateToCurrencySelectionPage = () => { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CURRENCY.getRoute(action, iouType, transactionID, reportID, pageIndex, currency, Navigation.getActiveRoute())); + }; + const navigateToConfirmationPage = () => { switch (iouType) { case CONST.IOU.TYPE.REQUEST: @@ -161,7 +179,7 @@ function IOURequestStepAmount({ const amountInSmallestCurrencyUnits = convertToBackendAmount(Number.parseFloat(amount)); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, selectedCurrency || CONST.CURRENCY.USD, shouldKeepUserInput); + setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, shouldKeepUserInput); if (backTo) { Navigation.goBack(backTo); @@ -185,10 +203,10 @@ function IOURequestStepAmount({ if (shouldSkipConfirmation) { if (iouType === CONST.IOU.TYPE.PAY || iouType === CONST.IOU.TYPE.SEND) { if (paymentMethod && paymentMethod === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { - sendMoneyWithWallet(report, backendAmount, selectedCurrency, '', currentUserAccountIDParam, participants.at(0) ?? {}); + sendMoneyWithWallet(report, backendAmount, currency, '', currentUserAccountIDParam, participants.at(0) ?? {}); return; } - sendMoneyElsewhere(report, backendAmount, selectedCurrency, '', currentUserAccountIDParam, participants.at(0) ?? {}); + sendMoneyElsewhere(report, backendAmount, currency, '', currentUserAccountIDParam, participants.at(0) ?? {}); return; } if (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.REQUEST) { @@ -201,7 +219,7 @@ function IOURequestStepAmount({ }, transactionParams: { amount: backendAmount, - currency: selectedCurrency, + currency, created: transaction?.created ?? '', merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, attendees: transaction?.comment?.attendees, @@ -226,7 +244,7 @@ function IOURequestStepAmount({ }, transactionParams: { amount: backendAmount, - currency: selectedCurrency ?? CONST.CURRENCY.USD, + currency: currency ?? 'USD', created: transaction?.created, merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, }, @@ -237,7 +255,7 @@ function IOURequestStepAmount({ } if (isSplitBill && !report.isOwnPolicyExpenseChat && report.participants) { const participantAccountIDs = Object.keys(report.participants).map((accountID) => Number(accountID)); - setSplitShares(transaction, amountInSmallestCurrencyUnits, selectedCurrency || CONST.CURRENCY.USD, participantAccountIDs); + setSplitShares(transaction, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, participantAccountIDs); } setMoneyRequestParticipantsFromReport(transactionID, report, currentUserPersonalDetails.accountID).then(() => { navigateToConfirmationPage(); @@ -300,7 +318,7 @@ function IOURequestStepAmount({ // Edits to the amount from the splits page should reset the split shares. if (transaction?.splitShares) { - resetSplitShares(transaction, newAmount, selectedCurrency); + resetSplitShares(transaction, newAmount, currency); } if (!isEditing) { @@ -310,20 +328,20 @@ function IOURequestStepAmount({ // If the value hasn't changed, don't request to save changes on the server and just close the modal const transactionCurrency = getCurrency(currentTransaction); - if (newAmount === getAmount(currentTransaction, false, false, allowNegative, disableOppositeConversion) && selectedCurrency === transactionCurrency) { + if (newAmount === getAmount(currentTransaction, false, false, allowNegative, disableOppositeConversion) && currency === transactionCurrency) { navigateBack(); return; } // If currency has changed, then we get the default tax rate based on currency, otherwise we use the current tax rate selected in transaction, if we have it. const transactionTaxCode = getTransactionDetails(currentTransaction)?.taxCode; - const defaultTaxCode = getDefaultTaxCode(policy, currentTransaction, selectedCurrency) ?? ''; - const taxCode = (selectedCurrency !== transactionCurrency ? defaultTaxCode : transactionTaxCode) ?? defaultTaxCode; + const defaultTaxCode = getDefaultTaxCode(policy, currentTransaction, currency) ?? ''; + const taxCode = (currency !== transactionCurrency ? defaultTaxCode : transactionTaxCode) ?? defaultTaxCode; const taxPercentage = getTaxValue(policy, currentTransaction, taxCode) ?? ''; - const taxAmount = convertToBackendAmount(calculateTaxAmount(taxPercentage, newAmount, selectedCurrency ?? CONST.CURRENCY.USD)); + const taxAmount = convertToBackendAmount(calculateTaxAmount(taxPercentage, newAmount, currency ?? CONST.CURRENCY.USD)); if (isSplitBill) { - setDraftSplitTransaction(transactionID, splitDraftTransaction, {amount: newAmount, currency: selectedCurrency, taxCode, taxAmount}); + setDraftSplitTransaction(transactionID, splitDraftTransaction, {amount: newAmount, currency, taxCode, taxAmount}); navigateBack(); return; } @@ -333,7 +351,7 @@ function IOURequestStepAmount({ transactionThreadReportID: reportID, transactions: duplicateTransactions, transactionViolations: duplicateTransactionViolations, - currency: selectedCurrency, + currency, amount: newAmount, taxAmount, policy, @@ -346,18 +364,6 @@ function IOURequestStepAmount({ navigateBack(); }; - const hideCurrencyPicker = useCallback(() => { - setIsCurrencyPickerVisible(false); - }, []); - - const updateSelectedCurrency = useCallback((value: string) => { - setSelectedCurrency(value); - }, []); - - const showCurrencyPicker = useCallback(() => { - setIsCurrencyPickerVisible(true); - }, []); - return ( - ; + +function IOURequestStepCurrency({ + route: { + params: {backTo, pageIndex, transactionID, action, currency: selectedCurrency = ''}, + }, +}: IOURequestStepCurrencyProps) { + const {translate} = useLocalize(); + const [draftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {canBeMissing: true}); + const [recentlyUsedCurrencies] = useOnyx(ONYXKEYS.RECENTLY_USED_CURRENCIES, {canBeMissing: true}); + const {currency: originalCurrency = ''} = getTransactionDetails(draftTransaction) ?? {}; + const currency = isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; + + const navigateBack = (selectedCurrencyValue = '') => { + // If the currency selection was done from the confirmation step (eg. + > submit expense > manual > confirm > amount > currency) + // then the user needs taken back to the confirmation page instead of the initial amount page. This is because the route params + // are only able to handle one backTo param at a time and the user needs to go back to the amount page before going back + // to the confirmation page + if (pageIndex === CONST.IOU.PAGE_INDEX.CONFIRM) { + if (selectedCurrencyValue) { + Navigation.goBack(appendParam(backTo as string, 'currency', selectedCurrencyValue), {compareParams: false}); + } else { + Navigation.goBack(backTo); + } + return; + } + Navigation.goBack(backTo); + }; + + const confirmCurrencySelection = (option: CurrencyListItem) => { + Keyboard.dismiss(); + if (pageIndex !== CONST.IOU.PAGE_INDEX.CONFIRM) { + setMoneyRequestCurrency(transactionID, option.currencyCode, action === CONST.IOU.ACTION.EDIT); + } + + Navigation.setNavigationActionToMicrotaskQueue(() => navigateBack(option.currencyCode)); + }; + + return ( + navigateBack()} + shouldShowWrapper + testID={IOURequestStepCurrency.displayName} + includeSafeAreaPaddingBottom + > + {({didScreenTransitionEnd}) => ( + { + if (!didScreenTransitionEnd) { + return; + } + confirmCurrencySelection(option); + }} + initiallySelectedCurrencyCode={currency.toUpperCase()} + /> + )} + + ); +} + +IOURequestStepCurrency.displayName = 'IOURequestStepCurrency'; + +/* eslint-disable rulesdir/no-negated-variables */ +const IOURequestStepCurrencyWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepCurrency); + +export default IOURequestStepCurrencyWithFullTransactionOrNotFound; diff --git a/src/pages/iou/request/step/IOURequestStepCurrencyModal.tsx b/src/pages/iou/request/step/IOURequestStepCurrencyModal.tsx deleted file mode 100644 index 0c3c1a309080..000000000000 --- a/src/pages/iou/request/step/IOURequestStepCurrencyModal.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React from 'react'; -import CurrencySelectionList from '@components/CurrencySelectionList'; -import type {CurrencyListItem} from '@components/CurrencySelectionList/types'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Modal from '@components/Modal'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; - -type CurrencyPickerProps = { - /** Whether the picker modal is visible */ - isPickerVisible: boolean; - - /** Function to hide the picker modal */ - hidePickerModal: () => void; - - /** Header text for the modal */ - headerText: string; - - /** Current value of the selected item */ - value?: string; - - /** Callback when the list item is selected */ - onInputChange?: (value: string, key?: string) => void; - - /** List of currencies to exclude from the list */ - excludeCurrencies?: string[]; -}; - -function IOURequestStepCurrencyModal({isPickerVisible, hidePickerModal, headerText, value, excludeCurrencies, onInputChange = () => {}}: CurrencyPickerProps) { - const {translate} = useLocalize(); - const [recentlyUsedCurrencies] = useOnyx(ONYXKEYS.RECENTLY_USED_CURRENCIES, {canBeMissing: true}); - const styles = useThemeStyles(); - - const updateInput = (item: CurrencyListItem) => { - onInputChange?.(item.currencyCode); - hidePickerModal(); - }; - - return ( - - - - - - - ); -} - -IOURequestStepCurrencyModal.displayName = 'IOURequestStepCurrencyModal'; -export default IOURequestStepCurrencyModal; diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 1d0dab8c8dbc..c54346dc5113 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -8,7 +8,7 @@ import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; import useRestartOnReceiptFailure from '@hooks/useRestartOnReceiptFailure'; import {setDraftSplitTransaction, setMoneyRequestCurrency, setMoneyRequestParticipantsFromReport, setMoneyRequestTaxAmount, updateMoneyRequestTaxAmount} from '@libs/actions/IOU'; -import {convertToBackendAmount} from '@libs/CurrencyUtils'; +import {convertToBackendAmount, isValidCurrencyCode} from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getTransactionDetails} from '@libs/ReportUtils'; import {calculateTaxAmount, getAmount, getDefaultTaxCode, getTaxValue, getTaxAmount as getTransactionTaxAmount} from '@libs/TransactionUtils'; @@ -46,7 +46,7 @@ function getTaxAmount(transaction: OnyxEntry, policy: OnyxEntry { @@ -87,6 +87,23 @@ function IOURequestStepTaxAmountPage({ Navigation.goBack(backTo); }; + const navigateToCurrencySelectionPage = () => { + // If the expense being created is a distance expense, don't allow the user to choose the currency. + // Only USD is allowed for distance expenses. + // Remove query from the route and encode it. + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_CURRENCY.getRoute( + CONST.IOU.ACTION.CREATE, + iouType, + transactionID, + reportID, + backTo ? 'confirm' : '', + currency, + Navigation.getActiveRouteWithoutParams(), + ), + ); + }; + const updateTaxAmount = (currentAmount: CurrentMoney) => { const taxAmountInSmallestCurrencyUnits = convertToBackendAmount(Number.parseFloat(currentAmount.amount)); @@ -157,8 +174,7 @@ function IOURequestStepTaxAmountPage({ ref={(e) => { textInput.current = e; }} - // onCurrencyButtonPress is intentionally left empty as currency selection is not allowed on this page - onCurrencyButtonPress={() => {}} + onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={updateTaxAmount} isCurrencyPressable={false} chatReportID={reportID} diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx index a2858b91b1f5..2c1c6d40c815 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx @@ -3,7 +3,6 @@ import type {ComponentType} from 'react'; import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import useOnyx from '@hooks/useOnyx'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; @@ -40,6 +39,7 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_CATEGORY | typeof SCREENS.MONEY_REQUEST.STEP_TAX_RATE | typeof SCREENS.MONEY_REQUEST.STEP_SCAN + | typeof SCREENS.MONEY_REQUEST.STEP_CURRENCY | typeof SCREENS.MONEY_REQUEST.STEP_SEND_FROM | typeof SCREENS.MONEY_REQUEST.STEP_REPORT | typeof SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO @@ -56,7 +56,6 @@ type WithFullTransactionOrNotFoundProps export default function >( WrappedComponent: ComponentType, - shouldShowLoadingIndicator = false, ): React.ComponentType> { // eslint-disable-next-line rulesdir/no-negated-variables function WithFullTransactionOrNotFound(props: Omit) { @@ -82,10 +81,6 @@ export default function ; } - - if (isLoadingTransaction && shouldShowLoadingIndicator) { - return ; - } return (