-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[Payment due @marufsharifi] Add currency selection to card rules #93063
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
03bc82f
add the new routes
JS00001 6267c32
add page content
JS00001 04ae927
create the page
JS00001 e72ba4c
Merge branch 'main' of github.com:Expensify/App into jsenyitko-curren…
JS00001 a473ea5
cleanup types
JS00001 1f8f6fb
Merge branch 'main' of github.com:Expensify/App into jsenyitko-curren…
JS00001 3847f71
add null to spend rule toggle
JS00001 87519dc
update to hide/shjow based ona ction
JS00001 9f25d5f
add new lang
JS00001 f695d36
update currency page & Cat base
JS00001 8076929
update to have the titles
JS00001 e50eda8
add currencies to ast
JS00001 08dffd7
add new screens
JS00001 a265ae6
Merge branches 'jsenyitko-currency-in-rules' and 'main' of github.com…
JS00001 dd8801d
cleanup pages
JS00001 8e2deb3
Merge branch 'main' of github.com:Expensify/App into jsenyitko-curren…
JS00001 aafe45b
cleanup more code, remove 'off'
JS00001 72e2775
fix not sending rules
JS00001 6a81dd3
add translations
JS00001 1d7f2ab
add translations
JS00001 be4554c
add logic to issuing
JS00001 5dd23b0
cleanup logic for set spend rules step
JS00001 248f4f3
fix bugs with issuing
JS00001 ec47ed3
Merge branch 'main' of github.com:Expensify/App into jsenyitko-curren…
JS00001 2e7029e
add settlenment currency
JS00001 cbc9888
start adding header
JS00001 7049790
add select all
JS00001 461907c
fix bugs with selection
JS00001 9c0b29c
add to translations
JS00001 fff5373
update langs for specific modals
JS00001 d453c24
use the correct translations
JS00001 cd6c6c4
add other langs
JS00001 cf8acb1
Merge branch 'main' of github.com:Expensify/App into jsenyitko-curren…
JS00001 20b74bd
translation fix
JS00001 310392d
fix language types
JS00001 dea9660
address comments
JS00001 b912d92
fix lint
JS00001 0b0d62e
address PR comments
JS00001 668030b
Merge branch 'main' of github.com:Expensify/App into jsenyitko-curren…
JS00001 8533875
add subtitles
JS00001 3d3f390
polyglot parrot
JS00001 bce0ef5
address comments
JS00001 b1d5bfb
Merge branch 'main' of github.com:Expensify/App into jsenyitko-curren…
JS00001 d627627
address comments
JS00001 ad25c5e
add translations
JS00001 72a891e
address comments
JS00001 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
209 changes: 209 additions & 0 deletions
209
src/components/SpendRules/configuration/SpendRulesCurrencyBase.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| import React, {useState} from 'react'; | ||
| import {View} from 'react-native'; | ||
| import {useCurrencyListActions, useCurrencyListState} from '@components/CurrencyListContextProvider'; | ||
| import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; | ||
| import HeaderWithBackButton from '@components/HeaderWithBackButton'; | ||
| import Icon from '@components/Icon'; | ||
| import ScreenWrapper from '@components/ScreenWrapper'; | ||
| import SelectionList from '@components/SelectionList'; | ||
| import MultiSelectListItem from '@components/SelectionList/ListItem/MultiSelectListItem'; | ||
| import type {ListItem} from '@components/SelectionList/types'; | ||
| import Text from '@components/Text'; | ||
| import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useSearchResults from '@hooks/useSearchResults'; | ||
| import useTheme from '@hooks/useTheme'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import {canUseTouchScreen} from '@libs/DeviceCapabilities'; | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import {getCurrencyOptions} from '@libs/SearchUIUtils'; | ||
|
|
||
| type SpendRulesCurrencyBaseProps = { | ||
| /** The currently selected currencies */ | ||
| currencies: string[]; | ||
|
|
||
| /** The settlement currency of the currently selected cards */ | ||
| settlementCurrency: string; | ||
|
|
||
| /** Handle the currencies changing */ | ||
| onCurrenciesChange: (currencies: string[]) => void; | ||
| }; | ||
|
|
||
| type CurrencyListItem = ListItem & { | ||
| value: string; | ||
| }; | ||
|
|
||
| export default function SpendRulesCurrencyBase({currencies, settlementCurrency, onCurrenciesChange}: SpendRulesCurrencyBaseProps) { | ||
| const theme = useTheme(); | ||
| const styles = useThemeStyles(); | ||
| const {translate, localeCompare} = useLocalize(); | ||
| const icons = useMemoizedLazyExpensifyIcons(['Lock']); | ||
|
|
||
| const {currencyList} = useCurrencyListState(); | ||
| const {getCurrencySymbol} = useCurrencyListActions(); | ||
| const currencyOptions = getCurrencyOptions(currencyList, getCurrencySymbol); | ||
| const validCurrencyOptions = currencyOptions.filter((option) => option.value !== settlementCurrency); | ||
|
|
||
| const [selectedCurrencies, setSelectedCurrencies] = useState<string[]>(() => { | ||
| if (currencies.length > 0) { | ||
| return currencies.filter((currency) => currency !== settlementCurrency); | ||
| } | ||
|
|
||
| return validCurrencyOptions.map((option) => option.value); | ||
| }); | ||
|
|
||
| const currencyItems: CurrencyListItem[] = []; | ||
| const selectedCurrenciesSet = new Set(selectedCurrencies); | ||
|
|
||
| let areAllCurrenciesSelected = true; | ||
| let settlementCurrencyLabel = settlementCurrency; | ||
|
|
||
| for (const currencyOption of currencyOptions) { | ||
| if (currencyOption.value === settlementCurrency) { | ||
| settlementCurrencyLabel = currencyOption.text; | ||
| continue; | ||
| } | ||
|
|
||
| const isSelected = selectedCurrenciesSet.has(currencyOption.value); | ||
|
|
||
| currencyItems.push({ | ||
| isSelected, | ||
| keyForList: currencyOption.value, | ||
| text: currencyOption.text, | ||
| value: currencyOption.value, | ||
| }); | ||
|
|
||
| if (!isSelected) { | ||
| areAllCurrenciesSelected = false; | ||
| } | ||
| } | ||
|
|
||
| const filterCurrency = (item: CurrencyListItem, searchInput: string) => { | ||
| return (item.text ?? '').toLowerCase().includes(searchInput); | ||
|
JS00001 marked this conversation as resolved.
|
||
| }; | ||
|
|
||
| const sortCurrencies = (items: CurrencyListItem[]) => { | ||
| return items.sort((a, b) => localeCompare(a.text ?? '', b.text ?? '')); | ||
| }; | ||
|
|
||
| const [inputValue, setInputValue, filteredCurrencyItems] = useSearchResults(currencyItems, filterCurrency, sortCurrencies); | ||
|
|
||
| const toggleCurrency = (item: CurrencyListItem) => { | ||
| setSelectedCurrencies((prev) => { | ||
| if (prev.includes(item.value)) { | ||
| return prev.filter((currency) => currency !== item.value); | ||
| } | ||
| return [...prev, item.value]; | ||
| }); | ||
| }; | ||
|
|
||
| const toggleSelectAll = () => { | ||
| const visibleValues = filteredCurrencyItems.map((item) => item.value); | ||
| const allVisibleSelected = visibleValues.length > 0 && visibleValues.every((value) => selectedCurrenciesSet.has(value)); | ||
|
|
||
| if (allVisibleSelected) { | ||
| const visibleSet = new Set(visibleValues); | ||
| setSelectedCurrencies((prev) => prev.filter((currency) => !visibleSet.has(currency))); | ||
| return; | ||
| } | ||
|
|
||
| setSelectedCurrencies((prev) => { | ||
| const next = new Set([...prev, ...visibleValues]); | ||
| return Array.from(next); | ||
| }); | ||
| }; | ||
|
|
||
| const goBack = () => { | ||
| Navigation.goBack(); | ||
| }; | ||
|
|
||
| const saveChanges = () => { | ||
| if (selectedCurrencies.length === currencyOptions.length - 1) { | ||
| onCurrenciesChange([]); | ||
| } else { | ||
| onCurrenciesChange([...selectedCurrencies, settlementCurrency]); | ||
| } | ||
|
|
||
| goBack(); | ||
| }; | ||
|
|
||
| const ListHeaderContent = ( | ||
| <View style={[styles.flexColumn]}> | ||
| <MultiSelectListItem | ||
| isFocused={false} | ||
| showTooltip={false} | ||
| keyForList="select-all" | ||
| item={{keyForList: 'select-all', text: translate('workspace.rules.spendRules.allCurrencies'), isSelected: areAllCurrenciesSelected}} | ||
| onSelectRow={toggleSelectAll} | ||
| /> | ||
| <View style={[styles.borderBottom, styles.mh5, styles.mv2]} /> | ||
| <View style={[styles.flexRow, styles.alignItemsCenter, styles.mv4, styles.mh5]}> | ||
| <View style={[styles.flex1, styles.gap1]}> | ||
| <Text | ||
| style={styles.textStrong} | ||
| numberOfLines={1} | ||
| > | ||
| {settlementCurrencyLabel} | ||
| </Text> | ||
| <Text | ||
| style={[styles.textLabel, styles.textSupporting]} | ||
| numberOfLines={1} | ||
| > | ||
| {translate('workspace.rules.spendRules.settlementCurrencyPermittedSubtitle')} | ||
| </Text> | ||
| </View> | ||
| <Icon | ||
| medium | ||
| src={icons.Lock} | ||
| fill={theme.icon} | ||
| /> | ||
| </View> | ||
| </View> | ||
| ); | ||
|
|
||
| return ( | ||
| <ScreenWrapper | ||
| testID="SpendRuleCurrenciesPage" | ||
| shouldEnableMaxHeight | ||
| includeSafeAreaPaddingBottom | ||
| offlineIndicatorStyle={styles.mtAuto} | ||
| > | ||
| <HeaderWithBackButton | ||
| title={translate('workspace.rules.spendRules.permittedCurrencies')} | ||
| onBackButtonPress={goBack} | ||
| /> | ||
|
|
||
| <Text style={[styles.textLabel, styles.textSupporting, styles.ph5, styles.pb4]}>{translate('workspace.rules.spendRules.permittedCurrenciesSubtitle')}</Text> | ||
|
|
||
| <SelectionList | ||
| canSelectMultiple | ||
| shouldUpdateFocusedIndex | ||
| customListHeaderContent={ListHeaderContent} | ||
| ListItem={MultiSelectListItem} | ||
| data={filteredCurrencyItems} | ||
| selectedItems={selectedCurrencies} | ||
| shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()} | ||
| onSelectRow={toggleCurrency} | ||
| onSelectionButtonPress={toggleCurrency} | ||
| textInputOptions={{ | ||
| value: inputValue, | ||
| label: translate('common.search'), | ||
| onChangeText: setInputValue, | ||
| }} | ||
| style={{ | ||
| listHeaderWrapperStyle: [styles.pt5, styles.pb2], | ||
| listHeaderSelectAllTextStyle: [styles.textLabelSupporting], | ||
|
JS00001 marked this conversation as resolved.
|
||
| }} | ||
| footerContent={ | ||
| <FormAlertWithSubmitButton | ||
| buttonText={translate('common.save')} | ||
| isAlertVisible={false} | ||
| onSubmit={saveChanges} | ||
| enabledWhenOffline | ||
| containerStyles={[styles.flexReset, styles.flexGrow0, styles.flexShrink0, styles.flexBasisAuto]} | ||
| /> | ||
| } | ||
| /> | ||
| </ScreenWrapper> | ||
| ); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.