diff --git a/cypress/e2e/wallet-dropdown.test.ts b/cypress/e2e/wallet-dropdown.test.ts
index 46246eab48..2378d3889d 100644
--- a/cypress/e2e/wallet-dropdown.test.ts
+++ b/cypress/e2e/wallet-dropdown.test.ts
@@ -144,4 +144,32 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('wallet-settings')).should('not.be.visible')
})
})
+
+ describe('local currency', () => {
+ it('loads local currency from the query param', () => {
+ cy.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
+ cy.get(getTestSelector('web3-status-connected')).click()
+ cy.get(getTestSelector('wallet-settings')).click()
+ cy.contains('USD')
+
+ cy.visit('/?cur=AUD', { featureFlags: [FeatureFlag.currencyConversion] })
+ cy.contains('AUD')
+ })
+
+ it('loads local currency from menu', () => {
+ cy.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
+ cy.get(getTestSelector('web3-status-connected')).click()
+ cy.get(getTestSelector('wallet-settings')).click()
+ cy.contains('USD')
+
+ cy.get(getTestSelector('local-currency-settings-button')).click()
+ cy.get(getTestSelector('wallet-local-currency-item')).contains('AUD').click({ force: true })
+ cy.location('hash').should('match', /\?cur=AUD$/)
+ cy.contains('AUD')
+
+ cy.get(getTestSelector('wallet-local-currency-item')).contains('USD').click({ force: true })
+ cy.location('hash').should('match', /\?cur=USD$/)
+ cy.contains('USD')
+ })
+ })
})
diff --git a/src/components/AccountDrawer/DefaultMenu.tsx b/src/components/AccountDrawer/DefaultMenu.tsx
index ed7615f693..f3e2605387 100644
--- a/src/components/AccountDrawer/DefaultMenu.tsx
+++ b/src/components/AccountDrawer/DefaultMenu.tsx
@@ -1,11 +1,12 @@
import { useWeb3React } from '@web3-react/core'
import Column from 'components/Column'
import WalletModal from 'components/WalletModal'
-import { useCallback, useEffect, useState } from 'react'
+import { useCallback, useEffect, useMemo, useState } from 'react'
import styled from 'styled-components'
import AuthenticatedHeader from './AuthenticatedHeader'
import LanguageMenu from './LanguageMenu'
+import LocalCurrencyMenu from './LocalCurrencyMenu'
import SettingsMenu from './SettingsMenu'
const DefaultMenuWrap = styled(Column)`
@@ -17,6 +18,7 @@ enum MenuState {
DEFAULT,
SETTINGS,
LANGUAGE_SETTINGS,
+ LOCAL_CURRENCY_SETTINGS,
}
function DefaultMenu({ drawerOpen }: { drawerOpen: boolean }) {
@@ -27,6 +29,7 @@ function DefaultMenu({ drawerOpen }: { drawerOpen: boolean }) {
const openSettings = useCallback(() => setMenu(MenuState.SETTINGS), [])
const closeSettings = useCallback(() => setMenu(MenuState.DEFAULT), [])
const openLanguageSettings = useCallback(() => setMenu(MenuState.LANGUAGE_SETTINGS), [])
+ const openLocalCurrencySettings = useCallback(() => setMenu(MenuState.LOCAL_CURRENCY_SETTINGS), [])
useEffect(() => {
if (!drawerOpen && menu !== MenuState.DEFAULT) {
@@ -39,20 +42,30 @@ function DefaultMenu({ drawerOpen }: { drawerOpen: boolean }) {
return
}, [drawerOpen, menu, closeSettings])
- return (
-
- {menu === MenuState.DEFAULT &&
- (isAuthenticated ? (
+ const SubMenu = useMemo(() => {
+ switch (menu) {
+ case MenuState.DEFAULT:
+ return isAuthenticated ? (
) : (
- ))}
- {menu === MenuState.SETTINGS && (
-
- )}
- {menu === MenuState.LANGUAGE_SETTINGS && }
-
- )
+ )
+ case MenuState.SETTINGS:
+ return (
+
+ )
+ case MenuState.LANGUAGE_SETTINGS:
+ return
+ case MenuState.LOCAL_CURRENCY_SETTINGS:
+ return
+ }
+ }, [account, closeSettings, isAuthenticated, menu, openLanguageSettings, openLocalCurrencySettings, openSettings])
+
+ return {SubMenu}
}
export default DefaultMenu
diff --git a/src/components/AccountDrawer/LanguageMenu.tsx b/src/components/AccountDrawer/LanguageMenu.tsx
index f7aa133467..8723dcccb3 100644
--- a/src/components/AccountDrawer/LanguageMenu.tsx
+++ b/src/components/AccountDrawer/LanguageMenu.tsx
@@ -2,36 +2,21 @@ import { Trans } from '@lingui/macro'
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
import { useActiveLocale } from 'hooks/useActiveLocale'
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
-import { Check } from 'react-feather'
-import { Link } from 'react-router-dom'
-import styled, { useTheme } from 'styled-components'
-import { ClickableStyle, ThemedText } from 'theme'
+import { MenuColumn, MenuItem } from './shared'
import { SlideOutMenu } from './SlideOutMenu'
-const InternalLinkMenuItem = styled(Link)`
- ${ClickableStyle}
- flex: 1;
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 12px 0;
- justify-content: space-between;
- text-decoration: none;
- color: ${({ theme }) => theme.neutral1};
-`
-
function LanguageMenuItem({ locale, isActive }: { locale: SupportedLocale; isActive: boolean }) {
const { to, onClick } = useLocationLinkProps(locale)
- const theme = useTheme()
-
- if (!to) return null
return (
-
- {LOCALE_LABEL[locale]}
- {isActive && }
-
+
)
}
@@ -50,7 +35,9 @@ export function LanguageMenuItems() {
export default function LanguageMenu({ onClose }: { onClose: () => void }) {
return (
Language} onClose={onClose}>
-
+
+
+
)
}
diff --git a/src/components/AccountDrawer/LocalCurrencyMenu.tsx b/src/components/AccountDrawer/LocalCurrencyMenu.tsx
new file mode 100644
index 0000000000..1d5de9620d
--- /dev/null
+++ b/src/components/AccountDrawer/LocalCurrencyMenu.tsx
@@ -0,0 +1,61 @@
+import { Trans } from '@lingui/macro'
+import { getLocalCurrencyIcon, SUPPORTED_LOCAL_CURRENCIES, SupportedLocalCurrency } from 'constants/localCurrencies'
+import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency'
+import { useLocalCurrencyLinkProps } from 'hooks/useLocalCurrencyLinkProps'
+import { useMemo } from 'react'
+import styled from 'styled-components'
+
+import { MenuColumn, MenuItem } from './shared'
+import { SlideOutMenu } from './SlideOutMenu'
+
+const StyledLocalCurrencyIcon = styled.div`
+ width: 20px;
+ height: 20px;
+ border-radius: 100%;
+ overflow: hidden;
+`
+
+function LocalCurrencyMenuItem({
+ localCurrency,
+ isActive,
+}: {
+ localCurrency: SupportedLocalCurrency
+ isActive: boolean
+}) {
+ const { to, onClick } = useLocalCurrencyLinkProps(localCurrency)
+
+ const LocalCurrencyIcon = useMemo(() => {
+ return {getLocalCurrencyIcon(localCurrency)}
+ }, [localCurrency])
+
+ if (!to) return null
+
+ return (
+
+ )
+}
+
+export default function LocalCurrencyMenu({ onClose }: { onClose: () => void }) {
+ const activeLocalCurrency = useActiveLocalCurrency()
+
+ return (
+ Currency} onClose={onClose}>
+
+ {SUPPORTED_LOCAL_CURRENCIES.map((localCurrency) => (
+
+ ))}
+
+
+ )
+}
diff --git a/src/components/AccountDrawer/SettingsMenu.tsx b/src/components/AccountDrawer/SettingsMenu.tsx
index c706705e22..a804c958c0 100644
--- a/src/components/AccountDrawer/SettingsMenu.tsx
+++ b/src/components/AccountDrawer/SettingsMenu.tsx
@@ -3,6 +3,7 @@ import Column from 'components/Column'
import Row from 'components/Row'
import { LOCALE_LABEL } from 'constants/locales'
import { useCurrencyConversionFlagEnabled } from 'featureFlags/flags/currencyConversion'
+import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency'
import { useActiveLocale } from 'hooks/useActiveLocale'
import { ReactNode } from 'react'
import { ChevronRight } from 'react-feather'
@@ -27,15 +28,16 @@ const SectionTitle = styled(ThemedText.SubHeader)`
padding-bottom: 24px;
`
-const ToggleWrapper = styled.div`
+const ToggleWrapper = styled.div<{ currencyConversionEnabled?: boolean }>`
display: flex;
flex-direction: column;
gap: 16px;
- margin-bottom: 24px;
+ margin-bottom: ${({ currencyConversionEnabled }) => (currencyConversionEnabled ? '10px' : '24px')};
`
const SettingsButtonWrapper = styled(Row)`
${ClickableStyle}
+ padding: 16px 0px;
`
const StyledChevron = styled(ChevronRight)`
@@ -60,7 +62,7 @@ const SettingsButton = ({
{title}
- {currentState}
+ {currentState}
@@ -69,12 +71,15 @@ const SettingsButton = ({
export default function SettingsMenu({
onClose,
openLanguageSettings,
+ openLocalCurrencySettings,
}: {
onClose: () => void
openLanguageSettings: () => void
+ openLocalCurrencySettings: () => void
}) {
const currencyConversionEnabled = useCurrencyConversionFlagEnabled()
const activeLocale = useActiveLocale()
+ const activeLocalCurrency = useActiveLocalCurrency()
return (
Settings} onClose={onClose}>
@@ -83,7 +88,7 @@ export default function SettingsMenu({
Preferences
-
+
@@ -99,12 +104,20 @@ export default function SettingsMenu({
)}
{currencyConversionEnabled && (
- Language}
- currentState={LOCALE_LABEL[activeLocale]}
- onClick={openLanguageSettings}
- testId="language-settings-button"
- />
+
+ Language}
+ currentState={LOCALE_LABEL[activeLocale]}
+ onClick={openLanguageSettings}
+ testId="language-settings-button"
+ />
+ Currency}
+ currentState={activeLocalCurrency}
+ onClick={openLocalCurrencySettings}
+ testId="local-currency-settings-button"
+ />
+
)}
diff --git a/src/components/AccountDrawer/shared.tsx b/src/components/AccountDrawer/shared.tsx
new file mode 100644
index 0000000000..5ec69265f0
--- /dev/null
+++ b/src/components/AccountDrawer/shared.tsx
@@ -0,0 +1,57 @@
+import Column from 'components/Column'
+import Row from 'components/Row'
+import { ReactNode } from 'react'
+import { Check } from 'react-feather'
+import type { To } from 'react-router-dom'
+import { Link } from 'react-router-dom'
+import styled, { useTheme } from 'styled-components'
+import { BREAKPOINTS, ClickableStyle, ThemedText } from 'theme'
+
+const InternalLinkMenuItem = styled(Link)`
+ ${ClickableStyle}
+
+ flex: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 12px 0;
+ justify-content: space-between;
+ text-decoration: none;
+ color: ${({ theme }) => theme.neutral1};
+`
+
+export const MenuColumn = styled(Column)`
+ @media screen and (max-width: ${BREAKPOINTS.sm}px) {
+ padding-bottom: 14px;
+ }
+`
+
+export function MenuItem({
+ label,
+ logo,
+ to,
+ onClick,
+ isActive,
+ testId,
+}: {
+ label: ReactNode
+ logo?: ReactNode
+ to?: To
+ onClick?: () => void
+ isActive: boolean
+ testId?: string
+}) {
+ const theme = useTheme()
+
+ if (!to) return null
+
+ return (
+
+
+ {logo && logo}
+ {label}
+
+ {isActive && }
+
+ )
+}
diff --git a/src/constants/localCurrencies.tsx b/src/constants/localCurrencies.tsx
new file mode 100644
index 0000000000..63f2215a3c
--- /dev/null
+++ b/src/constants/localCurrencies.tsx
@@ -0,0 +1,90 @@
+import { ReactNode } from 'react'
+
+import {
+ AUD_ICON,
+ BRL_ICON,
+ CAD_ICON,
+ EUR_ICON,
+ GBP_ICON,
+ HKD_ICON,
+ IDR_ICON,
+ INR_ICON,
+ JPY_ICON,
+ NGN_ICON,
+ PKR_ICON,
+ RUB_ICON,
+ SGD_ICON,
+ THB_ICON,
+ TRY_ICON,
+ UAH_ICON,
+ USD_ICON,
+ VND_ICON,
+} from './localCurrencyIcons'
+
+export const SUPPORTED_LOCAL_CURRENCIES = [
+ 'USD',
+ 'AUD',
+ 'BRL',
+ 'CAD',
+ 'EUR',
+ 'GBP',
+ 'HKD',
+ 'IDR',
+ 'INR',
+ 'JPY',
+ 'NGN',
+ 'PKR',
+ 'RUB',
+ 'SGD',
+ 'THB',
+ 'TRY',
+ 'UAH',
+ 'VND',
+]
+
+export type SupportedLocalCurrency = (typeof SUPPORTED_LOCAL_CURRENCIES)[number]
+
+export const DEFAULT_LOCAL_CURRENCY: SupportedLocalCurrency = 'USD'
+
+export function getLocalCurrencyIcon(localCurrency: SupportedLocalCurrency, size = 20): ReactNode {
+ switch (localCurrency) {
+ case 'USD':
+ return
+ case 'EUR':
+ return
+ case 'RUB':
+ return
+ case 'INR':
+ return
+ case 'GBP':
+ return
+ case 'JPY':
+ return
+ case 'VND':
+ return
+ case 'SGD':
+ return
+ case 'BRL':
+ return
+ case 'HKD':
+ return
+ case 'CAD':
+ return
+ case 'IDR':
+ return
+ case 'TRY':
+ return
+ case 'NGN':
+ return
+ case 'AUD':
+ return
+ case 'PKR':
+ return
+ case 'UAH':
+ return
+ case 'THB':
+ return
+ default:
+ return null
+ }
+}
diff --git a/src/constants/localCurrencyIcons.tsx b/src/constants/localCurrencyIcons.tsx
new file mode 100644
index 0000000000..d8efefef02
--- /dev/null
+++ b/src/constants/localCurrencyIcons.tsx
@@ -0,0 +1,721 @@
+type SVGProps = React.SVGProps
+
+export const USD_ICON = (props: SVGProps) => (
+
+)
+
+export const EUR_ICON = (props: SVGProps) => (
+
+)
+
+export const RUB_ICON = (props: SVGProps) => (
+
+)
+
+export const INR_ICON = (props: SVGProps) => (
+
+)
+
+export const GBP_ICON = (props: SVGProps) => (
+
+)
+
+export const JPY_ICON = (props: SVGProps) => (
+
+)
+
+export const VND_ICON = (props: SVGProps) => (
+
+)
+
+export const SGD_ICON = (props: SVGProps) => (
+
+)
+
+export const BRL_ICON = (props: SVGProps) => (
+
+)
+
+export const HKD_ICON = (props: SVGProps) => (
+
+)
+
+export const CAD_ICON = (props: SVGProps) => (
+
+)
+
+export const IDR_ICON = (props: SVGProps) => (
+
+)
+
+export const TRY_ICON = (props: SVGProps) => (
+
+)
+
+export const NGN_ICON = (props: SVGProps) => (
+
+)
+
+export const AUD_ICON = (props: SVGProps) => (
+
+)
+
+export const PKR_ICON = (props: SVGProps) => (
+
+)
+
+export const UAH_ICON = (props: SVGProps) => (
+
+)
+
+export const THB_ICON = (props: SVGProps) => (
+
+)
diff --git a/src/hooks/useActiveLocalCurrency.ts b/src/hooks/useActiveLocalCurrency.ts
new file mode 100644
index 0000000000..b5ae8e9e4f
--- /dev/null
+++ b/src/hooks/useActiveLocalCurrency.ts
@@ -0,0 +1,29 @@
+import { DEFAULT_LOCAL_CURRENCY, SUPPORTED_LOCAL_CURRENCIES, SupportedLocalCurrency } from 'constants/localCurrencies'
+import { atomWithStorage, useAtomValue } from 'jotai/utils'
+import { useMemo } from 'react'
+
+import useParsedQueryString from './useParsedQueryString'
+
+export const activeLocalCurrencyAtom = atomWithStorage(
+ 'activeLocalCurrency',
+ DEFAULT_LOCAL_CURRENCY
+)
+
+function useUrlLocalCurrency() {
+ const parsed = useParsedQueryString()
+ const parsedLocalCurrency = parsed.cur
+
+ if (typeof parsedLocalCurrency !== 'string') return undefined
+
+ const lowerCaseSupportedLocalCurrency = parsedLocalCurrency.toLowerCase()
+ return SUPPORTED_LOCAL_CURRENCIES.find(
+ (localCurrency) => localCurrency.toLowerCase() === lowerCaseSupportedLocalCurrency
+ )
+}
+
+export function useActiveLocalCurrency(): SupportedLocalCurrency {
+ const activeLocalCurrency = useAtomValue(activeLocalCurrencyAtom)
+ const urlLocalCurrency = useUrlLocalCurrency()
+
+ return useMemo(() => urlLocalCurrency ?? activeLocalCurrency, [activeLocalCurrency, urlLocalCurrency])
+}
diff --git a/src/hooks/useLocalCurrencyLinkProps.ts b/src/hooks/useLocalCurrencyLinkProps.ts
new file mode 100644
index 0000000000..e1467cf3bf
--- /dev/null
+++ b/src/hooks/useLocalCurrencyLinkProps.ts
@@ -0,0 +1,40 @@
+import { sendAnalyticsEvent } from 'analytics'
+import { SupportedLocalCurrency } from 'constants/localCurrencies'
+import useParsedQueryString from 'hooks/useParsedQueryString'
+import { useAtom } from 'jotai'
+import { stringify } from 'qs'
+import { useMemo } from 'react'
+import type { To } from 'react-router-dom'
+import { useLocation } from 'react-router-dom'
+
+import { activeLocalCurrencyAtom, useActiveLocalCurrency } from './useActiveLocalCurrency'
+
+export function useLocalCurrencyLinkProps(localCurrency?: SupportedLocalCurrency): {
+ to?: To
+ onClick?: () => void
+} {
+ const location = useLocation()
+ const qs = useParsedQueryString()
+ const activeLocalCurrency = useActiveLocalCurrency()
+ const [, updateActiveLocalCurrency] = useAtom(activeLocalCurrencyAtom)
+
+ return useMemo(
+ () =>
+ !localCurrency
+ ? {}
+ : {
+ to: {
+ ...location,
+ search: stringify({ ...qs, cur: localCurrency }),
+ },
+ onClick: () => {
+ updateActiveLocalCurrency(localCurrency)
+ sendAnalyticsEvent('Local Currency Selected', {
+ previous_local_currency: activeLocalCurrency,
+ new_local_currency: localCurrency,
+ })
+ },
+ },
+ [localCurrency, location, qs, updateActiveLocalCurrency, activeLocalCurrency]
+ )
+}
diff --git a/src/theme/components/text.tsx b/src/theme/components/text.tsx
index cb511c241e..f80a1145b0 100644
--- a/src/theme/components/text.tsx
+++ b/src/theme/components/text.tsx
@@ -40,9 +40,6 @@ export const ThemedText = {
Hero(props: TextProps) {
return
},
- LabelMedium(props: TextProps) {
- return
- },
LabelSmall(props: TextProps) {
return
},