Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions src/components/CurrencySelectionList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,38 @@ import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelec
import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections';

import {useCurrencyListActions, useCurrencyListState} from '@hooks/useCurrencyList';
import useInitialSelection from '@hooks/useInitialSelection';
import useLocalize from '@hooks/useLocalize';

import getMatchScore from '@libs/getMatchScore';

import {isEmptyObject} from '@src/types/utils/EmptyObject';

import {Str} from 'expensify-common';
import React, {useState} from 'react';
import React, {useMemo, useState} from 'react';

import type {CurrencyListItem, CurrencySelectionListProps} from './types';

const EMPTY_SELECTED_CURRENCIES: string[] = [];

function CurrencySelectionList({
searchInputLabel,
initiallySelectedCurrencyCode,
onSelect,
didScreenTransitionEnd = true,
selectedCurrencies = [],
selectedCurrencies = EMPTY_SELECTED_CURRENCIES,
recentlyUsedCurrencies,
excludedCurrencies = [],
...restProps
}: CurrencySelectionListProps) {
const {currencyList} = useCurrencyListState();
const {getCurrencySymbol} = useCurrencyListActions();
const [searchValue, setSearchValue] = useState('');
const selectedCurrencyCodes = useMemo(() => [initiallySelectedCurrencyCode, ...selectedCurrencies].filter(Boolean), [initiallySelectedCurrencyCode, selectedCurrencies]);
const initiallyPinnedCurrencyCodes = useInitialSelection(selectedCurrencyCodes, {resetOnFocus: true});
const {translate} = useLocalize();
const getUnselectedOptions = (options: CurrencyListItem[]) => options.filter((option) => !option.isSelected);
const initiallyPinnedCurrencyCodeSet = new Set(initiallyPinnedCurrencyCodes);
const getUnpinnedOptions = (options: CurrencyListItem[]) => options.filter((option) => !initiallyPinnedCurrencyCodeSet.has(option.currencyCode));

const currencyOptions: CurrencyListItem[] = Object.entries(currencyList).reduce((acc, [currencyCode, currencyInfo]) => {
const isSelectedCurrency = currencyCode === initiallySelectedCurrencyCode || selectedCurrencies.includes(currencyCode);
Expand Down Expand Up @@ -64,15 +70,15 @@ function CurrencySelectionList({

const isEmpty = searchValue.trim() && !filteredCurrencies.length;
const shouldDisplayRecentlyOptions = !isEmptyObject(recentlyUsedCurrencyOptions) && !searchValue;
const selectedOptions = filteredCurrencies.filter((option) => option.isSelected);
const shouldDisplaySelectedOptionOnTop = selectedOptions.length > 0;
const unselectedOptions = getUnselectedOptions(filteredCurrencies);
const initiallyPinnedOptions = filteredCurrencies.filter((option) => initiallyPinnedCurrencyCodeSet.has(option.currencyCode));
const shouldDisplayInitiallyPinnedOptionsOnTop = initiallyPinnedOptions.length > 0;
const unpinnedOptions = getUnpinnedOptions(filteredCurrencies);
const sections = [];

if (shouldDisplaySelectedOptionOnTop) {
if (shouldDisplayInitiallyPinnedOptionsOnTop) {
sections.push({
title: undefined,
data: selectedOptions,
data: initiallyPinnedOptions,
sectionIndex: 0,
});
}
Expand All @@ -82,15 +88,15 @@ function CurrencySelectionList({
sections.push(
{
title: translate('common.recents'),
data: shouldDisplaySelectedOptionOnTop ? getUnselectedOptions(recentlyUsedCurrencyOptions) : recentlyUsedCurrencyOptions,
data: shouldDisplayInitiallyPinnedOptionsOnTop ? getUnpinnedOptions(recentlyUsedCurrencyOptions) : recentlyUsedCurrencyOptions,
sectionIndex: 1,
},
{title: translate('common.all'), data: shouldDisplayRecentlyOptions ? unselectedOptions : filteredCurrencies, sectionIndex: 2},
{title: translate('common.all'), data: shouldDisplayRecentlyOptions ? unpinnedOptions : filteredCurrencies, sectionIndex: 2},
);
}
} else if (!isEmpty) {
sections.push({
data: shouldDisplaySelectedOptionOnTop ? unselectedOptions : filteredCurrencies,
data: shouldDisplayInitiallyPinnedOptionsOnTop ? unpinnedOptions : filteredCurrencies,
sectionIndex: 3,
});
}
Expand Down
29 changes: 16 additions & 13 deletions src/components/Search/SearchSingleSelectionPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelec
import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections';

import useDebouncedState from '@hooks/useDebouncedState';
import useInitialSelection from '@hooks/useInitialSelection';
import useLocalize from '@hooks/useLocalize';

import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -47,28 +48,30 @@ function SearchSingleSelectionPicker({

const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
const [selectedItem, setSelectedItem] = useState<SearchSingleSelectionPickerItem | undefined>(initiallySelectedItem);
const initialSelectedItem = useInitialSelection(initiallySelectedItem, {resetOnFocus: true});

useEffect(() => {
setSelectedItem(initiallySelectedItem);
}, [initiallySelectedItem]);

const searchLower = debouncedSearchTerm?.toLowerCase();
const noneItem = allowNoneOption ? getNoneOption(debouncedSearchTerm, !selectedItem?.value, translate) : [];
const initialSelectedItemMatchesSearch =
!!initialSelectedItem && (initialSelectedItem.name.toLowerCase().includes(searchLower) || initialSelectedItem.searchableText?.toLowerCase().includes(searchLower));

const initiallySelectedItemSection =
initiallySelectedItem?.name.toLowerCase().includes(searchLower) || initiallySelectedItem?.searchableText?.toLowerCase().includes(searchLower)
? [
{
text: initiallySelectedItem.name,
keyForList: initiallySelectedItem.value,
isSelected: selectedItem?.value === initiallySelectedItem.value,
value: initiallySelectedItem.value,
},
]
: [];
const initiallySelectedItemSection = initialSelectedItemMatchesSearch
? [
{
text: initialSelectedItem.name,
keyForList: initialSelectedItem.value,
isSelected: selectedItem?.value === initialSelectedItem.value,
value: initialSelectedItem.value,
},
]
: [];

const remainingItemsSection = items
.filter((item) => item.value !== initiallySelectedItem?.value && (item.name.toLowerCase().includes(searchLower) || item.searchableText?.toLowerCase().includes(searchLower)))
.filter((item) => item.value !== initialSelectedItem?.value && (item.name.toLowerCase().includes(searchLower) || item.searchableText?.toLowerCase().includes(searchLower)))
.sort((a, b) => sortOptionsWithEmptyValue(a.name.toString(), b.name.toString(), localeCompare))
.map((item) => ({
text: item.name,
Expand Down Expand Up @@ -136,7 +139,7 @@ function SearchSingleSelectionPicker({
sections={sections}
onSelectRow={onSelectItem}
ListItem={SingleSelectListItem}
initiallyFocusedItemKey={initiallySelectedItem?.value}
initiallyFocusedItemKey={initialSelectedItem?.value}
shouldShowTextInput={shouldShowTextInput}
textInputOptions={textInputOptions}
footerContent={shouldAutoSave ? undefined : footerContent}
Expand Down
Loading