diff --git a/components/account/AccountPage.tsx b/components/account/AccountPage.tsx index 5cace5ac..71d72490 100644 --- a/components/account/AccountPage.tsx +++ b/components/account/AccountPage.tsx @@ -35,7 +35,7 @@ import { useWallet } from '@solana/wallet-adapter-react' import useLocalStorageState from 'hooks/useLocalStorageState' import AccountOnboardingTour from '@components/tours/AccountOnboardingTour' import dayjs from 'dayjs' -import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings' +import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' export async function getStaticProps({ locale }: { locale: string }) { return { diff --git a/components/account/ActivityFeed.tsx b/components/account/ActivityFeed.tsx index bca4c0d0..2d423a2d 100644 --- a/components/account/ActivityFeed.tsx +++ b/components/account/ActivityFeed.tsx @@ -3,6 +3,7 @@ import MangoDateRangePicker from '@components/forms/DateRangePicker' import Input from '@components/forms/Input' import Label from '@components/forms/Label' import MultiSelectDropdown from '@components/forms/MultiSelectDropdown' +import { EXPLORERS } from '@components/settings/PreferredExplorerSettings' import Button, { IconButton, LinkButton } from '@components/shared/Button' import Modal from '@components/shared/Modal' import Tooltip from '@components/shared/Tooltip' @@ -20,7 +21,6 @@ import useMangoGroup from 'hooks/useMangoGroup' import { useViewport } from 'hooks/useViewport' import { useTranslation } from 'next-i18next' import Image from 'next/legacy/image' -import { EXPLORERS } from 'pages/settings' import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react' import { PREFERRED_EXPLORER_KEY } from 'utils/constants' import { formatDecimal, formatFixedDecimals } from 'utils/numbers' diff --git a/components/account/ActivityFeedTable.tsx b/components/account/ActivityFeedTable.tsx index f918560f..31966506 100644 --- a/components/account/ActivityFeedTable.tsx +++ b/components/account/ActivityFeedTable.tsx @@ -1,3 +1,4 @@ +import { EXPLORERS } from '@components/settings/PreferredExplorerSettings' import { IconButton, LinkButton } from '@components/shared/Button' import SheenLoader from '@components/shared/SheenLoader' import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements' @@ -16,7 +17,6 @@ import useMangoAccount from 'hooks/useMangoAccount' import { useViewport } from 'hooks/useViewport' import { useTranslation } from 'next-i18next' import Image from 'next/legacy/image' -import { EXPLORERS } from 'pages/settings' import { Fragment, useCallback, useState } from 'react' import { PREFERRED_EXPLORER_KEY } from 'utils/constants' import { formatDecimal, formatFixedDecimals } from 'utils/numbers' diff --git a/components/settings/AnimationSettings.tsx b/components/settings/AnimationSettings.tsx new file mode 100644 index 00000000..f599e981 --- /dev/null +++ b/components/settings/AnimationSettings.tsx @@ -0,0 +1,68 @@ +import Switch from '@components/forms/Switch' +import useLocalStorageState from 'hooks/useLocalStorageState' +import { useTranslation } from 'next-i18next' +import { ANIMATION_SETTINGS_KEY } from 'utils/constants' + +export const INITIAL_ANIMATION_SETTINGS = { + 'number-scroll': false, + 'orderbook-flash': false, + 'swap-success': false, +} + +const AnimationSettings = () => { + const { t } = useTranslation(['common', 'settings']) + const [animationSettings, setAnimationSettings] = useLocalStorageState( + ANIMATION_SETTINGS_KEY, + INITIAL_ANIMATION_SETTINGS + ) + + const handleToggleAnimationSetting = (settingName: string) => { + if (settingName === 'all') { + const toggle = !Object.values(animationSettings).includes(false) + Object.keys(animationSettings).forEach((key) => { + animationSettings[key] = !toggle + }) + setAnimationSettings(animationSettings) + } else { + setAnimationSettings({ + ...animationSettings, + [settingName]: !animationSettings[settingName], + }) + } + } + + return ( + <> +
+

{t('settings:animations')}

+ handleToggleAnimationSetting('all')} + /> +
+
+

{t('settings:number-scroll')}

+ handleToggleAnimationSetting('number-scroll')} + /> +
+
+

{t('settings:orderbook-flash')}

+ handleToggleAnimationSetting('orderbook-flash')} + /> +
+
+

{t('settings:swap-success')}

+ handleToggleAnimationSetting('swap-success')} + /> +
+ + ) +} + +export default AnimationSettings diff --git a/components/settings/DisplaySettings.tsx b/components/settings/DisplaySettings.tsx new file mode 100644 index 00000000..bfcbd5db --- /dev/null +++ b/components/settings/DisplaySettings.tsx @@ -0,0 +1,105 @@ +import ButtonGroup from '@components/forms/ButtonGroup' +import dayjs from 'dayjs' +import useLocalStorageState from 'hooks/useLocalStorageState' +import { useTranslation } from 'next-i18next' +import { useTheme } from 'next-themes' +import { useRouter } from 'next/router' +import { useCallback, useMemo } from 'react' +import { NOTIFICATION_POSITION_KEY, SIZE_INPUT_UI_KEY } from 'utils/constants' + +const NOTIFICATION_POSITIONS = [ + 'bottom-left', + 'bottom-right', + 'top-left', + 'top-right', +] + +const LANGS = [ + { locale: 'en', name: 'english', description: 'english' }, + { locale: 'ru', name: 'russian', description: 'russian' }, + { locale: 'es', name: 'spanish', description: 'spanish' }, + { + locale: 'zh_tw', + name: 'chinese-traditional', + description: 'traditional chinese', + }, + { locale: 'zh', name: 'chinese', description: 'simplified chinese' }, +] + +const DisplaySettings = () => { + const { t } = useTranslation(['common', 'settings']) + const { theme, setTheme } = useTheme() + const [savedLanguage, setSavedLanguage] = useLocalStorageState('language', '') + const router = useRouter() + const { pathname, asPath, query } = router + const [notificationPosition, setNotificationPosition] = useLocalStorageState( + NOTIFICATION_POSITION_KEY, + 'bottom-left' + ) + const [tradeFormUi, setTradeFormUi] = useLocalStorageState( + SIZE_INPUT_UI_KEY, + 'Slider' + ) + const themes = useMemo(() => { + return [t('settings:light'), t('settings:mango'), t('settings:dark')] + }, [t]) + + const handleLangChange = useCallback( + (l: string) => { + setSavedLanguage(l) + router.push({ pathname, query }, asPath, { locale: l }) + dayjs.locale(l == 'zh_tw' ? 'zh-tw' : l) + }, + [router] + ) + + return ( + <> +

{t('settings:display')}

+
+

{t('settings:theme')}

+
+ setTheme(t)} + values={themes} + /> +
+
+
+

{t('settings:language')}

+
+ handleLangChange(l)} + values={LANGS.map((val) => val.locale)} + names={LANGS.map((val) => t(`settings:${val.name}`))} + /> +
+
+
+

