sound support and settings

This commit is contained in:
saml33 2022-11-23 16:38:31 +11:00
parent 37df58f959
commit f52cb54008
12 changed files with 149 additions and 14 deletions

View File

@ -31,6 +31,10 @@ import useJupiterMints from '../../hooks/useJupiterMints'
import { RouteInfo } from 'types/jupiter'
import useJupiterSwapData from './useJupiterSwapData'
import { Transaction } from '@solana/web3.js'
import useAudio from 'hooks/useAudio'
import { SOUND_SETTINGS_KEY } from 'utils/constants'
import { INITIAL_SOUND_SETTINGS } from 'pages/settings'
import useLocalStorageState from 'hooks/useLocalStorageState'
type JupiterRouteInfoProps = {
amountIn: Decimal
@ -105,6 +109,11 @@ const JupiterRouteInfo = ({
const { mangoTokens } = useJupiterMints()
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
const inputBank = mangoStore((s) => s.swap.inputBank)
const { play } = useAudio('/sounds/swap-success.mp3')
const [soundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS
)
const inputTokenIconUri = useMemo(() => {
return inputTokenInfo ? inputTokenInfo.logoURI : ''
@ -176,6 +185,9 @@ const JupiterRouteInfo = ({
set((s) => {
s.swap.success = true
})
if (soundSettings['swap-success'].active) {
play()
}
notify({
title: 'Transaction confirmed',
type: 'success',

View File

@ -1,13 +1,20 @@
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'
const SwapSuccessParticles = () => {
const { mangoTokens } = useJupiterMints()
const showSwapAnimation = mangoStore((s) => s.swap.success)
const swapTokenMint = mangoStore((s) => s.swap.outputBank)?.mint.toString()
const set = mangoStore((s) => s.set)
const [animationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY,
INITIAL_ANIMATION_SETTINGS
)
const tokenLogo = useMemo(() => {
if (!mangoTokens.length || !swapTokenMint) return ''
@ -27,7 +34,9 @@ const SwapSuccessParticles = () => {
}
}, [showSwapAnimation])
return showSwapAnimation && tokenLogo ? (
return animationSettings['swap-success'].active &&
showSwapAnimation &&
tokenLogo ? (
<Particles
id="tsparticles"
options={{
@ -75,6 +84,9 @@ const SwapSuccessParticles = () => {
opacity: {
value: 1,
},
position: {
y: -1000,
},
},
}}
/>

View File

@ -8,7 +8,7 @@ import isEqual from 'lodash/isEqual'
import usePrevious from '@components/shared/usePrevious'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
import { ORDERBOOK_FLASH_KEY } from 'utils/constants'
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
import { useTranslation } from 'next-i18next'
import Decimal from 'decimal.js'
import OrderbookIcon from '@components/icons/OrderbookIcon'
@ -23,6 +23,7 @@ import {
PerpMarket,
} from '@blockworks-foundation/mango-v4'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { INITIAL_ANIMATION_SETTINGS } from 'pages/settings'
function decodeBookL2(
client: MangoClient,
@ -582,11 +583,14 @@ const OrderbookRow = ({
tickSize: number
}) => {
const element = useRef<HTMLDivElement>(null)
const [showOrderbookFlash] = useLocalStorageState(ORDERBOOK_FLASH_KEY, true)
const [animationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY,
INITIAL_ANIMATION_SETTINGS
)
const flashClassName = side === 'sell' ? 'red-flash' : 'green-flash'
useEffect(() => {
showOrderbookFlash &&
animationSettings['orderbook-flash'].active &&
!element.current?.classList.contains(`${flashClassName}`) &&
element.current?.classList.add(`${flashClassName}`)
const id = setTimeout(

23
hooks/useAudio.ts Normal file
View File

@ -0,0 +1,23 @@
import { useEffect, useState } from 'react'
const useAudio = (url: string) => {
const [audio] = useState(new Audio(url))
const [playing, setPlaying] = useState(false)
const play = () => setPlaying(true)
useEffect(() => {
playing ? audio.play() : audio.pause()
}, [playing])
useEffect(() => {
audio.addEventListener('ended', () => setPlaying(false))
return () => {
audio.removeEventListener('ended', () => setPlaying(false))
}
}, [])
return { playing, play }
}
export default useAudio

View File

@ -7,13 +7,14 @@ import ButtonGroup from '../components/forms/ButtonGroup'
import useLocalStorageState from '../hooks/useLocalStorageState'
import dayjs from 'dayjs'
import {
ANIMATION_SETTINGS_KEY,
NOTIFICATION_POSITION_KEY,
ORDERBOOK_FLASH_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 { useCallback, useEffect, useMemo, useReducer } from 'react'
import { CheckCircleIcon } from '@heroicons/react/20/solid'
import Image from 'next/legacy/image'
@ -61,6 +62,32 @@ const NOTIFICATION_POSITIONS = [
'top-right',
]
interface ReducerItems {
[key: string]: {
active: boolean
}
}
export const INITIAL_ANIMATION_SETTINGS = {
'orderbook-flash': {
active: true,
},
'swap-success': {
active: true,
},
}
export const INITIAL_SOUND_SETTINGS = {
'swap-success': {
active: true,
},
}
const settingsReducer = (state: ReducerItems, name: string) => {
const updatedState = { ...state, [name]: { active: !state[name].active } }
return updatedState
}
const Settings: NextPage = () => {
const { t } = useTranslation(['common', 'settings'])
const { theme, setTheme } = useTheme()
@ -71,10 +98,6 @@ const Settings: NextPage = () => {
)
const router = useRouter()
const { pathname, asPath, query } = router
const [showOrderbookFlash, setShowOrderbookFlash] = useLocalStorageState(
ORDERBOOK_FLASH_KEY,
true
)
const [preferredExplorer, setPreferredExplorer] = useLocalStorageState(
PREFERRED_EXPLORER_KEY,
EXPLORERS[0]
@ -86,6 +109,30 @@ const Settings: NextPage = () => {
const themes = useMemo(() => {
return [t('settings:light'), t('settings:mango'), t('settings:dark')]
}, [t])
const [sounds, soundsDispatch] = useReducer(
settingsReducer,
INITIAL_SOUND_SETTINGS
)
const [, setSoundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS
)
const [animations, animationsDispatch] = useReducer(
settingsReducer,
INITIAL_ANIMATION_SETTINGS
)
const [, setAnimationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY,
INITIAL_ANIMATION_SETTINGS
)
useEffect(() => {
setAnimationSettings(animations)
}, [animations])
useEffect(() => {
setSoundSettings(sounds)
}, [sounds])
const handleLangChange = useCallback(
(l: string) => {
@ -153,11 +200,31 @@ const Settings: NextPage = () => {
/>
</div>
</div>
</div>
<div className="col-span-12 border-b border-th-bkg-3 pt-8 lg:col-span-8 lg:col-start-3">
<h2 className="mb-4 text-base">{t('settings:animations')}</h2>
<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={showOrderbookFlash}
onChange={(checked) => setShowOrderbookFlash(checked)}
checked={animations['orderbook-flash'].active}
onChange={() => animationsDispatch('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={animations['swap-success'].active}
onChange={() => animationsDispatch('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">
<h2 className="mb-4 text-base">{t('settings:sounds')}</h2>
<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={sounds['swap-success'].active}
onChange={() => soundsDispatch('swap-success')}
/>
</div>
</div>

View File

@ -1,4 +1,5 @@
{
"animations": "Animations",
"bottom-left": "Bottom-Left",
"bottom-right": "Bottom-Right",
"buttons": "Buttons",
@ -19,7 +20,9 @@
"solana-explorer": "Solana Explorer",
"solanafm": "SolanaFM",
"solscan": "Solscan",
"sounds": "Sounds",
"spanish": "Español",
"swap-success": "Swap Success",
"theme": "Theme",
"top-left": "Top-Left",
"top-right": "Top-Right",

View File

@ -1,4 +1,5 @@
{
"animations": "Animations",
"bottom-left": "Bottom-Left",
"bottom-right": "Bottom-Right",
"buttons": "Buttons",
@ -19,7 +20,9 @@
"solana-explorer": "Solana Explorer",
"solanafm": "SolanaFM",
"solscan": "Solscan",
"sounds": "Sounds",
"spanish": "Español",
"swap-success": "Swap Success",
"theme": "Theme",
"top-left": "Top-Left",
"top-right": "Top-Right",

View File

@ -1,4 +1,5 @@
{
"animations": "Animations",
"bottom-left": "Bottom-Left",
"bottom-right": "Bottom-Right",
"buttons": "Buttons",
@ -19,7 +20,9 @@
"solana-explorer": "Solana Explorer",
"solanafm": "SolanaFM",
"solscan": "Solscan",
"sounds": "Sounds",
"spanish": "Español",
"swap-success": "Swap Success",
"theme": "Theme",
"top-left": "Top-Left",
"top-right": "Top-Right",

View File

@ -1,4 +1,5 @@
{
"animations": "Animations",
"bottom-left": "Bottom-Left",
"bottom-right": "Bottom-Right",
"buttons": "Buttons",
@ -19,7 +20,9 @@
"solana-explorer": "Solana Explorer",
"solanafm": "SolanaFM",
"solscan": "Solscan",
"sounds": "Sounds",
"spanish": "Español",
"swap-success": "Swap Success",
"theme": "Theme",
"top-left": "Top-Left",
"top-right": "Top-Right",

View File

@ -1,4 +1,5 @@
{
"animations": "Animations",
"bottom-left": "Bottom-Left",
"bottom-right": "Bottom-Right",
"buttons": "Buttons",
@ -19,7 +20,9 @@
"solana-explorer": "Solana Explorer",
"solanafm": "SolanaFM",
"solscan": "Solscan",
"sounds": "Sounds",
"spanish": "Español",
"swap-success": "Swap Success",
"theme": "Theme",
"top-left": "Top-Left",
"top-right": "Top-Right",

Binary file not shown.

View File

@ -19,6 +19,10 @@ export const ONBOARDING_TOUR_KEY = 'showOnboardingTour'
export const PREFERRED_EXPLORER_KEY = 'preferredExplorer'
export const ANIMATION_SETTINGS_KEY = 'animationSettings'
export const SOUND_SETTINGS_KEY = 'soundSettings'
export const SIZE_INPUT_UI_KEY = 'tradeFormUi'
export const PROFILE_CATEGORIES = [
@ -41,8 +45,6 @@ export const DEFAULT_MARKET_NAME = 'SOL/USDC'
export const GRID_LAYOUT_KEY = 'savedLayouts-0.1'
export const ORDERBOOK_FLASH_KEY = 'showOrderbookFlash-0.1'
export const NOTIFICATION_POSITION_KEY = 'notificationPosition'
export const MIN_SOL_BALANCE = 0.04