better sound support and fix settings

This commit is contained in:
saml33 2022-11-23 23:40:38 +11:00
parent f52cb54008
commit 0447fc5d61
14 changed files with 120 additions and 68 deletions

View File

@ -31,10 +31,10 @@ import useJupiterMints from '../../hooks/useJupiterMints'
import { RouteInfo } from 'types/jupiter' 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 useAudio from 'hooks/useAudio'
import { SOUND_SETTINGS_KEY } from 'utils/constants' import { SOUND_SETTINGS_KEY } from 'utils/constants'
import { INITIAL_SOUND_SETTINGS } from 'pages/settings' import { INITIAL_SOUND_SETTINGS } from 'pages/settings'
import useLocalStorageState from 'hooks/useLocalStorageState' import useLocalStorageState from 'hooks/useLocalStorageState'
import { Howl } from 'howler'
type JupiterRouteInfoProps = { type JupiterRouteInfoProps = {
amountIn: Decimal amountIn: Decimal
@ -109,7 +109,10 @@ const JupiterRouteInfo = ({
const { mangoTokens } = useJupiterMints() const { mangoTokens } = useJupiterMints()
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData() const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
const inputBank = mangoStore((s) => s.swap.inputBank) const inputBank = mangoStore((s) => s.swap.inputBank)
const { play } = useAudio('/sounds/swap-success.mp3') const successSound = new Howl({
src: ['/sounds/swap-success.mp3'],
volume: 0.2,
})
const [soundSettings] = useLocalStorageState( const [soundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY, SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS INITIAL_SOUND_SETTINGS
@ -185,13 +188,14 @@ const JupiterRouteInfo = ({
set((s) => { set((s) => {
s.swap.success = true s.swap.success = true
}) })
if (soundSettings['swap-success'].active) { if (soundSettings['swap-success']) {
play() successSound.play()
} }
notify({ notify({
title: 'Transaction confirmed', title: 'Transaction confirmed',
type: 'success', type: 'success',
txid: tx, txid: tx,
noSound: true,
}) })
actions.fetchGroup() actions.fetchGroup()
await actions.reloadMangoAccount() await actions.reloadMangoAccount()

View File

@ -34,9 +34,7 @@ const SwapSuccessParticles = () => {
} }
}, [showSwapAnimation]) }, [showSwapAnimation])
return animationSettings['swap-success'].active && return animationSettings['swap-success'] && showSwapAnimation && tokenLogo ? (
showSwapAnimation &&
tokenLogo ? (
<Particles <Particles
id="tsparticles" id="tsparticles"
options={{ options={{

View File

@ -1,23 +0,0 @@
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

@ -22,12 +22,14 @@
"@solflare-wallet/pfp": "^0.0.6", "@solflare-wallet/pfp": "^0.0.6",
"@tanstack/react-query": "^4.10.1", "@tanstack/react-query": "^4.10.1",
"@tippyjs/react": "^4.2.6", "@tippyjs/react": "^4.2.6",
"@types/howler": "^2.2.7",
"@types/lodash": "^4.14.185", "@types/lodash": "^4.14.185",
"assert": "^2.0.0", "assert": "^2.0.0",
"big.js": "^6.2.1", "big.js": "^6.2.1",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"dayjs": "^1.11.3", "dayjs": "^1.11.3",
"decimal.js": "^10.4.0", "decimal.js": "^10.4.0",
"howler": "^2.2.3",
"html-react-parser": "^3.0.4", "html-react-parser": "^3.0.4",
"immer": "^9.0.12", "immer": "^9.0.12",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View File

@ -14,7 +14,7 @@ import {
SOUND_SETTINGS_KEY, SOUND_SETTINGS_KEY,
} from 'utils/constants' } from 'utils/constants'
import Switch from '@components/forms/Switch' import Switch from '@components/forms/Switch'
import { useCallback, useEffect, useMemo, useReducer } from 'react' import { useCallback, useMemo, useReducer } from 'react'
import { CheckCircleIcon } from '@heroicons/react/20/solid' import { CheckCircleIcon } from '@heroicons/react/20/solid'
import Image from 'next/legacy/image' import Image from 'next/legacy/image'
@ -63,28 +63,22 @@ const NOTIFICATION_POSITIONS = [
] ]
interface ReducerItems { interface ReducerItems {
[key: string]: { [key: string]: boolean
active: boolean
}
} }
export const INITIAL_ANIMATION_SETTINGS = { export const INITIAL_ANIMATION_SETTINGS = {
'orderbook-flash': { 'orderbook-flash': true,
active: true, 'swap-success': true,
},
'swap-success': {
active: true,
},
} }
export const INITIAL_SOUND_SETTINGS = { export const INITIAL_SOUND_SETTINGS = {
'swap-success': { 'swap-success': true,
active: true, 'transaction-success': true,
}, 'transaction-fail': true,
} }
const settingsReducer = (state: ReducerItems, name: string) => { const settingsReducer = (state: ReducerItems, name: string) => {
const updatedState = { ...state, [name]: { active: !state[name].active } } const updatedState = { ...state, [name]: !state[name] }
return updatedState return updatedState
} }
@ -109,30 +103,35 @@ const Settings: NextPage = () => {
const themes = useMemo(() => { const themes = useMemo(() => {
return [t('settings:light'), t('settings:mango'), t('settings:dark')] return [t('settings:light'), t('settings:mango'), t('settings:dark')]
}, [t]) }, [t])
const [sounds, soundsDispatch] = useReducer( const [, soundsDispatch] = useReducer(settingsReducer, INITIAL_SOUND_SETTINGS)
settingsReducer, const [soundSettings, setSoundSettings] = useLocalStorageState(
INITIAL_SOUND_SETTINGS
)
const [, setSoundSettings] = useLocalStorageState(
SOUND_SETTINGS_KEY, SOUND_SETTINGS_KEY,
INITIAL_SOUND_SETTINGS INITIAL_SOUND_SETTINGS
) )
const [animations, animationsDispatch] = useReducer( const [, animationsDispatch] = useReducer(
settingsReducer, settingsReducer,
INITIAL_ANIMATION_SETTINGS INITIAL_ANIMATION_SETTINGS
) )
const [, setAnimationSettings] = useLocalStorageState( const [animationSettings, setAnimationSettings] = useLocalStorageState(
ANIMATION_SETTINGS_KEY, ANIMATION_SETTINGS_KEY,
INITIAL_ANIMATION_SETTINGS INITIAL_ANIMATION_SETTINGS
) )
useEffect(() => { const handleToggleAnimationSetting = (settingName: string) => {
setAnimationSettings(animations) animationsDispatch(settingName)
}, [animations]) setAnimationSettings({
...animationSettings,
[settingName]: !animationSettings[settingName],
})
}
useEffect(() => { const handleToggleSoundSetting = (settingName: string) => {
setSoundSettings(sounds) soundsDispatch(settingName)
}, [sounds]) setSoundSettings({
...soundSettings,
[settingName]: !soundSettings[settingName],
})
}
const handleLangChange = useCallback( const handleLangChange = useCallback(
(l: string) => { (l: string) => {
@ -206,25 +205,39 @@ const Settings: NextPage = () => {
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4"> <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> <p className="mb-2 lg:mb-0">{t('settings:orderbook-flash')}</p>
<Switch <Switch
checked={animations['orderbook-flash'].active} checked={animationSettings['orderbook-flash']}
onChange={() => animationsDispatch('orderbook-flash')} onChange={() => handleToggleAnimationSetting('orderbook-flash')}
/> />
</div> </div>
<div className="flex items-center justify-between border-t border-th-bkg-3 py-4 md:px-4"> <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> <p className="mb-2 lg:mb-0">{t('settings:swap-success')}</p>
<Switch <Switch
checked={animations['swap-success'].active} checked={animationSettings['swap-success']}
onChange={() => animationsDispatch('swap-success')} onChange={() => handleToggleAnimationSetting('swap-success')}
/> />
</div> </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="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> <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: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"> <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> <p className="mb-2 lg:mb-0">{t('settings:swap-success')}</p>
<Switch <Switch
checked={sounds['swap-success'].active} checked={soundSettings['swap-success']}
onChange={() => soundsDispatch('swap-success')} onChange={() => handleToggleSoundSetting('swap-success')}
/> />
</div> </div>
</div> </div>

View File

@ -23,8 +23,10 @@
"sounds": "Sounds", "sounds": "Sounds",
"spanish": "Español", "spanish": "Español",
"swap-success": "Swap Success", "swap-success": "Swap Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"theme": "Theme", "theme": "Theme",
"top-left": "Top-Left", "top-left": "Top-Left",
"top-right": "Top-Right", "top-right": "Top-Right",
"swap-trade-size-selector": "Swap/Trade Size Selector" "transaction-fail": "Transaction Fail",
"transaction-success": "Transaction Success"
} }

View File

@ -23,8 +23,10 @@
"sounds": "Sounds", "sounds": "Sounds",
"spanish": "Español", "spanish": "Español",
"swap-success": "Swap Success", "swap-success": "Swap Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"theme": "Theme", "theme": "Theme",
"top-left": "Top-Left", "top-left": "Top-Left",
"top-right": "Top-Right", "top-right": "Top-Right",
"swap-trade-size-selector": "Swap/Trade Size Selector" "transaction-fail": "Transaction Fail",
"transaction-success": "Transaction Success"
} }

View File

@ -23,8 +23,10 @@
"sounds": "Sounds", "sounds": "Sounds",
"spanish": "Español", "spanish": "Español",
"swap-success": "Swap Success", "swap-success": "Swap Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"theme": "Theme", "theme": "Theme",
"top-left": "Top-Left", "top-left": "Top-Left",
"top-right": "Top-Right", "top-right": "Top-Right",
"swap-trade-size-selector": "Swap/Trade Size Selector" "transaction-fail": "Transaction Fail",
"transaction-success": "Transaction Success"
} }

View File

@ -23,8 +23,10 @@
"sounds": "Sounds", "sounds": "Sounds",
"spanish": "Español", "spanish": "Español",
"swap-success": "Swap Success", "swap-success": "Swap Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"theme": "Theme", "theme": "Theme",
"top-left": "Top-Left", "top-left": "Top-Left",
"top-right": "Top-Right", "top-right": "Top-Right",
"swap-trade-size-selector": "Swap/Trade Size Selector" "transaction-fail": "Transaction Fail",
"transaction-success": "Transaction Success"
} }

View File

@ -23,8 +23,10 @@
"sounds": "Sounds", "sounds": "Sounds",
"spanish": "Español", "spanish": "Español",
"swap-success": "Swap Success", "swap-success": "Swap Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"theme": "Theme", "theme": "Theme",
"top-left": "Top-Left", "top-left": "Top-Left",
"top-right": "Top-Right", "top-right": "Top-Right",
"swap-trade-size-selector": "Swap/Trade Size Selector" "transaction-fail": "Transaction Fail",
"transaction-success": "Transaction Success"
} }

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,7 @@
import mangoStore from '@store/mangoStore' 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 = { export type Notification = {
type: 'success' | 'info' | 'error' | 'confirm' type: 'success' | 'info' | 'error' | 'confirm'
@ -9,16 +12,51 @@ export type Notification = {
id: number id: number
} }
interface ReducerItems {
[key: string]: {
active: boolean
}
}
export function notify(newNotification: { export function notify(newNotification: {
type?: 'success' | 'info' | 'error' | 'confirm' type?: 'success' | 'info' | 'error' | 'confirm'
title: string title: string
description?: string description?: string
txid?: string txid?: string
noSound?: boolean
}) { }) {
const setMangoStore = mangoStore.getState().set const setMangoStore = mangoStore.getState().set
const notifications = mangoStore.getState().notifications const notifications = mangoStore.getState().notifications
const lastId = mangoStore.getState().notificationIdCounter const lastId = mangoStore.getState().notificationIdCounter
const newId = lastId + 1 const newId = lastId + 1
const successSound = new Howl({
src: ['/sounds/transaction-success.mp3'],
volume: 0.5,
})
const failSound = new Howl({
src: ['/sounds/transaction-fail.mp3'],
volume: 0.2,
})
const savedSoundSettings = localStorage.getItem(SOUND_SETTINGS_KEY)
const soundSettings = savedSoundSettings
? JSON.parse(savedSoundSettings)
: INITIAL_SOUND_SETTINGS
if (newNotification.type && !newNotification.noSound) {
switch (newNotification.type) {
case 'success': {
if (soundSettings['transaction-success']) {
successSound.play()
}
break
}
case 'error': {
if (soundSettings['transaction-fail']) {
failSound.play()
}
}
}
}
const newNotif: Notification = { const newNotif: Notification = {
id: newId, id: newId,

View File

@ -1516,6 +1516,11 @@
"@types/react" "*" "@types/react" "*"
hoist-non-react-statics "^3.3.0" hoist-non-react-statics "^3.3.0"
"@types/howler@^2.2.7":
version "2.2.7"
resolved "https://registry.yarnpkg.com/@types/howler/-/howler-2.2.7.tgz#5acfbed57f9e1d99b8dabe1b824729e1c1ea1fae"
integrity sha512-PEZldwZqJJw1PWRTpupyC7ajVTZA8aHd8nB/Y0n6zRZi5u8ktYDntsHj13ltEiBRqWwF06pASxBEvCTxniG8eA==
"@types/json-schema@^7.0.9": "@types/json-schema@^7.0.9":
version "7.0.11" version "7.0.11"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
@ -3910,6 +3915,11 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
dependencies: dependencies:
react-is "^16.7.0" react-is "^16.7.0"
howler@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/howler/-/howler-2.2.3.tgz#a2eff9b08b586798e7a2ee17a602a90df28715da"
integrity sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==
html-dom-parser@3.1.2: html-dom-parser@3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-3.1.2.tgz#c137c42df80e17d185ff35a806925d96cc73f408" resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-3.1.2.tgz#c137c42df80e17d185ff35a806925d96cc73f408"