{t('settings:notification-position')}

+
+ setNotificationPosition(p)} + values={NOTIFICATION_POSITIONS} + names={NOTIFICATION_POSITIONS.map((val) => t(`settings:${val}`))} + /> +
+
+
+

{t('settings:swap-trade-size-selector')}

+
+ setTradeFormUi(v)} + values={[t('settings:slider'), t('settings:buttons')]} + /> +
+
+ + ) +} + +export default DisplaySettings diff --git a/components/settings/PreferredExplorerSettings.tsx b/components/settings/PreferredExplorerSettings.tsx new file mode 100644 index 00000000..bd148ee1 --- /dev/null +++ b/components/settings/PreferredExplorerSettings.tsx @@ -0,0 +1,49 @@ +import { CheckCircleIcon } from '@heroicons/react/20/solid' +import useLocalStorageState from 'hooks/useLocalStorageState' +import { useTranslation } from 'next-i18next' +import Image from 'next/image' +import { PREFERRED_EXPLORER_KEY } from 'utils/constants' + +export const EXPLORERS = [ + { name: 'solana-explorer', url: 'https://explorer.solana.com/tx/' }, + { name: 'solscan', url: 'https://solscan.io/tx/' }, + { name: 'solana-beach', url: 'https://solanabeach.io/transaction/' }, + { name: 'solanafm', url: 'https://solana.fm/tx/' }, +] + +const PreferredExplorerSettings = () => { + const { t } = useTranslation('settings') + const [preferredExplorer, setPreferredExplorer] = useLocalStorageState( + PREFERRED_EXPLORER_KEY, + EXPLORERS[0] + ) + return ( + <> +

{t('settings:preferred-explorer')}

+
+ {EXPLORERS.map((ex) => ( + + ))} +
+ + ) +} + +export default PreferredExplorerSettings diff --git a/components/settings/SettingsPage.tsx b/components/settings/SettingsPage.tsx new file mode 100644 index 00000000..78a28382 --- /dev/null +++ b/components/settings/SettingsPage.tsx @@ -0,0 +1,25 @@ +import AnimationSettings from './AnimationSettings' +import DisplaySettings from './DisplaySettings' +import PreferredExplorerSettings from './PreferredExplorerSettings' +import SoundSettings from './SoundSettings' + +const SettingsPage = () => { + return ( +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ ) +} + +export default SettingsPage diff --git a/components/settings/SoundSettings.tsx b/components/settings/SoundSettings.tsx new file mode 100644 index 00000000..f34fdbd8 --- /dev/null +++ b/components/settings/SoundSettings.tsx @@ -0,0 +1,68 @@ +import Switch from '@components/forms/Switch' +import useLocalStorageState from 'hooks/useLocalStorageState' +import { useTranslation } from 'next-i18next' +import { SOUND_SETTINGS_KEY } from 'utils/constants' + +export const INITIAL_SOUND_SETTINGS = { + 'swap-success': false, + 'transaction-success': false, + 'transaction-fail': false, +} + +const SoundSettings = () => { + const { t } = useTranslation(['common', 'settings']) + const [soundSettings, setSoundSettings] = useLocalStorageState( + SOUND_SETTINGS_KEY, + INITIAL_SOUND_SETTINGS + ) + + const handleToggleSoundSetting = (settingName: string) => { + if (settingName === 'all') { + const toggle = !Object.values(soundSettings).includes(false) + Object.keys(soundSettings).forEach((key) => { + soundSettings[key] = !toggle + }) + setSoundSettings(soundSettings) + } else { + setSoundSettings({ + ...soundSettings, + [settingName]: !soundSettings[settingName], + }) + } + } + + return ( + <> +
+

{t('settings:sounds')}

+ handleToggleSoundSetting('all')} + /> +
+
+

{t('settings:transaction-success')}

+ handleToggleSoundSetting('transaction-success')} + /> +
+
+

