Skip to content

Commit

Permalink
Merge pull request #1197 from alephium/region-mw
Browse files Browse the repository at this point in the history
Region settings
  • Loading branch information
nop33 authored Feb 3, 2025
2 parents 3ec04e7 + a189bf6 commit 31fac99
Show file tree
Hide file tree
Showing 25 changed files with 1,801 additions and 40 deletions.
5 changes: 5 additions & 0 deletions .changeset/tall-foxes-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@alephium/mobile-wallet": patch
---

Format amounts based on user's locale
5 changes: 5 additions & 0 deletions .changeset/wild-jobs-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@alephium/mobile-wallet": patch
---

Tap to reveal hidden amounts
2 changes: 1 addition & 1 deletion apps/desktop-wallet/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import useTrackUserSettings from '@/features/analytics/useTrackUserSettings'
import AutoUpdateSnackbar from '@/features/autoUpdate/AutoUpdateSnackbar'
import { languageOptions } from '@/features/localization/languages'
import { systemLanguageMatchFailed, systemLanguageMatchSucceeded } from '@/features/localization/localizationActions'
import useRegionOptions from '@/features/settings/regionSettings/useRegionOptions'
import {
localStorageGeneralSettingsMigrated,
systemRegionMatchFailed,
systemRegionMatchSucceeded
} from '@/features/settings/settingsActions'
import useRegionOptions from '@/features/settings/useRegionOptions'
import { darkTheme, lightTheme } from '@/features/theme/themes'
import { WalletConnectContextProvider } from '@/features/walletConnect/walletConnectContext'
import { useAppDispatch, useAppSelector } from '@/hooks/redux'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const useTrackUserSettings = () => {
const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement)
const fiatCurrency = useAppSelector((s) => s.settings.fiatCurrency)
const network = useAppSelector((s) => s.network.name)
const region = useAppSelector((s) => s.settings.region)

