Skip to content

Commit 31fac99

Browse files
authored
Merge pull request #1197 from alephium/region-mw
Region settings
2 parents 3ec04e7 + a189bf6 commit 31fac99

File tree

25 files changed

+1801
-40
lines changed

25 files changed

+1801
-40
lines changed

.changeset/tall-foxes-eat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@alephium/mobile-wallet": patch
3+
---
4+
5+
Format amounts based on user's locale

.changeset/wild-jobs-train.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@alephium/mobile-wallet": patch
3+
---
4+
5+
Tap to reveal hidden amounts

apps/desktop-wallet/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import useTrackUserSettings from '@/features/analytics/useTrackUserSettings'
1414
import AutoUpdateSnackbar from '@/features/autoUpdate/AutoUpdateSnackbar'
1515
import { languageOptions } from '@/features/localization/languages'
1616
import { systemLanguageMatchFailed, systemLanguageMatchSucceeded } from '@/features/localization/localizationActions'
17+
import useRegionOptions from '@/features/settings/regionSettings/useRegionOptions'
1718
import {
1819
localStorageGeneralSettingsMigrated,
1920
systemRegionMatchFailed,
2021
systemRegionMatchSucceeded
2122
} from '@/features/settings/settingsActions'
22-
import useRegionOptions from '@/features/settings/useRegionOptions'
2323
import { darkTheme, lightTheme } from '@/features/theme/themes'
2424
import { WalletConnectContextProvider } from '@/features/walletConnect/walletConnectContext'
2525
import { useAppDispatch, useAppSelector } from '@/hooks/redux'

apps/desktop-wallet/src/features/analytics/useTrackUserSettings.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const useTrackUserSettings = () => {
1515
const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement)
1616
const fiatCurrency = useAppSelector((s) => s.settings.fiatCurrency)
1717
const network = useAppSelector((s) => s.network.name)
18+
const region = useAppSelector((s) => s.settings.region)
1819

1920
useEffect(() => {
2021
if (posthog.__loaded)
@@ -27,7 +28,8 @@ const useTrackUserSettings = () => {
2728
language,
2829
passwordRequirement,
2930
fiatCurrency,
30-
network
31+
network,
32+
region
3133
})
3234
}, [
3335
devTools,
@@ -38,6 +40,7 @@ const useTrackUserSettings = () => {
3840
passwordRequirement,
3941
posthog.__loaded,
4042
posthog.people,
43+
region,
4144
theme,
4245
walletLockTimeInMinutes
4346
])

apps/desktop-wallet/src/features/settings/RegionSettings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next'
33
import KeyValueInput from '@/components/Inputs/InlineLabelValueInput'
44
import Select from '@/components/Inputs/Select'
55
import useAnalytics from '@/features/analytics/useAnalytics'
6+
import useRegionOptions from '@/features/settings/regionSettings/useRegionOptions'
67
import { numberFormatRegionChanged } from '@/features/settings/settingsActions'
7-
import useRegionOptions from '@/features/settings/useRegionOptions'
88
import { useAppDispatch, useAppSelector } from '@/hooks/redux'
99

