From 736e395cd71ea8bda3844b15089f145a752bf2a4 Mon Sep 17 00:00:00 2001 From: Jack Short Date: Mon, 11 Sep 2023 13:13:49 -0400 Subject: [PATCH] chore: displaying local currency and language formatting (#7267) * chore: displaying local currency and language formatting * moving to hook * moving it to useformatter hook * moving hook to bottom of file --- .../AccountDrawer/AuthenticatedHeader.tsx | 3 +- .../MiniPortfolio/Pools/index.tsx | 4 +- .../MiniPortfolio/Tokens/index.tsx | 9 +++- .../CurrencyInputPanel/FiatValue.tsx | 4 +- .../Tokens/TokenDetails/StatsSection.tsx | 4 +- src/components/Tokens/TokenTable/TokenRow.tsx | 4 +- src/components/swap/AdvancedSwapDetails.tsx | 3 +- src/components/swap/GasBreakdownTooltip.tsx | 4 +- src/components/swap/GasEstimateTooltip.tsx | 3 +- src/components/swap/SwapModalFooter.tsx | 3 +- src/components/swap/SwapModalHeaderAmount.tsx | 4 +- src/components/swap/TradePrice.tsx | 4 +- src/utils/formatNumbers.ts | 43 +++++++++++++++++++ 13 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/components/AccountDrawer/AuthenticatedHeader.tsx b/src/components/AccountDrawer/AuthenticatedHeader.tsx index 3d43b307d7..39b93c42bf 100644 --- a/src/components/AccountDrawer/AuthenticatedHeader.tsx +++ b/src/components/AccountDrawer/AuthenticatedHeader.tsx @@ -25,7 +25,7 @@ import { updateSelectedWallet } from 'state/user/reducer' import styled from 'styled-components' import { CopyHelper, ExternalLink, ThemedText } from 'theme' import { shortenAddress } from 'utils' -import { formatNumber, NumberType } from 'utils/formatNumbers' +import { NumberType, useFormatter } from 'utils/formatNumbers' import { useCloseModal, useFiatOnrampAvailability, useOpenModal, useToggleModal } from '../../state/application/hooks' import { ApplicationModal } from '../../state/application/reducer' @@ -159,6 +159,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account const resetSellAssets = useSellAsset((state) => state.reset) const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters) const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable) + const { formatNumber } = useFormatter() const shouldDisableNFTRoutes = useDisableNFTRoutes() diff --git a/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx b/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx index 414b1b165c..5830b2c896 100644 --- a/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx +++ b/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx @@ -13,7 +13,7 @@ import { useCallback, useMemo, useReducer } from 'react' import { useNavigate } from 'react-router-dom' import styled from 'styled-components' import { ThemedText } from 'theme' -import { formatNumber, NumberType } from 'utils/formatNumbers' +import { NumberType, useFormatter } from 'utils/formatNumbers' import { ExpandoRow } from '../ExpandoRow' import { PortfolioLogo } from '../PortfolioLogo' @@ -118,6 +118,8 @@ function calculcateLiquidityValue(price0: number | undefined, price1: number | u } function PositionListItem({ positionInfo }: { positionInfo: PositionInfo }) { + const { formatNumber } = useFormatter() + const { chainId, position, pool, details, inRange, closed } = positionInfo const { priceA, priceB, fees: feeValue } = useFeeValues(positionInfo) diff --git a/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx b/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx index 4233e72a20..abd7a23894 100644 --- a/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx +++ b/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx @@ -11,7 +11,7 @@ import { useCallback, useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' import styled from 'styled-components' import { EllipsisStyle, ThemedText } from 'theme' -import { formatNumber, NumberType } from 'utils/formatNumbers' +import { NumberType, useFormatter } from 'utils/formatNumbers' import { splitHiddenTokens } from 'utils/splitHiddenTokens' import { useToggleAccountDrawer } from '../..' @@ -79,6 +79,7 @@ function TokenRow({ token, quantity, denominatedValue, tokenProjectMarket }: Tok navigate(getTokenDetailsURL(token)) toggleWalletDrawer() }, [navigate, token, toggleWalletDrawer]) + const { formatNumber } = useFormatter() const currency = gqlToCurrency(token) if (!currency) { @@ -100,7 +101,11 @@ function TokenRow({ token, quantity, denominatedValue, tokenProjectMarket }: Tok title={{token?.name}} descriptor={ - {formatNumber({ input: quantity, type: NumberType.TokenNonTx })} {token?.symbol} + {formatNumber({ + input: quantity, + type: NumberType.TokenNonTx, + })}{' '} + {token?.symbol} } onClick={navigateToTokenDetails} diff --git a/src/components/CurrencyInputPanel/FiatValue.tsx b/src/components/CurrencyInputPanel/FiatValue.tsx index 1629389203..0e7625e162 100644 --- a/src/components/CurrencyInputPanel/FiatValue.tsx +++ b/src/components/CurrencyInputPanel/FiatValue.tsx @@ -6,7 +6,7 @@ import { MouseoverTooltip } from 'components/Tooltip' import { useMemo } from 'react' import styled from 'styled-components' import { ThemedText } from 'theme' -import { formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers' +import { formatPriceImpact, NumberType, useFormatter } from 'utils/formatNumbers' import { warningSeverity } from 'utils/prices' const FiatLoadingBubble = styled(LoadingBubble)` @@ -22,6 +22,8 @@ export function FiatValue({ fiatValue: { data?: number; isLoading: boolean } priceImpact?: Percent }) { + const { formatNumber } = useFormatter() + const priceImpactColor = useMemo(() => { if (!priceImpact) return undefined if (priceImpact.lessThan('0')) return 'success' diff --git a/src/components/Tokens/TokenDetails/StatsSection.tsx b/src/components/Tokens/TokenDetails/StatsSection.tsx index 35f2f0f4e0..728f841949 100644 --- a/src/components/Tokens/TokenDetails/StatsSection.tsx +++ b/src/components/Tokens/TokenDetails/StatsSection.tsx @@ -6,7 +6,7 @@ import { ReactNode } from 'react' import styled from 'styled-components' import { ExternalLink, ThemedText } from 'theme' import { textFadeIn } from 'theme/styles' -import { formatNumber, NumberType } from 'utils/formatNumbers' +import { NumberType, useFormatter } from 'utils/formatNumbers' import { UNSUPPORTED_METADATA_CHAINS } from '../constants' import { TokenSortMethod } from '../state' @@ -59,6 +59,8 @@ function Stat({ title: ReactNode description?: ReactNode }) { + const { formatNumber } = useFormatter() + return ( {title} diff --git a/src/components/Tokens/TokenTable/TokenRow.tsx b/src/components/Tokens/TokenTable/TokenRow.tsx index 6fb0179c72..f8e7a3ae60 100644 --- a/src/components/Tokens/TokenTable/TokenRow.tsx +++ b/src/components/Tokens/TokenTable/TokenRow.tsx @@ -16,7 +16,7 @@ import { CSSProperties, ReactNode } from 'react' import { Link, useParams } from 'react-router-dom' import styled, { css, useTheme } from 'styled-components' import { BREAKPOINTS, ClickableStyle } from 'theme' -import { formatNumber, formatUSDPrice, NumberType } from 'utils/formatNumbers' +import { formatUSDPrice, NumberType, useFormatter } from 'utils/formatNumbers' import { LARGE_MEDIA_BREAKPOINT, @@ -440,6 +440,8 @@ interface LoadedRowProps { /* Loaded State: row component with token information */ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef) => { + const { formatNumber } = useFormatter() + const { tokenListIndex, tokenListLength, token, sortRank } = props const filterString = useAtomValue(filterStringAtom) diff --git a/src/components/swap/AdvancedSwapDetails.tsx b/src/components/swap/AdvancedSwapDetails.tsx index d0eb021b64..2b9867315b 100644 --- a/src/components/swap/AdvancedSwapDetails.tsx +++ b/src/components/swap/AdvancedSwapDetails.tsx @@ -9,7 +9,7 @@ import { ZERO_PERCENT } from 'constants/misc' import useNativeCurrency from 'lib/hooks/useNativeCurrency' import { ClassicTrade, InterfaceTrade } from 'state/routing/types' import { getTransactionCount, isClassicTrade } from 'state/routing/utils' -import { formatCurrencyAmount, formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers' +import { formatCurrencyAmount, formatPriceImpact, NumberType, useFormatter } from 'utils/formatNumbers' import { ExternalLink, Separator, ThemedText } from '../../theme' import Column from '../Column' @@ -47,6 +47,7 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }: const { chainId } = useWeb3React() const nativeCurrency = useNativeCurrency(chainId) const txCount = getTransactionCount(trade) + const { formatNumber } = useFormatter() const supportsGasEstimate = chainId && SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) diff --git a/src/components/swap/GasBreakdownTooltip.tsx b/src/components/swap/GasBreakdownTooltip.tsx index 611c4b660a..0cad40a6b7 100644 --- a/src/components/swap/GasBreakdownTooltip.tsx +++ b/src/components/swap/GasBreakdownTooltip.tsx @@ -7,7 +7,7 @@ import { InterfaceTrade } from 'state/routing/types' import { isClassicTrade, isUniswapXTrade } from 'state/routing/utils' import styled from 'styled-components' import { Divider, ExternalLink, ThemedText } from 'theme' -import { formatNumber, NumberType } from 'utils/formatNumbers' +import { NumberType, useFormatter } from 'utils/formatNumbers' const Container = styled(AutoColumn)` padding: 4px; @@ -35,6 +35,8 @@ const GasCostItem = ({ itemValue?: React.ReactNode amount?: number }) => { + const { formatNumber } = useFormatter() + return ( {title} diff --git a/src/components/swap/GasEstimateTooltip.tsx b/src/components/swap/GasEstimateTooltip.tsx index 0dad275e1b..afb456e2c6 100644 --- a/src/components/swap/GasEstimateTooltip.tsx +++ b/src/components/swap/GasEstimateTooltip.tsx @@ -11,7 +11,7 @@ import { InterfaceTrade } from 'state/routing/types' import { isUniswapXTrade } from 'state/routing/utils' import styled from 'styled-components' import { ThemedText } from 'theme' -import { formatNumber, NumberType } from 'utils/formatNumbers' +import { NumberType, useFormatter } from 'utils/formatNumbers' import { GasBreakdownTooltip } from './GasBreakdownTooltip' @@ -26,6 +26,7 @@ const StyledGasIcon = styled(Gas)` export default function GasEstimateTooltip({ trade, loading }: { trade?: InterfaceTrade; loading: boolean }) { const { chainId } = useWeb3React() + const { formatNumber } = useFormatter() if (!trade || !chainId || !SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId)) { return null diff --git a/src/components/swap/SwapModalFooter.tsx b/src/components/swap/SwapModalFooter.tsx index d30419e59d..684e80899b 100644 --- a/src/components/swap/SwapModalFooter.tsx +++ b/src/components/swap/SwapModalFooter.tsx @@ -16,7 +16,7 @@ import { getTransactionCount, isClassicTrade } from 'state/routing/utils' import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks' import styled, { DefaultTheme, useTheme } from 'styled-components' import { ExternalLink, ThemedText } from 'theme' -import { formatNumber, formatPriceImpact, NumberType } from 'utils/formatNumbers' +import { formatPriceImpact, NumberType, useFormatter } from 'utils/formatNumbers' import { formatTransactionAmount, priceToPreciseFloat } from 'utils/formatNumbers' import getRoutingDiagramEntries from 'utils/getRoutingDiagramEntries' import { formatSwapButtonClickEventProperties } from 'utils/loggingFormatters' @@ -78,6 +78,7 @@ export default function SwapModalFooter({ const theme = useTheme() const { chainId } = useWeb3React() const nativeCurrency = useNativeCurrency(chainId) + const { formatNumber } = useFormatter() const label = `${trade.executionPrice.baseCurrency?.symbol} ` const labelInverted = `${trade.executionPrice.quoteCurrency?.symbol}` diff --git a/src/components/swap/SwapModalHeaderAmount.tsx b/src/components/swap/SwapModalHeaderAmount.tsx index 304dfc9c69..29882e4402 100644 --- a/src/components/swap/SwapModalHeaderAmount.tsx +++ b/src/components/swap/SwapModalHeaderAmount.tsx @@ -9,7 +9,7 @@ import { TextProps } from 'rebass' import { Field } from 'state/swap/actions' import styled from 'styled-components' import { BREAKPOINTS, ThemedText } from 'theme' -import { formatNumber, NumberType } from 'utils/formatNumbers' +import { NumberType, useFormatter } from 'utils/formatNumbers' import { formatReviewSwapCurrencyAmount } from 'utils/formatNumbers' export const Label = styled(ThemedText.BodySmall)<{ cursor?: string }>` @@ -45,6 +45,8 @@ interface AmountProps { } export function SwapModalHeaderAmount({ tooltipText, label, amount, usdAmount, field, currency }: AmountProps) { + const { formatNumber } = useFormatter() + return ( diff --git a/src/components/swap/TradePrice.tsx b/src/components/swap/TradePrice.tsx index 2e5078e2ef..1a3afb603f 100644 --- a/src/components/swap/TradePrice.tsx +++ b/src/components/swap/TradePrice.tsx @@ -5,7 +5,7 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { useCallback, useMemo, useState } from 'react' import styled from 'styled-components' import { ThemedText } from 'theme' -import { formatNumber, formatPrice, NumberType } from 'utils/formatNumbers' +import { formatPrice, NumberType, useFormatter } from 'utils/formatNumbers' interface TradePriceProps { price: Price @@ -28,6 +28,8 @@ const StyledPriceContainer = styled.button` ` export default function TradePrice({ price }: TradePriceProps) { + const { formatNumber } = useFormatter() + const [showInverted, setShowInverted] = useState(false) const { baseCurrency, quoteCurrency } = price diff --git a/src/utils/formatNumbers.ts b/src/utils/formatNumbers.ts index 19679bfdb1..7ab804029a 100644 --- a/src/utils/formatNumbers.ts +++ b/src/utils/formatNumbers.ts @@ -5,6 +5,10 @@ import { SupportedLocalCurrency, } from 'constants/localCurrencies' import { DEFAULT_LOCALE, SupportedLocale } from 'constants/locales' +import { useCurrencyConversionFlagEnabled } from 'featureFlags/flags/currencyConversion' +import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency' +import { useActiveLocale } from 'hooks/useActiveLocale' +import { useCallback, useMemo } from 'react' type Nullish = T | null | undefined type NumberFormatOptions = Intl.NumberFormatOptions @@ -525,3 +529,42 @@ export function formatReviewSwapCurrencyAmount(amount: CurrencyAmount) } return formattedAmount } + +function useFormatterLocales(): { + formatterLocale: SupportedLocale + formatterLocalCurrency: SupportedLocalCurrency +} { + const currencyConversionEnabled = useCurrencyConversionFlagEnabled() + const activeLocale = useActiveLocale() + const activeLocalCurrency = useActiveLocalCurrency() + + if (currencyConversionEnabled) { + return { + formatterLocale: activeLocale, + formatterLocalCurrency: activeLocalCurrency, + } + } + + return { + formatterLocale: DEFAULT_LOCALE, + formatterLocalCurrency: DEFAULT_LOCAL_CURRENCY, + } +} + +// Constructs an object that injects the correct locale and local currency into each of the above formatter functions. +export function useFormatter() { + const { formatterLocale, formatterLocalCurrency } = useFormatterLocales() + + const formatNumberWithLocales = useCallback( + (options: Omit) => + formatNumber({ ...options, locale: formatterLocale, localCurrency: formatterLocalCurrency }), + [formatterLocalCurrency, formatterLocale] + ) + + return useMemo( + () => ({ + formatNumber: formatNumberWithLocales, + }), + [formatNumberWithLocales] + ) +}