{t('settings:transaction-fail')}

+ handleToggleSoundSetting('transaction-fail')} + /> +
+
+

{t('settings:swap-success')}

+ handleToggleSoundSetting('swap-success')} + /> +
+ + ) +} + +export default SoundSettings diff --git a/components/shared/DetailedAreaChart.tsx b/components/shared/DetailedAreaChart.tsx index 9970099f..ff56c8b2 100644 --- a/components/shared/DetailedAreaChart.tsx +++ b/components/shared/DetailedAreaChart.tsx @@ -23,8 +23,8 @@ import ChartRangeButtons from './ChartRangeButtons' import Change from './Change' import useLocalStorageState from 'hooks/useLocalStorageState' import { ANIMATION_SETTINGS_KEY } from 'utils/constants' -import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings' import { formatFixedDecimals } from 'utils/numbers' +import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' dayjs.extend(relativeTime) diff --git a/components/shared/Notification.tsx b/components/shared/Notification.tsx index b8349923..74c15f90 100644 --- a/components/shared/Notification.tsx +++ b/components/shared/Notification.tsx @@ -16,9 +16,9 @@ import { PREFERRED_EXPLORER_KEY, } from '../../utils/constants' import useLocalStorageState from 'hooks/useLocalStorageState' -import { EXPLORERS } from 'pages/settings' import { useTranslation } from 'next-i18next' import useSolBalance from 'hooks/useSolBalance' +import { EXPLORERS } from '@components/settings/PreferredExplorerSettings' const setMangoStore = mangoStore.getState().set diff --git a/components/stats/TokenStats.tsx b/components/stats/TokenStats.tsx index a7a94853..40852245 100644 --- a/components/stats/TokenStats.tsx +++ b/components/stats/TokenStats.tsx @@ -22,7 +22,7 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements' import useMangoGroup from 'hooks/useMangoGroup' import useLocalStorageState from 'hooks/useLocalStorageState' import { ANIMATION_SETTINGS_KEY } from 'utils/constants' -import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings' +import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' const TokenStats = () => { const { t } = useTranslation(['common', 'token']) diff --git a/components/swap/JupiterRouteInfo.tsx b/components/swap/JupiterRouteInfo.tsx index b0b2af8f..75207595 100644 --- a/components/swap/JupiterRouteInfo.tsx +++ b/components/swap/JupiterRouteInfo.tsx @@ -32,9 +32,9 @@ import { RouteInfo } from 'types/jupiter' import useJupiterSwapData from './useJupiterSwapData' import { Transaction } from '@solana/web3.js' import { SOUND_SETTINGS_KEY } from 'utils/constants' -import { INITIAL_SOUND_SETTINGS } from 'pages/settings' import useLocalStorageState from 'hooks/useLocalStorageState' import { Howl } from 'howler' +import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings' type JupiterRouteInfoProps = { amountIn: Decimal diff --git a/components/swap/SwapHistoryTable.tsx b/components/swap/SwapHistoryTable.tsx index 7db2fff4..01f0df67 100644 --- a/components/swap/SwapHistoryTable.tsx +++ b/components/swap/SwapHistoryTable.tsx @@ -21,12 +21,12 @@ import { } from '../../utils/numbers' import useLocalStorageState from 'hooks/useLocalStorageState' import { PREFERRED_EXPLORER_KEY } from 'utils/constants' -import { EXPLORERS } from 'pages/settings' import Tooltip from '@components/shared/Tooltip' import { formatTokenSymbol } from 'utils/tokens' import useJupiterMints from 'hooks/useJupiterMints' import { Table, Td, Th, TrBody } from '@components/shared/TableElements' import { useWallet } from '@solana/wallet-adapter-react' +import { EXPLORERS } from '@components/settings/PreferredExplorerSettings' const SwapHistoryTable = ({ swapHistory, diff --git a/components/swap/SwapSuccessParticles.tsx b/components/swap/SwapSuccessParticles.tsx index 1bacda90..36bd2267 100644 --- a/components/swap/SwapSuccessParticles.tsx +++ b/components/swap/SwapSuccessParticles.tsx @@ -1,7 +1,7 @@ +import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' import mangoStore from '@store/mangoStore' import useJupiterMints from 'hooks/useJupiterMints' import useLocalStorageState from 'hooks/useLocalStorageState' -import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings' import { useEffect, useMemo } from 'react' import Particles from 'react-tsparticles' import { ANIMATION_SETTINGS_KEY } from 'utils/constants' diff --git a/components/swap/SwapTokenChart.tsx b/components/swap/SwapTokenChart.tsx index 3666c995..21d4288d 100644 --- a/components/swap/SwapTokenChart.tsx +++ b/components/swap/SwapTokenChart.tsx @@ -28,7 +28,7 @@ import mangoStore from '@store/mangoStore' import useJupiterSwapData from './useJupiterSwapData' import useLocalStorageState from 'hooks/useLocalStorageState' import { ANIMATION_SETTINGS_KEY } from 'utils/constants' -import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings' +import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' dayjs.extend(relativeTime) diff --git a/components/trade/Orderbook.tsx b/components/trade/Orderbook.tsx index 8b3f10a9..e0de1855 100644 --- a/components/trade/Orderbook.tsx +++ b/components/trade/Orderbook.tsx @@ -23,7 +23,7 @@ import { PerpMarket, } from '@blockworks-foundation/mango-v4' import useSelectedMarket from 'hooks/useSelectedMarket' -import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings' +import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' function decodeBookL2( client: MangoClient, diff --git a/pages/settings.tsx b/pages/settings.tsx index 22f2dc82..637618a6 100644 --- a/pages/settings.tsx +++ b/pages/settings.tsx @@ -1,22 +1,6 @@ import type { NextPage } from 'next' -import { useTranslation } from 'next-i18next' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -import { useTheme } from 'next-themes' -import { useRouter } from 'next/router' -import ButtonGroup from '../components/forms/ButtonGroup' -import useLocalStorageState from '../hooks/useLocalStorageState' -import dayjs from 'dayjs' -import { - ANIMATION_SETTINGS_KEY, - NOTIFICATION_POSITION_KEY, - PREFERRED_EXPLORER_KEY, - SIZE_INPUT_UI_KEY, - SOUND_SETTINGS_KEY, -} from 'utils/constants' -import Switch from '@components/forms/Switch' -import { useCallback, useMemo } from 'react' -import { CheckCircleIcon } from '@heroicons/react/20/solid' -import Image from 'next/legacy/image' +import SettingsPage from '@components/settings/SettingsPage' require('dayjs/locale/en') require('dayjs/locale/es') @@ -36,253 +20,10 @@ export async function getStaticProps({ locale }: { locale: string }) { } } -export const LANGS = [ - { locale: 'en', name: 'english', description: 'english' }, - { locale: 'ru', name: 'russian', description: 'russian' }, - { locale: 'es', name: 'spanish', description: 'spanish' }, - { - locale: 'zh_tw', - name: 'chinese-traditional', - description: 'traditional chinese', - }, - { locale: 'zh', name: 'chinese', description: 'simplified chinese' }, -] - -export const EXPLORERS = [ - { name: 'solana-explorer', url: 'https://explorer.solana.com/tx/' }, - { name: 'solscan', url: 'https://solscan.io/tx/' }, - { name: 'solana-beach', url: 'https://solanabeach.io/transaction/' }, - { name: 'solanafm', url: 'https://solana.fm/tx/' }, -] - -const NOTIFICATION_POSITIONS = [ - 'bottom-left', - 'bottom-right', - 'top-left', - 'top-right', -] - -export const INITIAL_ANIMATION_SETTINGS = { - 'number-scroll': false, - 'orderbook-flash': false, - 'swap-success': false, -} - -export const INITIAL_SOUND_SETTINGS = { - 'swap-success': false, - 'transaction-success': false, - 'transaction-fail': false, -} - const Settings: NextPage = () => { - const { t } = useTranslation(['common', 'settings']) - const { theme, setTheme } = useTheme() - const [savedLanguage, setSavedLanguage] = useLocalStorageState('language', '') - const [notificationPosition, setNotificationPosition] = useLocalStorageState( - NOTIFICATION_POSITION_KEY, - 'bottom-left' - ) - const router = useRouter() - const { pathname, asPath, query } = router - const [preferredExplorer, setPreferredExplorer] = useLocalStorageState( - PREFERRED_EXPLORER_KEY, - EXPLORERS[0] - ) - const [tradeFormUi, setTradeFormUi] = useLocalStorageState( - SIZE_INPUT_UI_KEY, - 'Slider' - ) - const themes = useMemo(() => { - return [t('settings:light'), t('settings:mango'), t('settings:dark')] - }, [t]) - const [soundSettings, setSoundSettings] = useLocalStorageState( - SOUND_SETTINGS_KEY, - INITIAL_SOUND_SETTINGS - ) - const [animationSettings, setAnimationSettings] = useLocalStorageState( - ANIMATION_SETTINGS_KEY, - INITIAL_ANIMATION_SETTINGS - ) - - const handleToggleAnimationSetting = (settingName: string) => { - if (settingName === 'all') { - const toggle = !Object.values(animationSettings).includes(false) - Object.keys(animationSettings).forEach((key) => { - animationSettings[key] = !toggle - }) - setAnimationSettings(animationSettings) - } else { - setAnimationSettings({ - ...animationSettings, - [settingName]: !animationSettings[settingName], - }) - } - } - - const handleToggleSoundSetting = (settingName: string) => { - if (settingName === 'all') { - const toggle = !Object.values(soundSettings).includes(false) - Object.keys(soundSettings).forEach((key) => { - soundSettings[key] = !toggle - }) - setSoundSettings(soundSettings) - } else { - setSoundSettings({ - ...soundSettings, - [settingName]: !soundSettings[settingName], - }) - } - } - - const handleLangChange = useCallback( - (l: string) => { - setSavedLanguage(l) - router.push({ pathname, query }, asPath, { locale: l }) - dayjs.locale(l == 'zh_tw' ? 'zh-tw' : l) - }, - [router] - ) - return (
-
-
-

{t('settings:display')}

-
-

{t('settings:theme')}

-
- setTheme(t)} - values={themes} - /> -
-
-
-

{t('settings:language')}

-
- handleLangChange(l)} - values={LANGS.map((val) => val.locale)} - names={LANGS.map((val) => t(`settings:${val.name}`))} - /> -
-
-
-

- {t('settings:notification-position')} -

-
- setNotificationPosition(p)} - values={NOTIFICATION_POSITIONS} - names={NOTIFICATION_POSITIONS.map((val) => - t(`settings:${val}`) - )} - /> -
-
-
-

- {t('settings:swap-trade-size-selector')} -

-
- setTradeFormUi(v)} - values={[t('settings:slider'), t('settings:buttons')]} - /> -
-
-
-
-
-

{t('settings:animations')}

- handleToggleAnimationSetting('all')} - /> -
-
-

