diff --git a/src/components/CurrencySelectionList/index.tsx b/src/components/CurrencySelectionList/index.tsx new file mode 100644 index 000000000000..361d82140326 --- /dev/null +++ b/src/components/CurrencySelectionList/index.tsx @@ -0,0 +1,64 @@ +import Str from 'expensify-common/lib/str'; +import React, {useMemo, useState} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import SelectionList from '@components/SelectionList'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import useLocalize from '@hooks/useLocalize'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {CurrencyListItem, CurrencySelectionListOnyxProps, CurrencySelectionListProps} from './types'; + +function CurrencySelectionList({searchInputLabel, initiallySelectedCurrencyCode, onSelect, currencyList}: CurrencySelectionListProps) { + const [searchValue, setSearchValue] = useState(''); + const {translate} = useLocalize(); + + const {sections, headerMessage} = useMemo(() => { + const currencyOptions: CurrencyListItem[] = Object.entries(currencyList ?? {}).map(([currencyCode, currencyInfo]) => { + const isSelectedCurrency = currencyCode === initiallySelectedCurrencyCode; + return { + currencyName: currencyInfo?.name ?? '', + text: `${currencyCode} - ${CurrencyUtils.getCurrencySymbol(currencyCode)}`, + currencyCode, + keyForList: currencyCode, + isSelected: isSelectedCurrency, + }; + }); + + const searchRegex = new RegExp(Str.escapeForRegExp(searchValue.trim()), 'i'); + const filteredCurrencies = currencyOptions.filter((currencyOption) => searchRegex.test(currencyOption.text ?? '') || searchRegex.test(currencyOption.currencyName)); + const isEmpty = searchValue.trim() && !filteredCurrencies.length; + + return { + sections: isEmpty + ? [] + : [ + { + data: filteredCurrencies, + }, + ], + headerMessage: isEmpty ? translate('common.noResultsFound') : '', + }; + }, [currencyList, searchValue, translate, initiallySelectedCurrencyCode]); + + return ( + + ); +} + +CurrencySelectionList.displayName = 'CurrencySelectionList'; + +const CurrencySelectionListWithOnyx = withOnyx({ + currencyList: {key: ONYXKEYS.CURRENCY_LIST}, +})(CurrencySelectionList); + +export default CurrencySelectionListWithOnyx; diff --git a/src/components/CurrencySelectionList/types.ts b/src/components/CurrencySelectionList/types.ts new file mode 100644 index 000000000000..eb7cf72d4e1e --- /dev/null +++ b/src/components/CurrencySelectionList/types.ts @@ -0,0 +1,26 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {ListItem} from '@components/SelectionList/types'; +import type {CurrencyList} from '@src/types/onyx'; + +type CurrencyListItem = ListItem & { + currencyName: string; + currencyCode: string; +}; + +type CurrencySelectionListOnyxProps = { + /** List of available currencies */ + currencyList: OnyxEntry; +}; + +type CurrencySelectionListProps = CurrencySelectionListOnyxProps & { + /** Label for the search text input */ + searchInputLabel: string; + + /** Currency item to be selected initially */ + initiallySelectedCurrencyCode?: string; + + /** Callback to fire when a currency is selected */ + onSelect: (item: CurrencyListItem) => void; +}; + +export type {CurrencyListItem, CurrencySelectionListProps, CurrencySelectionListOnyxProps}; diff --git a/src/pages/iou/request/step/IOURequestStepCurrency.tsx b/src/pages/iou/request/step/IOURequestStepCurrency.tsx index d03136063b61..8669563f3b9f 100644 --- a/src/pages/iou/request/step/IOURequestStepCurrency.tsx +++ b/src/pages/iou/request/step/IOURequestStepCurrency.tsx @@ -1,11 +1,9 @@ -import Str from 'expensify-common/lib/str'; -import React, {useMemo, useState} from 'react'; +import React from 'react'; import {Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import SelectionList from '@components/SelectionList'; -import RadioListItem from '@components/SelectionList/RadioListItem'; -import type {ListItem} from '@components/SelectionList/types'; +import CurrencySelectionList from '@components/CurrencySelectionList'; +import type {CurrencyListItem} from '@components/CurrencySelectionList/types'; import useLocalize from '@hooks/useLocalize'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -16,35 +14,25 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES, {getUrlWithBackToParam} from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {CurrencyList, Transaction} from '@src/types/onyx'; +import type {Transaction} from '@src/types/onyx'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; type IOURequestStepCurrencyOnyxProps = { - /** Constant, list of available currencies */ - currencyList: OnyxEntry; - /** The draft transaction object being modified in Onyx */ draftTransaction: OnyxEntry; }; type IOURequestStepCurrencyProps = IOURequestStepCurrencyOnyxProps & WithFullTransactionOrNotFoundProps; -type CurrencyListItem = ListItem & { - currencyName: string; - currencyCode: string; -}; - function IOURequestStepCurrency({ - currencyList, route: { params: {backTo, iouType, pageIndex, reportID, transactionID, action, currency: selectedCurrency = ''}, }, draftTransaction, }: IOURequestStepCurrencyProps) { const {translate} = useLocalize(); - const [searchValue, setSearchValue] = useState(''); const {currency: originalCurrency = ''} = ReportUtils.getTransactionDetails(draftTransaction) ?? {}; const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; @@ -76,35 +64,6 @@ function IOURequestStepCurrency({ navigateBack(option.currencyCode); }; - const {sections, headerMessage, initiallyFocusedOptionKey} = useMemo(() => { - const currencyOptions: CurrencyListItem[] = Object.entries(currencyList ?? {}).map(([currencyCode, currencyInfo]) => { - const isSelectedCurrency = currencyCode === currency.toUpperCase(); - return { - currencyName: currencyInfo?.name ?? '', - text: `${currencyCode} - ${CurrencyUtils.getLocalizedCurrencySymbol(currencyCode)}`, - currencyCode, - keyForList: currencyCode, - isSelected: isSelectedCurrency, - }; - }); - - const searchRegex = new RegExp(Str.escapeForRegExp(searchValue.trim()), 'i'); - const filteredCurrencies = currencyOptions.filter((currencyOption) => searchRegex.test(currencyOption.text ?? '') || searchRegex.test(currencyOption.currencyName)); - const isEmpty = searchValue.trim() && !filteredCurrencies.length; - - return { - initiallyFocusedOptionKey: filteredCurrencies.find((filteredCurrency) => filteredCurrency.currencyCode === currency.toUpperCase())?.keyForList, - sections: isEmpty - ? [] - : [ - { - data: filteredCurrencies, - }, - ], - headerMessage: isEmpty ? translate('common.noResultsFound') : '', - }; - }, [currencyList, searchValue, currency, translate]); - return ( {({didScreenTransitionEnd}) => ( - { + { if (!didScreenTransitionEnd) { return; } confirmCurrencySelection(option); }} - headerMessage={headerMessage} - initiallyFocusedOptionKey={initiallyFocusedOptionKey} - showScrollIndicator + initiallySelectedCurrencyCode={currency.toUpperCase()} /> )} @@ -138,7 +91,6 @@ function IOURequestStepCurrency({ IOURequestStepCurrency.displayName = 'IOURequestStepCurrency'; const IOURequestStepCurrencyWithOnyx = withOnyx({ - currencyList: {key: ONYXKEYS.CURRENCY_LIST}, draftTransaction: { key: ({route}) => { const transactionID = route?.params?.transactionID ?? 0; diff --git a/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx b/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx index bef697f37a88..8d00975c7494 100644 --- a/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx +++ b/src/pages/workspace/WorkspaceProfileCurrencyPage.tsx @@ -1,73 +1,25 @@ -import React, {useState} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import React from 'react'; +import CurrencySelectionList from '@components/CurrencySelectionList'; +import type {CurrencyListItem} from '@components/CurrencySelectionList/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; -import SelectionList from '@components/SelectionList'; -import RadioListItem from '@components/SelectionList/RadioListItem'; import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {CurrencyList} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import AccessOrNotFoundWrapper from './AccessOrNotFoundWrapper'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; -type WorkspaceProfileCurrentPageOnyxProps = { - /** Constant, list of available currencies */ - currencyList: OnyxEntry; -}; +type WorkspaceProfileCurrencyPageProps = WithPolicyAndFullscreenLoadingProps; -type WorkspaceProfileCurrentPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceProfileCurrentPageOnyxProps; - -type WorkspaceProfileCurrencyPageSectionItem = { - text: string; - keyForList: string; - isSelected: boolean; -}; - -const getDisplayText = (currencyCode: string, currencySymbol: string) => `${currencyCode} - ${currencySymbol}`; - -function WorkspaceProfileCurrencyPage({currencyList = {}, policy}: WorkspaceProfileCurrentPageProps) { +function WorkspaceProfileCurrencyPage({policy}: WorkspaceProfileCurrencyPageProps) { const {translate} = useLocalize(); - const [searchText, setSearchText] = useState(''); - const trimmedText = searchText.trim().toLowerCase(); - const currencyListKeys = Object.keys(currencyList ?? {}); - - const filteredItems = currencyListKeys.filter((currencyCode: string) => { - const currency = currencyList?.[currencyCode]; - return getDisplayText(currencyCode, currency?.symbol ?? '') - .toLowerCase() - .includes(trimmedText); - }); - - let initiallyFocusedOptionKey; - - const currencyItems: WorkspaceProfileCurrencyPageSectionItem[] = filteredItems.map((currencyCode: string) => { - const currency = currencyList?.[currencyCode]; - const isSelected = policy?.outputCurrency === currencyCode; - - if (isSelected) { - initiallyFocusedOptionKey = currencyCode; - } - - return { - text: getDisplayText(currencyCode, currency?.symbol ?? ''), - keyForList: currencyCode, - isSelected, - }; - }); - - const sections = [{data: currencyItems}]; - - const headerMessage = searchText.trim() && !currencyItems.length ? translate('common.noResultsFound') : ''; - const onSelectCurrency = (item: WorkspaceProfileCurrencyPageSectionItem) => { - Policy.updateGeneralSettings(policy?.id ?? '', policy?.name ?? '', item.keyForList); + const onSelectCurrency = (item: CurrencyListItem) => { + Policy.updateGeneralSettings(policy?.id ?? '', policy?.name ?? '', item.currencyCode); Navigation.goBack(); }; @@ -86,16 +38,10 @@ function WorkspaceProfileCurrencyPage({currencyList = {}, policy}: WorkspaceProf onBackButtonPress={() => Navigation.goBack()} /> - @@ -104,8 +50,4 @@ function WorkspaceProfileCurrencyPage({currencyList = {}, policy}: WorkspaceProf WorkspaceProfileCurrencyPage.displayName = 'WorkspaceProfileCurrencyPage'; -export default withPolicyAndFullscreenLoading( - withOnyx({ - currencyList: {key: ONYXKEYS.CURRENCY_LIST}, - })(WorkspaceProfileCurrencyPage), -); +export default withPolicyAndFullscreenLoading(WorkspaceProfileCurrencyPage);