1010
const RegionSettings = () => {

apps/desktop-wallet/src/features/settings/useRegionOptions.ts renamed to apps/desktop-wallet/src/features/settings/regionSettings/useRegionOptions.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { upperFirst } from 'lodash'
22
import { useMemo } from 'react'
33

4-
import { Language } from '@/features/localization/languages'
54
import { useAppSelector } from '@/hooks/redux'
65

76
import regionsLocales from './regions.json'
@@ -14,6 +13,11 @@ const useRegionOptions = () => {
1413

1514
export default useRegionOptions
1615

16+
const getRegionsOptions = (languageLocale: string) =>
17+
regionsLocales
18+
.map((regionLocale) => getRegionOption(regionLocale, languageLocale))
19+
.sort((a, b) => a.label.localeCompare(b.label))
20+
1721
// Inspired by https://github.com/LedgerHQ/ledger-live/blob/065dda3/apps/ledger-live-desktop/src/renderer/screens/settings/sections/General/RegionSelect.tsx
1822
const getRegionOption = (regionLocale: string, languageLocale: string | Intl.Locale) => {
1923
const [language, region = ''] = regionLocale.split('-')
@@ -32,8 +36,3 @@ const getRegionOption = (regionLocale: string, languageLocale: string | Intl.Loc
3236
label
3337
}
3438
}
35-
36-
const getRegionsOptions = (languageLocale: Language) =>
37-
regionsLocales
38-
.map((regionLocale) => getRegionOption(regionLocale, languageLocale))
39-
.sort((a, b) => a.label.localeCompare(b.label))

apps/mobile-wallet/locales/en-US/translation.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,5 +456,7 @@
456456
"Splitting your funds into multiple addresses can help you stay organized and increase privacy.": "Splitting your funds into multiple addresses can help you stay organized and increase privacy.",
457457
"No dApps match your search: \"{{ searchText }}\"": "No dApps match your search: \"{{ searchText }}\"",
458458
"Visit dApp": "Visit dApp",
459+
"Region": "Region",
460+
"Choose your region to update formats of dates, time, and currencies.": "Choose your region to update formats of dates, time, and currencies.",
459461
"More details on Alph.land": "More details on Alph.land"
460462
}

apps/mobile-wallet/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { DefaultTheme, ThemeProvider } from 'styled-components/native'
2020
import ToastAnchor from '~/components/toasts/ToastAnchor'
2121
import LoadingManager from '~/features/loader/LoadingManager'
2222
import { useLocalization } from '~/features/localization/useLocalization'
23+
import { useSystemRegion } from '~/features/settings/regionSettings/useSystemRegion'
2324
import useLoadStoredSettings from '~/features/settings/useLoadStoredSettings'
2425
import { useAppDispatch, useAppSelector } from '~/hooks/redux'
2526
import { useAsyncData } from '~/hooks/useAsyncData'
@@ -129,6 +130,7 @@ const Main = ({ children, ...props }: ViewProps) => {
129130
useLoadStoredSettings()
130131
useInitializeClient()
131132
useLocalization()
133+
useSystemRegion()
132134

133135
useEffect(() => {
134136
if (walletMetadata) dispatch(appLaunchedWithLastUsedWallet(walletMetadata))

apps/mobile-wallet/src/components/Amount.tsx

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { convertToPositive, formatAmountForDisplay, formatFiatAmountForDisplay } from '@alephium/shared'
1+
import { convertToPositive, formatAmountForDisplay } from '@alephium/shared'
2+
import { useState } from 'react'
23
import { StyleProp, TextStyle } from 'react-native'
34

45
import { useAppSelector } from '~/hooks/redux'
@@ -9,7 +10,6 @@ export interface AmountProps extends AppTextProps {
910
value?: bigint | number
1011
decimals?: number
1112
isFiat?: boolean
12-
fadeDecimals?: boolean
1313
fullPrecision?: boolean
1414
nbOfDecimalsToShow?: number
1515
suffix?: string
@@ -24,7 +24,6 @@ export interface AmountProps extends AppTextProps {
2424

2525
const Amount = ({
2626
value,
27-
fadeDecimals,
2827
fullPrecision = false,
2928
suffix = '',
3029
showOnDiscreetMode = false,
@@ -40,59 +39,70 @@ const Amount = ({
4039
...props
4140
}: AmountProps) => {
4241
const discreetMode = useAppSelector((state) => state.settings.discreetMode)
42+
const region = useAppSelector((state) => state.settings.region)
43+
const fiatCurrency = useAppSelector((state) => state.settings.currency)
44+
45+
const [tappedToDisableDiscreetMode, setTappedToDisableDiscreetMode] = useState(false)
46+
47+
const hideAmount = discreetMode && !showOnDiscreetMode && !tappedToDisableDiscreetMode
48+
49+
const handleTappedToDisableDiscreetMode = () => setTappedToDisableDiscreetMode(!tappedToDisableDiscreetMode)
4350

44-
let quantitySymbol = ''
4551
let amount = ''
52+
let tinyAmount = ''
4653
let isNegative = false
54+
const color = props.color ?? (highlight && value !== undefined ? (value < 0 ? 'send' : 'receive') : 'primary')
4755

4856
if (value !== undefined) {
4957
isNegative = value < 0
5058

5159
if (isFiat && typeof value === 'number') {
52-
amount = formatFiatAmountForDisplay(isNegative ? value * -1 : value)
60+
amount = new Intl.NumberFormat(region, { style: 'currency', currency: fiatCurrency }).format(value)
61+
62+
return (
63+
<AppText {...props} {...{ color, style }} onPress={handleTappedToDisableDiscreetMode}>
64+
{hideAmount ? '•••' : amount}
65+
</AppText>
66+
)
5367
} else if (isUnknownToken) {
5468
amount = convertToPositive(value as bigint).toString()
5569
} else {
5670
amount = formatAmountForDisplay({
5771
amount: convertToPositive(value as bigint),
5872
amountDecimals: decimals,
5973
displayDecimals: nbOfDecimalsToShow,
60-
fullPrecision
74+
fullPrecision,
75+
region
6176
})
62-
}
6377

64-
if (fadeDecimals && ['K', 'M', 'B', 'T'].some((char) => amount.endsWith(char))) {
65-
quantitySymbol = amount.slice(-1)
66-
amount = amount.slice(0, -1)
67-
}
68-
}
69-
70-
let [integralPart, fractionalPart] = amount.split('.')
78+
const amountIsTooSmall = formatAmountForDisplay({
79+
amount: convertToPositive(value as bigint),
80+
amountDecimals: decimals,
81+
displayDecimals: nbOfDecimalsToShow,
82+
fullPrecision
83+
}).startsWith('0.0000')
7184

72-
if (useTinyAmountShorthand && amount.startsWith('0.0000')) {
73-
integralPart = '< 0'
74-
fractionalPart = '0001'
85+
tinyAmount =
86+
useTinyAmountShorthand && amountIsTooSmall
87+
? formatAmountForDisplay({ amount: BigInt(1), amountDecimals: 4, region })
88+
: ''
89+
}
7590
}
7691

77-
const color = props.color ?? (highlight && value !== undefined ? (value < 0 ? 'send' : 'receive') : 'primary')
78-
const fadedColor = fadeDecimals ? 'secondary' : color
79-
8092
return (
81-
<AppText {...props} {...{ color, style }}>
82-
{discreetMode && !showOnDiscreetMode ? (
93+
<AppText {...props} {...{ color, style }} onPress={handleTappedToDisableDiscreetMode}>
94+
{hideAmount ? (
8395
'•••'
84-
) : integralPart ? (
96+
) : amount ? (
8597
<>
8698
{showPlusMinus && (
8799
<AppText {...props} color={color}>
88100
{isNegative ? '-' : '+'}
89101
</AppText>
90102
)}
91103
<AppText {...props} color={color}>
92-
{integralPart}
104+
{tinyAmount ? `< ${tinyAmount}` : amount}
93105
</AppText>
94-
{fractionalPart && <AppText {...props} color={fadedColor}>{`.${fractionalPart}`}</AppText>}
95-
{quantitySymbol && <AppText {...props} color={fadedColor}>{` ${quantitySymbol} `}</AppText>}
96106
{!isUnknownToken && (
97107
<AppText {...props} color={fadeSuffix ? 'secondary' : color}>{` ${suffix || 'ALPH'}`}</AppText>
98108
)}

apps/mobile-wallet/src/components/ConsolidationModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const ConsolidationModal = withModal<ConsolidationModalProps>(({ id, onConsolida
4242
</AppText>
4343
<Fee>
4444
<AppText>{t('Fee')}:</AppText>
45-
<Amount value={fees} fullPrecision fadeDecimals bold />
45+
<Amount value={fees} fullPrecision bold />
4646
</Fee>
4747
</View>
4848
</ScreenSection>

apps/mobile-wallet/src/features/modals/AppModals.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import TokenAmountModal from '~/features/send/modals/TokenAmountModal'
3131
import CurrencySelectModal from '~/features/settings/CurrencySelectModal'
3232
import EditWalletNameModal from '~/features/settings/EditWalletNameModal'
3333
import MnemonicModal from '~/features/settings/MnemonicModal'
34+
import RegionSelectModal from '~/features/settings/regionSettings/RegionSelectModal'
3435
import SafePlaceWarningModal from '~/features/settings/SafePlaceWarningModal'
3536
import WalletDeleteModal from '~/features/settings/WalletDeleteModal'
3637
import TransactionModal from '~/features/transactionsDisplay/TransactionModal'
@@ -130,6 +131,8 @@ const AppModals = () => {
130131
return <DAppQuickActionsModal key={id} id={id} {...params.props} />
131132
case 'DAppDetailsModal':
132133
return <DAppDetailsModal key={id} id={id} {...params.props} />
134+
case 'RegionSelectModal':
135+
return <RegionSelectModal key={id} id={id} />
133136
default:
134137
return null
135138
}

apps/mobile-wallet/src/features/modals/modalTypes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import TokenAmountModal from '~/features/send/modals/TokenAmountModal'
2929
import CurrencySelectModal from '~/features/settings/CurrencySelectModal'
3030
import EditWalletNameModal from '~/features/settings/EditWalletNameModal'
3131
import MnemonicModal from '~/features/settings/MnemonicModal'
32+
import RegionSelectModal from '~/features/settings/regionSettings/RegionSelectModal'
3233
import SafePlaceWarningModal from '~/features/settings/SafePlaceWarningModal'
3334
import WalletDeleteModal from '~/features/settings/WalletDeleteModal'
3435
import TransactionModal from '~/features/transactionsDisplay/TransactionModal'
@@ -76,7 +77,8 @@ export const ModalComponents = {
7677
AddressQRCodeScanActionsModal,
7778
AddressPickerQuickActionsModal,
7879
DAppQuickActionsModal,
79-
DAppDetailsModal
80+
DAppDetailsModal,
81+
RegionSelectModal
8082
}
8183

8284
export type ModalName = keyof typeof ModalComponents
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { FlashList } from '@shopify/flash-list'
2+
import { useTranslation } from 'react-i18next'
3+
4+
import { sendAnalytics } from '~/analytics'
5+
import RadioButtonRow from '~/components/RadioButtonRow'
6+
import BottomModalFlashList from '~/features/modals/BottomModalFlashList'
7+
import { closeModal } from '~/features/modals/modalActions'
8+
import withModal from '~/features/modals/withModal'
9+
import { numberFormatRegionChanged } from '~/features/settings/regionSettings/regionSettingsActions'
10+
import { regionOptions } from '~/features/settings/regionSettings/regionsUtils'
11+
import { useAppDispatch, useAppSelector } from '~/hooks/redux'
12+
13+
const RegionSelectModal = withModal(({ id }) => {
14+
const dispatch = useAppDispatch()
15+
const { t } = useTranslation()
16+
const currentRegion = useAppSelector((s) => s.settings.region)
17+
18+
const handleRegionChange = (region: string) => {
19+
dispatch(numberFormatRegionChanged(region))
20+
dispatch(closeModal({ id }))
21+
sendAnalytics({ event: 'Region changed', props: { region } })
22+
}
23+
24+
return (
25+
<BottomModalFlashList
26+
modalId={id}
27+
title={t('Region')}
28+
flashListRender={(props) => (
29+
<FlashList
30+
data={regionOptions}
31+
estimatedItemSize={65}
32+
renderItem={({ item: regionOption, index }) => (
33+
<RadioButtonRow
34+
key={regionOption.label}
35+
title={regionOption.label}
36+
onPress={() => handleRegionChange(regionOption.value)}
37+
isActive={currentRegion === regionOption.value}
38+
isLast={index === regionOptions.length - 1}
39+
/>
40+
)}
41+
{...props}
42+
/>
43+
)}
44+
/>
45+
)
46+
})
47+
48+
export default RegionSelectModal
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useTranslation } from 'react-i18next'
2+
3+
import AppText from '~/components/AppText'
4+
import Row from '~/components/Row'
5+
import { openModal } from '~/features/modals/modalActions'
6+
import { regionOptions } from '~/features/settings/regionSettings/regionsUtils'
7+
import { useAppDispatch, useAppSelector } from '~/hooks/redux'
8+
9+
const RegionSettingsRow = () => {
10+
const currentRegion = useAppSelector((s) => s.settings.region)
11+
const { t } = useTranslation()
12+
const dispatch = useAppDispatch()
13+
14+
const openRegionSelectModal = () => dispatch(openModal({ name: 'RegionSelectModal' }))
15+
16+
return (
17+
<Row onPress={openRegionSelectModal} title={t('Region')}>
18+
<AppText bold style={{ textAlign: 'right' }}>
19+
{regionOptions.find((region) => region.value === currentRegion)?.label}
20+
</AppText>
21+
</Row>
22+
)
23+
}
24+
25+
export default RegionSettingsRow
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { createAction } from '@reduxjs/toolkit'
2+
3+
import { Settings } from '~/types/settings'
4+
5+
export const numberFormatRegionChanged = createAction<Settings['general']['region']>(
6+
'settings/numberFormatRegionChanged'
7+
)
8+
9+
export const systemRegionMatchSucceeded = createAction<string>('settings/systemRegionMatchSucceeded')
10+
11+
export const systemRegionMatchFailed = createAction('settings/systemRegionMatchFailed')

0 commit comments

Comments
 (0)