useEffect(() => {
if (posthog.__loaded)
Expand All @@ -27,7 +28,8 @@ const useTrackUserSettings = () => {
language,
passwordRequirement,
fiatCurrency,
network
network,
region
})
}, [
devTools,
Expand All @@ -38,6 +40,7 @@ const useTrackUserSettings = () => {
passwordRequirement,
posthog.__loaded,
posthog.people,
region,
theme,
walletLockTimeInMinutes
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next'
import KeyValueInput from '@/components/Inputs/InlineLabelValueInput'
import Select from '@/components/Inputs/Select'
import useAnalytics from '@/features/analytics/useAnalytics'
import useRegionOptions from '@/features/settings/regionSettings/useRegionOptions'
import { numberFormatRegionChanged } from '@/features/settings/settingsActions'
import useRegionOptions from '@/features/settings/useRegionOptions'
import { useAppDispatch, useAppSelector } from '@/hooks/redux'

const RegionSettings = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { upperFirst } from 'lodash'
import { useMemo } from 'react'

import { Language } from '@/features/localization/languages'
import { useAppSelector } from '@/hooks/redux'

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

export default useRegionOptions

const getRegionsOptions = (languageLocale: string) =>
regionsLocales
.map((regionLocale) => getRegionOption(regionLocale, languageLocale))
.sort((a, b) => a.label.localeCompare(b.label))

// Inspired by https://github.com/LedgerHQ/ledger-live/blob/065dda3/apps/ledger-live-desktop/src/renderer/screens/settings/sections/General/RegionSelect.tsx
const getRegionOption = (regionLocale: string, languageLocale: string | Intl.Locale) => {
const [language, region = ''] = regionLocale.split('-')
Expand All @@ -32,8 +36,3 @@ const getRegionOption = (regionLocale: string, languageLocale: string | Intl.Loc
label
}
}

const getRegionsOptions = (languageLocale: Language) =>
regionsLocales
.map((regionLocale) => getRegionOption(regionLocale, languageLocale))
.sort((a, b) => a.label.localeCompare(b.label))
2 changes: 2 additions & 0 deletions apps/mobile-wallet/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -456,5 +456,7 @@
"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.",
"No dApps match your search: \"{{ searchText }}\"": "No dApps match your search: \"{{ searchText }}\"",
"Visit dApp": "Visit dApp",
"Region": "Region",
"Choose your region to update formats of dates, time, and currencies.": "Choose your region to update formats of dates, time, and currencies.",
"More details on Alph.land": "More details on Alph.land"
}
2 changes: 2 additions & 0 deletions apps/mobile-wallet/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { DefaultTheme, ThemeProvider } from 'styled-components/native'
import ToastAnchor from '~/components/toasts/ToastAnchor'
import LoadingManager from '~/features/loader/LoadingManager'
import { useLocalization } from '~/features/localization/useLocalization'
import { useSystemRegion } from '~/features/settings/regionSettings/useSystemRegion'
import useLoadStoredSettings from '~/features/settings/useLoadStoredSettings'
import { useAppDispatch, useAppSelector } from '~/hooks/redux'
import { useAsyncData } from '~/hooks/useAsyncData'
Expand Down Expand Up @@ -129,6 +130,7 @@ const Main = ({ children, ...props }: ViewProps) => {
useLoadStoredSettings()
useInitializeClient()
useLocalization()
useSystemRegion()

useEffect(() => {
if (walletMetadata) dispatch(appLaunchedWithLastUsedWallet(walletMetadata))
Expand Down
62 changes: 36 additions & 26 deletions apps/mobile-wallet/src/components/Amount.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { convertToPositive, formatAmountForDisplay, formatFiatAmountForDisplay } from '@alephium/shared'
import { convertToPositive, formatAmountForDisplay } from '@alephium/shared'
import { useState } from 'react'
import { StyleProp, TextStyle } from 'react-native'

import { useAppSelector } from '~/hooks/redux'
Expand All @@ -9,7 +10,6 @@ export interface AmountProps extends AppTextProps {
value?: bigint | number
decimals?: number
isFiat?: boolean
fadeDecimals?: boolean
fullPrecision?: boolean
nbOfDecimalsToShow?: number
suffix?: string
Expand All @@ -24,7 +24,6 @@ export interface AmountProps extends AppTextProps {

const Amount = ({
value,
fadeDecimals,
fullPrecision = false,
suffix = '',
showOnDiscreetMode = false,
Expand All @@ -40,59 +39,70 @@ const Amount = ({
...props
}: AmountProps) => {
const discreetMode = useAppSelector((state) => state.settings.discreetMode)
const region = useAppSelector((state) => state.settings.region)
const fiatCurrency = useAppSelector((state) => state.settings.currency)

const [tappedToDisableDiscreetMode, setTappedToDisableDiscreetMode] = useState(false)

const hideAmount = discreetMode && !showOnDiscreetMode && !tappedToDisableDiscreetMode

const handleTappedToDisableDiscreetMode = () => setTappedToDisableDiscreetMode(!tappedToDisableDiscreetMode)

let quantitySymbol = ''
let amount = ''
let tinyAmount = ''
let isNegative = false
const color = props.color ?? (highlight && value !== undefined ? (value < 0 ? 'send' : 'receive') : 'primary')

if (value !== undefined) {
isNegative = value < 0

if (isFiat && typeof value === 'number') {
amount = formatFiatAmountForDisplay(isNegative ? value * -1 : value)
amount = new Intl.NumberFormat(region, { style: 'currency', currency: fiatCurrency }).format(value)

return (
<AppText {...props} {...{ color, style }} onPress={handleTappedToDisableDiscreetMode}>
{hideAmount ? '•••' : amount}
</AppText>
)
} else if (isUnknownToken) {
amount = convertToPositive(value as bigint).toString()
} else {
amount = formatAmountForDisplay({
amount: convertToPositive(value as bigint),
amountDecimals: decimals,
displayDecimals: nbOfDecimalsToShow,
fullPrecision
fullPrecision,
region
})
}

if (fadeDecimals && ['K', 'M', 'B', 'T'].some((char) => amount.endsWith(char))) {
quantitySymbol = amount.slice(-1)
amount = amount.slice(0, -1)
}
}

let [integralPart, fractionalPart] = amount.split('.')
const amountIsTooSmall = formatAmountForDisplay({
amount: convertToPositive(value as bigint),
amountDecimals: decimals,
displayDecimals: nbOfDecimalsToShow,
fullPrecision
}).startsWith('0.0000')

if (useTinyAmountShorthand && amount.startsWith('0.0000')) {
integralPart = '< 0'
fractionalPart = '0001'
tinyAmount =
useTinyAmountShorthand && amountIsTooSmall
? formatAmountForDisplay({ amount: BigInt(1), amountDecimals: 4, region })
: ''
}
}

const color = props.color ?? (highlight && value !== undefined ? (value < 0 ? 'send' : 'receive') : 'primary')
const fadedColor = fadeDecimals ? 'secondary' : color

return (
<AppText {...props} {...{ color, style }}>
{discreetMode && !showOnDiscreetMode ? (
<AppText {...props} {...{ color, style }} onPress={handleTappedToDisableDiscreetMode}>
{hideAmount ? (
'•••'
) : integralPart ? (
) : amount ? (
<>
{showPlusMinus && (
<AppText {...props} color={color}>
{isNegative ? '-' : '+'}
</AppText>
)}
<AppText {...props} color={color}>
{integralPart}
{tinyAmount ? `< ${tinyAmount}` : amount}
</AppText>
{fractionalPart && <AppText {...props} color={fadedColor}>{`.${fractionalPart}`}</AppText>}
{quantitySymbol && <AppText {...props} color={fadedColor}>{` ${quantitySymbol} `}</AppText>}
{!isUnknownToken && (
<AppText {...props} color={fadeSuffix ? 'secondary' : color}>{` ${suffix || 'ALPH'}`}</AppText>
)}
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile-wallet/src/components/ConsolidationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const ConsolidationModal = withModal<ConsolidationModalProps>(({ id, onConsolida
</AppText>
<Fee>
<AppText>{t('Fee')}:</AppText>
<Amount value={fees} fullPrecision fadeDecimals bold />
<Amount value={fees} fullPrecision bold />
</Fee>
</View>
</ScreenSection>
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile-wallet/src/features/modals/AppModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import TokenAmountModal from '~/features/send/modals/TokenAmountModal'
import CurrencySelectModal from '~/features/settings/CurrencySelectModal'
import EditWalletNameModal from '~/features/settings/EditWalletNameModal'
import MnemonicModal from '~/features/settings/MnemonicModal'
import RegionSelectModal from '~/features/settings/regionSettings/RegionSelectModal'
import SafePlaceWarningModal from '~/features/settings/SafePlaceWarningModal'
import WalletDeleteModal from '~/features/settings/WalletDeleteModal'
import TransactionModal from '~/features/transactionsDisplay/TransactionModal'
Expand Down Expand Up @@ -130,6 +131,8 @@ const AppModals = () => {
return <DAppQuickActionsModal key={id} id={id} {...params.props} />
case 'DAppDetailsModal':
return <DAppDetailsModal key={id} id={id} {...params.props} />
case 'RegionSelectModal':
return <RegionSelectModal key={id} id={id} />
default:
return null
}
Expand Down
4 changes: 3 additions & 1 deletion apps/mobile-wallet/src/features/modals/modalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import TokenAmountModal from '~/features/send/modals/TokenAmountModal'
import CurrencySelectModal from '~/features/settings/CurrencySelectModal'
import EditWalletNameModal from '~/features/settings/EditWalletNameModal'
import MnemonicModal from '~/features/settings/MnemonicModal'
import RegionSelectModal from '~/features/settings/regionSettings/RegionSelectModal'
import SafePlaceWarningModal from '~/features/settings/SafePlaceWarningModal'
import WalletDeleteModal from '~/features/settings/WalletDeleteModal'
import TransactionModal from '~/features/transactionsDisplay/TransactionModal'
Expand Down Expand Up @@ -76,7 +77,8 @@ export const ModalComponents = {
AddressQRCodeScanActionsModal,
AddressPickerQuickActionsModal,
DAppQuickActionsModal,
DAppDetailsModal
DAppDetailsModal,
RegionSelectModal
}

export type ModalName = keyof typeof ModalComponents
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { FlashList } from '@shopify/flash-list'
import { useTranslation } from 'react-i18next'

import { sendAnalytics } from '~/analytics'
import RadioButtonRow from '~/components/RadioButtonRow'
import BottomModalFlashList from '~/features/modals/BottomModalFlashList'
import { closeModal } from '~/features/modals/modalActions'
import withModal from '~/features/modals/withModal'
import { numberFormatRegionChanged } from '~/features/settings/regionSettings/regionSettingsActions'
import { regionOptions } from '~/features/settings/regionSettings/regionsUtils'
import { useAppDispatch, useAppSelector } from '~/hooks/redux'

const RegionSelectModal = withModal(({ id }) => {
const dispatch = useAppDispatch()
const { t } = useTranslation()
const currentRegion = useAppSelector((s) => s.settings.region)

const handleRegionChange = (region: string) => {
dispatch(numberFormatRegionChanged(region))
dispatch(closeModal({ id }))
sendAnalytics({ event: 'Region changed', props: { region } })
}

return (
<BottomModalFlashList
modalId={id}
title={t('Region')}
flashListRender={(props) => (
<FlashList
data={regionOptions}
estimatedItemSize={65}
renderItem={({ item: regionOption, index }) => (
<RadioButtonRow
key={regionOption.label}
title={regionOption.label}
onPress={() => handleRegionChange(regionOption.value)}
isActive={currentRegion === regionOption.value}
isLast={index === regionOptions.length - 1}
/>
)}
{...props}
/>
)}
/>
)
})

export default RegionSelectModal
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useTranslation } from 'react-i18next'

import AppText from '~/components/AppText'
import Row from '~/components/Row'
import { openModal } from '~/features/modals/modalActions'
import { regionOptions } from '~/features/settings/regionSettings/regionsUtils'
import { useAppDispatch, useAppSelector } from '~/hooks/redux'

const RegionSettingsRow = () => {
const currentRegion = useAppSelector((s) => s.settings.region)
const { t } = useTranslation()
const dispatch = useAppDispatch()

const openRegionSelectModal = () => dispatch(openModal({ name: 'RegionSelectModal' }))

return (
<Row onPress={openRegionSelectModal} title={t('Region')}>
<AppText bold style={{ textAlign: 'right' }}>
{regionOptions.find((region) => region.value === currentRegion)?.label}
</AppText>
</Row>
)
}

export default RegionSettingsRow
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createAction } from '@reduxjs/toolkit'

import { Settings } from '~/types/settings'

export const numberFormatRegionChanged = createAction<Settings['general']['region']>(
'settings/numberFormatRegionChanged'
)

export const systemRegionMatchSucceeded = createAction<string>('settings/systemRegionMatchSucceeded')

export const systemRegionMatchFailed = createAction('settings/systemRegionMatchFailed')
Loading

0 comments on commit 31fac99

Please sign in to comment.