feat: add soundLoop (#244) (#246)

This commit is contained in:
flynn-eye
2022-08-17 20:06:32 +08:00
committed by GitHub
parent 95b32dd0fd
commit 0d782aa508
5 changed files with 40 additions and 3 deletions

View File

@@ -18,17 +18,23 @@ function generateWordSoundSrc(word: string, pronunciation: Exclude<Pronunciation
}
export default function usePronunciationSound(word: string) {
const { pronunciation } = useAppState()
const { pronunciation, soundLoop } = useAppState()
const [isPlaying, setIsPlaying] = useState(false)
const [play, { stop, sound }] = useSound(generateWordSoundSrc(word, pronunciation as Exclude<PronunciationType, false>), {
html5: true,
format: ['mp3'],
loop: soundLoop,
} as HookOptions)
useEffect(() => {
if (!sound) return
sound.loop(soundLoop)
return () => {}
}, [soundLoop])
useEffect(() => {
if (!sound) return
const unListens: Array<() => void> = []
unListens.push(addHowlListener(sound, 'play', () => setIsPlaying(true)))
@@ -37,6 +43,7 @@ export default function usePronunciationSound(word: string) {
unListens.push(addHowlListener(sound, 'playerror', () => setIsPlaying(false)))
return () => {
setIsPlaying(false)
unListens.forEach((unListen) => unListen())
;(sound as Howl).unload()
}

View File

@@ -18,6 +18,7 @@ import {
faExclamation,
faRandom,
faRepeat,
faRotate,
} from '@fortawesome/free-solid-svg-icons'
import { faGithub } from '@fortawesome/free-brands-svg-icons'
@@ -41,4 +42,5 @@ library.add(
faExclamation,
faRandom,
faRepeat,
faRotate,
)

View File

@@ -83,6 +83,17 @@ const Switcher: React.FC<SwitcherPropsType> = ({ state, dispatch }) => {
<FontAwesomeIcon icon="repeat" fixedWidth />
</button>
</Tooltip>
<Tooltip content="开关循环播放单词发音">
<button
className={`${state.soundLoop ? 'text-indigo-400' : 'text-gray-400'} text-lg focus:outline-none`}
onClick={(e) => {
dispatch('soundLoop')
e.currentTarget.blur()
}}
>
<FontAwesomeIcon icon={'rotate'} fixedWidth />
</button>
</Tooltip>
<Tooltip content="开关键盘声音Ctrl + M">
<button
className={`${state.sound ? 'text-indigo-400' : 'text-gray-400'} text-lg focus:outline-none`}

View File

@@ -1,5 +1,5 @@
import { useState } from 'react'
import { useSetSoundState, useDarkMode, useRandomState, useSetLoopState, usePhoneticState } from 'store/AppState'
import { useSetSoundState, useDarkMode, useRandomState, useSetLoopState, usePhoneticState, useSetSoundLoopState } from 'store/AppState'
export type SwitcherStateType = {
phonetic: boolean
@@ -8,6 +8,7 @@ export type SwitcherStateType = {
random: boolean
darkMode: boolean
loop: boolean
soundLoop: boolean
}
export type SwitcherDispatchType = (type: string, newStatus?: boolean) => void
@@ -24,6 +25,7 @@ const useSwitcherState = (initialState: {
const [sound, setSound] = useSetSoundState()
const [random, setRandom] = useRandomState()
const [darkMode, setDarkMode] = useDarkMode()
const [soundLoop, setSoundLoop] = useSetSoundLoopState()
const dispatch: SwitcherDispatchType = (type, newStatus) => {
switch (type) {
@@ -44,9 +46,13 @@ const useSwitcherState = (initialState: {
break
case 'loop':
setLoop(newStatus ?? !loop)
break
case 'soundLoop':
setSoundLoop(newStatus ?? !soundLoop)
break
}
}
return [{ phonetic, wordVisible, sound, random, darkMode, loop }, dispatch]
return [{ phonetic, wordVisible, sound, random, darkMode, loop, soundLoop }, dispatch]
}
export default useSwitcherState

View File

@@ -44,6 +44,10 @@ export type AppState = {
* Whether dark mode is enabled
*/
darkMode: boolean
/**
* loop play sound until word spells right
*/
soundLoop: boolean
}
export type AppStateData = {
@@ -79,6 +83,12 @@ export function useSetLoopState(): [status: boolean, setLoop: (state: boolean) =
return [state.loop, setLoop]
}
export function useSetSoundLoopState(): [status: boolean, setLoop: (state: boolean) => void] {
const { state, dispatch } = useContext(AppStateContext)
const setSoundLoop = useCallback((soundLoop: boolean) => dispatch({ ...state, soundLoop }), [state, dispatch])
return [state.soundLoop, setSoundLoop]
}
export function usePhoneticState(): [status: boolean, setPhonetic: (state: boolean) => void] {
const { state, dispatch } = useContext(AppStateContext)
const setPhonetic = useCallback((phonetic: boolean) => dispatch({ ...state, phonetic }), [state, dispatch])
@@ -151,6 +161,7 @@ const defaultState: AppState = {
loop: false,
phonetic: true,
darkMode: window.matchMedia('(prefers-color-scheme: dark)').matches,
soundLoop: false,
}
export const AppStateProvider: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {