Skip to content

Commit

Permalink
Merge pull request #30889 from tsa321/curr-slist
Browse files Browse the repository at this point in the history
  • Loading branch information
cead22 authored Apr 30, 2024
2 parents 0fc945b + 4a9648c commit 4c1e8b8
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 126 deletions.
64 changes: 64 additions & 0 deletions src/components/CurrencySelectionList/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<SelectionList
sections={sections}
ListItem={RadioListItem}
textInputLabel={searchInputLabel}
textInputValue={searchValue}
onChangeText={setSearchValue}
onSelectRow={onSelect}
headerMessage={headerMessage}
initiallyFocusedOptionKey={initiallySelectedCurrencyCode}
showScrollIndicator
/>
);
}

CurrencySelectionList.displayName = 'CurrencySelectionList';

const CurrencySelectionListWithOnyx = withOnyx<CurrencySelectionListProps, CurrencySelectionListOnyxProps>({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
})(CurrencySelectionList);

export default CurrencySelectionListWithOnyx;
26 changes: 26 additions & 0 deletions src/components/CurrencySelectionList/types.ts
Original file line number Diff line number Diff line change
@@ -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<CurrencyList>;
};

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};
64 changes: 8 additions & 56 deletions src/pages/iou/request/step/IOURequestStepCurrency.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<CurrencyList>;

/** The draft transaction object being modified in Onyx */
draftTransaction: OnyxEntry<Transaction>;
};

type IOURequestStepCurrencyProps = IOURequestStepCurrencyOnyxProps & WithFullTransactionOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_CURRENCY>;

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;

Expand Down Expand Up @@ -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 (
<StepScreenWrapper
headerTitle={translate('common.selectCurrency')}
Expand All @@ -114,21 +73,15 @@ function IOURequestStepCurrency({
includeSafeAreaPaddingBottom={false}
>
{({didScreenTransitionEnd}) => (
<SelectionList
sections={sections}
ListItem={RadioListItem}
textInputLabel={translate('common.search')}
textInputValue={searchValue}
onChangeText={setSearchValue}
onSelectRow={(option) => {
<CurrencySelectionList
searchInputLabel={translate('common.search')}
onSelect={(option: CurrencyListItem) => {
if (!didScreenTransitionEnd) {
return;
}
confirmCurrencySelection(option);
}}
headerMessage={headerMessage}
initiallyFocusedOptionKey={initiallyFocusedOptionKey}
showScrollIndicator
initiallySelectedCurrencyCode={currency.toUpperCase()}
/>
)}
</StepScreenWrapper>
Expand All @@ -138,7 +91,6 @@ function IOURequestStepCurrency({
IOURequestStepCurrency.displayName = 'IOURequestStepCurrency';

const IOURequestStepCurrencyWithOnyx = withOnyx<IOURequestStepCurrencyProps, IOURequestStepCurrencyOnyxProps>({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
draftTransaction: {
key: ({route}) => {
const transactionID = route?.params?.transactionID ?? 0;
Expand Down
82 changes: 12 additions & 70 deletions src/pages/workspace/WorkspaceProfileCurrencyPage.tsx
Original file line number Diff line number Diff line change
@@ -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<CurrencyList>;
};
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();
};

Expand All @@ -86,16 +38,10 @@ function WorkspaceProfileCurrencyPage({currencyList = {}, policy}: WorkspaceProf
onBackButtonPress={() => Navigation.goBack()}
/>

<SelectionList
sections={sections}
ListItem={RadioListItem}
textInputLabel={translate('workspace.editor.currencyInputLabel')}
textInputValue={searchText}
onChangeText={setSearchText}
onSelectRow={onSelectCurrency}
headerMessage={headerMessage}
initiallyFocusedOptionKey={initiallyFocusedOptionKey}
showScrollIndicator
<CurrencySelectionList
searchInputLabel={translate('workspace.editor.currencyInputLabel')}
onSelect={onSelectCurrency}
initiallySelectedCurrencyCode={policy?.outputCurrency}
/>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
Expand All @@ -104,8 +50,4 @@ function WorkspaceProfileCurrencyPage({currencyList = {}, policy}: WorkspaceProf

WorkspaceProfileCurrencyPage.displayName = 'WorkspaceProfileCurrencyPage';

export default withPolicyAndFullscreenLoading(
withOnyx<WorkspaceProfileCurrentPageProps, WorkspaceProfileCurrentPageOnyxProps>({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
})(WorkspaceProfileCurrencyPage),
);
export default withPolicyAndFullscreenLoading(WorkspaceProfileCurrencyPage);

0 comments on commit 4c1e8b8

Please sign in to comment.