componentize settings page
This commit is contained in:
parent
39485015c2
commit
75f2fd720b
|
@ -35,7 +35,7 @@ import { useWallet } from '@solana/wallet-adapter-react'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import AccountOnboardingTour from '@components/tours/AccountOnboardingTour'
|
import AccountOnboardingTour from '@components/tours/AccountOnboardingTour'
|
||||||
import dayjs from 'dayjs'
|
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 }) {
|
export async function getStaticProps({ locale }: { locale: string }) {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import MangoDateRangePicker from '@components/forms/DateRangePicker'
|
||||||
import Input from '@components/forms/Input'
|
import Input from '@components/forms/Input'
|
||||||
import Label from '@components/forms/Label'
|
import Label from '@components/forms/Label'
|
||||||
import MultiSelectDropdown from '@components/forms/MultiSelectDropdown'
|
import MultiSelectDropdown from '@components/forms/MultiSelectDropdown'
|
||||||
|
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
|
||||||
import Button, { IconButton, LinkButton } from '@components/shared/Button'
|
import Button, { IconButton, LinkButton } from '@components/shared/Button'
|
||||||
import Modal from '@components/shared/Modal'
|
import Modal from '@components/shared/Modal'
|
||||||
import Tooltip from '@components/shared/Tooltip'
|
import Tooltip from '@components/shared/Tooltip'
|
||||||
|
@ -20,7 +21,6 @@ import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
import { useViewport } from 'hooks/useViewport'
|
import { useViewport } from 'hooks/useViewport'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import Image from 'next/legacy/image'
|
import Image from 'next/legacy/image'
|
||||||
import { EXPLORERS } from 'pages/settings'
|
|
||||||
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
|
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
||||||
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
|
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
|
||||||
import { IconButton, LinkButton } from '@components/shared/Button'
|
import { IconButton, LinkButton } from '@components/shared/Button'
|
||||||
import SheenLoader from '@components/shared/SheenLoader'
|
import SheenLoader from '@components/shared/SheenLoader'
|
||||||
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
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 { useViewport } from 'hooks/useViewport'
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import Image from 'next/legacy/image'
|
import Image from 'next/legacy/image'
|
||||||
import { EXPLORERS } from 'pages/settings'
|
|
||||||
import { Fragment, useCallback, useState } from 'react'
|
import { Fragment, useCallback, useState } from 'react'
|
||||||
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
||||||
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
|
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
|
||||||
|
|
|
@ -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 (
|
||||||
|
<>
|
||||||
|
<div className="mb-4 flex items-center justify-between pr-4">
|
||||||
|
<h2 className="text-base">{t('settings:animations')}</h2>
|
||||||
|
<Switch
|
||||||
|
checked={!Object.values(animationSettings).includes(false)}
|
||||||
|
onChange={() => handleToggleAnimationSetting('all')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:number-scroll')}</p>
|
||||||
|
<Switch
|
||||||
|
checked={animationSettings['number-scroll']}
|
||||||
|
onChange={() => handleToggleAnimationSetting('number-scroll')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:orderbook-flash')}</p>
|
||||||
|
<Switch
|
||||||
|
checked={animationSettings['orderbook-flash']}
|
||||||
|
onChange={() => handleToggleAnimationSetting('orderbook-flash')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:swap-success')}</p>
|
||||||
|
<Switch
|
||||||
|
checked={animationSettings['swap-success']}
|
||||||
|
onChange={() => handleToggleAnimationSetting('swap-success')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnimationSettings
|
|
@ -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 (
|
||||||
|
<>
|
||||||
|
<h2 className="mb-4 text-base">{t('settings:display')}</h2>
|
||||||
|
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:theme')}</p>
|
||||||
|
<div className="w-full min-w-[220px] md:w-auto">
|
||||||
|
<ButtonGroup
|
||||||
|
activeValue={theme}
|
||||||
|
onChange={(t) => setTheme(t)}
|
||||||
|
values={themes}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:language')}</p>
|
||||||
|
<div className="w-full min-w-[330px] md:w-[480px] md:pl-4">
|
||||||
|
<ButtonGroup
|
||||||
|
activeValue={savedLanguage}
|
||||||
|
onChange={(l) => handleLangChange(l)}
|
||||||
|
values={LANGS.map((val) => val.locale)}
|
||||||
|
names={LANGS.map((val) => t(`settings:${val.name}`))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:notification-position')}</p>
|
||||||
|
<div className="w-full min-w-[330px] md:w-[480px] md:pl-4">
|
||||||
|
<ButtonGroup
|
||||||
|
activeValue={notificationPosition}
|
||||||
|
onChange={(p) => setNotificationPosition(p)}
|
||||||
|
values={NOTIFICATION_POSITIONS}
|
||||||
|
names={NOTIFICATION_POSITIONS.map((val) => t(`settings:${val}`))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:swap-trade-size-selector')}</p>
|
||||||
|
<div className="w-full min-w-[160px] md:w-auto">
|
||||||
|
<ButtonGroup
|
||||||
|
activeValue={tradeFormUi}
|
||||||
|
onChange={(v) => setTradeFormUi(v)}
|
||||||
|
values={[t('settings:slider'), t('settings:buttons')]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DisplaySettings
|
|
@ -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 (
|
||||||
|
<>
|
||||||
|
<h2 className="mb-4 text-base">{t('settings:preferred-explorer')}</h2>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{EXPLORERS.map((ex) => (
|
||||||
|
<button
|
||||||
|
className="default-transition flex w-full items-center justify-between rounded-md bg-th-bkg-2 p-4 hover:bg-th-bkg-3"
|
||||||
|
onClick={() => setPreferredExplorer(ex)}
|
||||||
|
key={ex.name}
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Image
|
||||||
|
alt=""
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
src={`/explorer-logos/${ex.name}.png`}
|
||||||
|
/>
|
||||||
|
<p>{t(`settings:${ex.name}`)}</p>
|
||||||
|
</div>
|
||||||
|
{preferredExplorer.url === ex.url ? (
|
||||||
|
<CheckCircleIcon className="h-5 w-5 text-th-green" />
|
||||||
|
) : null}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PreferredExplorerSettings
|
|
@ -0,0 +1,25 @@
|
||||||
|
import AnimationSettings from './AnimationSettings'
|
||||||
|
import DisplaySettings from './DisplaySettings'
|
||||||
|
import PreferredExplorerSettings from './PreferredExplorerSettings'
|
||||||
|
import SoundSettings from './SoundSettings'
|
||||||
|
|
||||||
|
const SettingsPage = () => {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-12">
|
||||||
|
<div className="col-span-12 border-b border-th-bkg-3 lg:col-span-8 lg:col-start-3">
|
||||||
|
<DisplaySettings />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-12 border-b border-th-bkg-3 pt-8 lg:col-span-8 lg:col-start-3">
|
||||||
|
<AnimationSettings />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-12 border-b border-th-bkg-3 pt-8 lg:col-span-8 lg:col-start-3">
|
||||||
|
<SoundSettings />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-12 pt-8 lg:col-span-8 lg:col-start-3">
|
||||||
|
<PreferredExplorerSettings />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingsPage
|
|
@ -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 (
|
||||||
|
<>
|
||||||
|
<div className="mb-4 flex items-center justify-between pr-4">
|
||||||
|
<h2 className="text-base">{t('settings:sounds')}</h2>
|
||||||
|
<Switch
|
||||||
|
checked={!Object.values(soundSettings).includes(false)}
|
||||||
|
onChange={() => handleToggleSoundSetting('all')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:transaction-success')}</p>
|
||||||
|
<Switch
|
||||||
|
checked={soundSettings['transaction-success']}
|
||||||
|
onChange={() => handleToggleSoundSetting('transaction-success')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:transaction-fail')}</p>
|
||||||
|
<Switch
|
||||||
|
checked={soundSettings['transaction-fail']}
|
||||||
|
onChange={() => handleToggleSoundSetting('transaction-fail')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
||||||
|
<p className="mb-2 lg:mb-0">{t('settings:swap-success')}</p>
|
||||||
|
<Switch
|
||||||
|
checked={soundSettings['swap-success']}
|
||||||
|
onChange={() => handleToggleSoundSetting('swap-success')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SoundSettings
|
|
@ -23,8 +23,8 @@ import ChartRangeButtons from './ChartRangeButtons'
|
||||||
import Change from './Change'
|
import Change from './Change'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
||||||
import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings'
|
|
||||||
import { formatFixedDecimals } from 'utils/numbers'
|
import { formatFixedDecimals } from 'utils/numbers'
|
||||||
|
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@ import {
|
||||||
PREFERRED_EXPLORER_KEY,
|
PREFERRED_EXPLORER_KEY,
|
||||||
} from '../../utils/constants'
|
} from '../../utils/constants'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { EXPLORERS } from 'pages/settings'
|
|
||||||
import { useTranslation } from 'next-i18next'
|
import { useTranslation } from 'next-i18next'
|
||||||
import useSolBalance from 'hooks/useSolBalance'
|
import useSolBalance from 'hooks/useSolBalance'
|
||||||
|
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
|
||||||
|
|
||||||
const setMangoStore = mangoStore.getState().set
|
const setMangoStore = mangoStore.getState().set
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
||||||
import useMangoGroup from 'hooks/useMangoGroup'
|
import useMangoGroup from 'hooks/useMangoGroup'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
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 TokenStats = () => {
|
||||||
const { t } = useTranslation(['common', 'token'])
|
const { t } = useTranslation(['common', 'token'])
|
||||||
|
|
|
@ -32,9 +32,9 @@ import { RouteInfo } from 'types/jupiter'
|
||||||
import useJupiterSwapData from './useJupiterSwapData'
|
import useJupiterSwapData from './useJupiterSwapData'
|
||||||
import { Transaction } from '@solana/web3.js'
|
import { Transaction } from '@solana/web3.js'
|
||||||
import { SOUND_SETTINGS_KEY } from 'utils/constants'
|
import { SOUND_SETTINGS_KEY } from 'utils/constants'
|
||||||
import { INITIAL_SOUND_SETTINGS } from 'pages/settings'
|
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { Howl } from 'howler'
|
import { Howl } from 'howler'
|
||||||
|
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
|
||||||
|
|
||||||
type JupiterRouteInfoProps = {
|
type JupiterRouteInfoProps = {
|
||||||
amountIn: Decimal
|
amountIn: Decimal
|
||||||
|
|
|
@ -21,12 +21,12 @@ import {
|
||||||
} from '../../utils/numbers'
|
} from '../../utils/numbers'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
|
||||||
import { EXPLORERS } from 'pages/settings'
|
|
||||||
import Tooltip from '@components/shared/Tooltip'
|
import Tooltip from '@components/shared/Tooltip'
|
||||||
import { formatTokenSymbol } from 'utils/tokens'
|
import { formatTokenSymbol } from 'utils/tokens'
|
||||||
import useJupiterMints from 'hooks/useJupiterMints'
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
import { Table, Td, Th, TrBody } from '@components/shared/TableElements'
|
import { Table, Td, Th, TrBody } from '@components/shared/TableElements'
|
||||||
import { useWallet } from '@solana/wallet-adapter-react'
|
import { useWallet } from '@solana/wallet-adapter-react'
|
||||||
|
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
|
||||||
|
|
||||||
const SwapHistoryTable = ({
|
const SwapHistoryTable = ({
|
||||||
swapHistory,
|
swapHistory,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import useJupiterMints from 'hooks/useJupiterMints'
|
import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings'
|
|
||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect, useMemo } from 'react'
|
||||||
import Particles from 'react-tsparticles'
|
import Particles from 'react-tsparticles'
|
||||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
||||||
|
|
|
@ -28,7 +28,7 @@ import mangoStore from '@store/mangoStore'
|
||||||
import useJupiterSwapData from './useJupiterSwapData'
|
import useJupiterSwapData from './useJupiterSwapData'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
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)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {
|
||||||
PerpMarket,
|
PerpMarket,
|
||||||
} from '@blockworks-foundation/mango-v4'
|
} from '@blockworks-foundation/mango-v4'
|
||||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||||
import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings'
|
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
|
||||||
|
|
||||||
function decodeBookL2(
|
function decodeBookL2(
|
||||||
client: MangoClient,
|
client: MangoClient,
|
||||||
|
|
|
@ -1,22 +1,6 @@
|
||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next'
|
||||||
import { useTranslation } from 'next-i18next'
|
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
import { useTheme } from 'next-themes'
|
import SettingsPage from '@components/settings/SettingsPage'
|
||||||
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'
|
|
||||||
|
|
||||||
require('dayjs/locale/en')
|
require('dayjs/locale/en')
|
||||||
require('dayjs/locale/es')
|
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 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 (
|
return (
|
||||||
<div className="p-8 pb-20 md:pb-16 lg:p-10">
|
<div className="p-8 pb-20 md:pb-16 lg:p-10">
|
||||||
<div className="grid grid-cols-12">
|
<SettingsPage />
|
||||||
<div className="col-span-12 border-b border-th-bkg-3 lg:col-span-8 lg:col-start-3">
|
|
||||||
<h2 className="mb-4 text-base">{t('settings:display')}</h2>
|
|
||||||
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:theme')}</p>
|
|
||||||
<div className="w-full min-w-[220px] md:w-auto">
|
|
||||||
<ButtonGroup
|
|
||||||
activeValue={theme}
|
|
||||||
onChange={(t) => setTheme(t)}
|
|
||||||
values={themes}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:language')}</p>
|
|
||||||
<div className="w-full min-w-[330px] md:w-[480px] md:pl-4">
|
|
||||||
<ButtonGroup
|
|
||||||
activeValue={savedLanguage}
|
|
||||||
onChange={(l) => handleLangChange(l)}
|
|
||||||
values={LANGS.map((val) => val.locale)}
|
|
||||||
names={LANGS.map((val) => t(`settings:${val.name}`))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">
|
|
||||||
{t('settings:notification-position')}
|
|
||||||
</p>
|
|
||||||
<div className="w-full min-w-[330px] md:w-[480px] md:pl-4">
|
|
||||||
<ButtonGroup
|
|
||||||
activeValue={notificationPosition}
|
|
||||||
onChange={(p) => setNotificationPosition(p)}
|
|
||||||
values={NOTIFICATION_POSITIONS}
|
|
||||||
names={NOTIFICATION_POSITIONS.map((val) =>
|
|
||||||
t(`settings:${val}`)
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col border-t border-th-bkg-3 py-4 md:flex-row md:items-center md:justify-between md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">
|
|
||||||
{t('settings:swap-trade-size-selector')}
|
|
||||||
</p>
|
|
||||||
<div className="w-full min-w-[160px] md:w-auto">
|
|
||||||
<ButtonGroup
|
|
||||||
activeValue={tradeFormUi}
|
|
||||||
onChange={(v) => setTradeFormUi(v)}
|
|
||||||
values={[t('settings:slider'), t('settings:buttons')]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-12 border-b border-th-bkg-3 pt-8 lg:col-span-8 lg:col-start-3">
|
|
||||||
<div className="mb-4 flex items-center justify-between pr-4">
|
|
||||||
<h2 className="text-base">{t('settings:animations')}</h2>
|
|
||||||
<Switch
|
|
||||||
checked={!Object.values(animationSettings).includes(false)}
|
|
||||||
onChange={() => handleToggleAnimationSetting('all')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:number-scroll')}</p>
|
|
||||||
<Switch
|
|
||||||
checked={animationSettings['number-scroll']}
|
|
||||||
onChange={() => handleToggleAnimationSetting('number-scroll')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:orderbook-flash')}</p>
|
|
||||||
<Switch
|
|
||||||
checked={animationSettings['orderbook-flash']}
|
|
||||||
onChange={() => handleToggleAnimationSetting('orderbook-flash')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:swap-success')}</p>
|
|
||||||
<Switch
|
|
||||||
checked={animationSettings['swap-success']}
|
|
||||||
onChange={() => handleToggleAnimationSetting('swap-success')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-12 border-b border-th-bkg-3 pt-8 lg:col-span-8 lg:col-start-3">
|
|
||||||
<div className="mb-4 flex items-center justify-between pr-4">
|
|
||||||
<h2 className="text-base">{t('settings:sounds')}</h2>
|
|
||||||
<Switch
|
|
||||||
checked={!Object.values(soundSettings).includes(false)}
|
|
||||||
onChange={() => handleToggleSoundSetting('all')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:transaction-success')}</p>
|
|
||||||
<Switch
|
|
||||||
checked={soundSettings['transaction-success']}
|
|
||||||
onChange={() => handleToggleSoundSetting('transaction-success')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:transaction-fail')}</p>
|
|
||||||
<Switch
|
|
||||||
checked={soundSettings['transaction-fail']}
|
|
||||||
onChange={() => handleToggleSoundSetting('transaction-fail')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4">
|
|
||||||
<p className="mb-2 lg:mb-0">{t('settings:swap-success')}</p>
|
|
||||||
<Switch
|
|
||||||
checked={soundSettings['swap-success']}
|
|
||||||
onChange={() => handleToggleSoundSetting('swap-success')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-12 pt-8 lg:col-span-8 lg:col-start-3">
|
|
||||||
<h2 className="mb-4 text-base">{t('settings:preferred-explorer')}</h2>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{EXPLORERS.map((ex) => (
|
|
||||||
<button
|
|
||||||
className="default-transition flex w-full items-center justify-between rounded-md bg-th-bkg-2 p-4 hover:bg-th-bkg-3"
|
|
||||||
onClick={() => setPreferredExplorer(ex)}
|
|
||||||
key={ex.name}
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Image
|
|
||||||
alt=""
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
src={`/explorer-logos/${ex.name}.png`}
|
|
||||||
/>
|
|
||||||
<p>{t(`settings:${ex.name}`)}</p>
|
|
||||||
</div>
|
|
||||||
{preferredExplorer.url === ex.url ? (
|
|
||||||
<CheckCircleIcon className="h-5 w-5 text-th-green" />
|
|
||||||
) : null}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import useJupiterMints from 'hooks/useJupiterMints'
|
||||||
import { useCoingecko } from 'hooks/useCoingecko'
|
import { useCoingecko } from 'hooks/useCoingecko'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
|
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'), {
|
const PriceChart = dynamic(() => import('@components/token/PriceChart'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { INITIAL_SOUND_SETTINGS } from '@components/settings/SoundSettings'
|
||||||
import mangoStore from '@store/mangoStore'
|
import mangoStore from '@store/mangoStore'
|
||||||
import { Howl } from 'howler'
|
import { Howl } from 'howler'
|
||||||
import { INITIAL_SOUND_SETTINGS } from 'pages/settings'
|
|
||||||
import { SOUND_SETTINGS_KEY } from './constants'
|
import { SOUND_SETTINGS_KEY } from './constants'
|
||||||
|
|
||||||
export type Notification = {
|
export type Notification = {
|
||||||
|
|
Loading…
Reference in New Issue