{t('settings:number-scroll')}

- handleToggleAnimationSetting('number-scroll')} - /> -
-
-

{t('settings:orderbook-flash')}

- handleToggleAnimationSetting('orderbook-flash')} - /> -
-
-

{t('settings:swap-success')}

- handleToggleAnimationSetting('swap-success')} - /> -
-
-
-
-

{t('settings:sounds')}

- handleToggleSoundSetting('all')} - /> -
-
-

{t('settings:transaction-success')}

- handleToggleSoundSetting('transaction-success')} - /> -
-
-

{t('settings:transaction-fail')}

- handleToggleSoundSetting('transaction-fail')} - /> -
-
-

{t('settings:swap-success')}

- handleToggleSoundSetting('swap-success')} - /> -
-
-
-

{t('settings:preferred-explorer')}

-
- {EXPLORERS.map((ex) => ( - - ))} -
-
-
+
) } diff --git a/pages/token/[token].tsx b/pages/token/[token].tsx index e2599b86..e8d718bf 100644 --- a/pages/token/[token].tsx +++ b/pages/token/[token].tsx @@ -31,7 +31,7 @@ import useJupiterMints from 'hooks/useJupiterMints' import { useCoingecko } from 'hooks/useCoingecko' import useLocalStorageState from 'hooks/useLocalStorageState' import { ANIMATION_SETTINGS_KEY } from 'utils/constants' -import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings' +import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' const PriceChart = dynamic(() => import('@components/token/PriceChart'), { ssr: false, }) diff --git a/utils/notifications.ts b/utils/notifications.ts index 70cbe4b0..070cb893 100644 --- a/utils/notifications.ts +++ b/utils/notifications.ts @@ -1,6 +1,6 @@ +import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings' import mangoStore from '@store/mangoStore' import { Howl } from 'howler' -import { INITIAL_SOUND_SETTINGS } from 'pages/settings' import { SOUND_SETTINGS_KEY } from './constants' export type Notification = {