fixed the issue of missing cleanup function in the AudioBtn component (#3133)

This commit is contained in:
legao 2024-04-09 07:10:58 +00:00 committed by GitHub
parent 269432a5e6
commit 29918c498c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 67 additions and 48 deletions

View File

@ -1,11 +1,12 @@
'use client' 'use client'
import { useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { t } from 'i18next' import { t } from 'i18next'
import { useParams, usePathname } from 'next/navigation' import { useParams, usePathname } from 'next/navigation'
import s from './style.module.css' import s from './style.module.css'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import { randomString } from '@/utils' import { randomString } from '@/utils'
import { textToAudio } from '@/service/share' import { textToAudio } from '@/service/share'
import Loading from '@/app/components/base/loading'
type AudioBtnProps = { type AudioBtnProps = {
value: string value: string
@ -14,6 +15,8 @@ type AudioBtnProps = {
isAudition?: boolean isAudition?: boolean
} }
type AudioState = 'initial' | 'loading' | 'playing' | 'paused' | 'ended'
const AudioBtn = ({ const AudioBtn = ({
value, value,
voice, voice,
@ -21,9 +24,8 @@ const AudioBtn = ({
isAudition, isAudition,
}: AudioBtnProps) => { }: AudioBtnProps) => {
const audioRef = useRef<HTMLAudioElement | null>(null) const audioRef = useRef<HTMLAudioElement | null>(null)
const [isPlaying, setIsPlaying] = useState(false) const [audioState, setAudioState] = useState<AudioState>('initial')
const [isPause, setPause] = useState(false)
const [hasEnded, setHasEnded] = useState(false)
const selector = useRef(`play-tooltip-${randomString(4)}`) const selector = useRef(`play-tooltip-${randomString(4)}`)
const params = useParams() const params = useParams()
const pathname = usePathname() const pathname = usePathname()
@ -34,9 +36,11 @@ const AudioBtn = ({
return '' return ''
} }
const playAudio = async () => { const loadAudio = async () => {
const formData = new FormData() const formData = new FormData()
if (value !== '') { if (value !== '') {
setAudioState('loading')
formData.append('text', removeCodeBlocks(value)) formData.append('text', removeCodeBlocks(value))
formData.append('voice', removeCodeBlocks(voice)) formData.append('voice', removeCodeBlocks(voice))
@ -59,67 +63,80 @@ const AudioBtn = ({
const blob_bytes = Buffer.from(audioResponse.data, 'latin1') const blob_bytes = Buffer.from(audioResponse.data, 'latin1')
const blob = new Blob([blob_bytes], { type: 'audio/wav' }) const blob = new Blob([blob_bytes], { type: 'audio/wav' })
const audioUrl = URL.createObjectURL(blob) const audioUrl = URL.createObjectURL(blob)
const audio = new Audio(audioUrl) audioRef.current!.src = audioUrl
audioRef.current = audio
audio.play().then(() => {}).catch(() => {
setIsPlaying(false)
URL.revokeObjectURL(audioUrl)
})
audio.onended = () => {
setHasEnded(true)
setIsPlaying(false)
}
} }
catch (error) { catch (error) {
setIsPlaying(false) setAudioState('initial')
console.error('Error playing audio:', error) console.error('Error playing audio:', error)
} }
} }
} }
const togglePlayPause = () => {
const handleToggle = () => {
if (audioState === 'initial')
loadAudio()
if (audioRef.current) { if (audioRef.current) {
if (isPlaying) { if (audioState === 'playing') {
if (!hasEnded) { audioRef.current.pause()
setPause(false) setAudioState('paused')
audioRef.current.play()
}
if (!isPause) {
setPause(true)
audioRef.current.pause()
}
} }
else if (!isPlaying) { else if (audioState === 'paused' || audioState === 'ended') {
if (isPause) { audioRef.current.play()
setPause(false) setAudioState('playing')
audioRef.current.play()
}
else {
setHasEnded(false)
playAudio().then()
}
} }
setIsPlaying(prevIsPlaying => !prevIsPlaying)
}
else {
setIsPlaying(true)
if (!isPlaying)
playAudio().then()
} }
} }
useEffect(() => {
const currentAudio = audioRef.current
const handleLoading = () => {
setAudioState('loading')
}
const handlePlay = () => {
currentAudio?.play()
setAudioState('playing')
}
const handleEnded = () => {
setAudioState('ended')
}
currentAudio?.addEventListener('progress', handleLoading)
currentAudio?.addEventListener('canplaythrough', handlePlay)
currentAudio?.addEventListener('ended', handleEnded)
return () => {
if (currentAudio) {
currentAudio.removeEventListener('progress', handleLoading)
currentAudio.removeEventListener('canplaythrough', handlePlay)
currentAudio.removeEventListener('ended', handleEnded)
URL.revokeObjectURL(currentAudio.src)
currentAudio.src = ''
}
}
}, [])
const tooltipContent = {
initial: t('appApi.play'),
ended: t('appApi.play'),
paused: t('appApi.pause'),
playing: t('appApi.playing'),
loading: t('appApi.loading'),
}[audioState]
return ( return (
<div className={`${(isPlaying && !hasEnded) ? 'mr-1' : className}`}> <div className={`${(audioState === 'loading' || audioState === 'playing') ? 'mr-1' : className}`}>
<Tooltip <Tooltip
selector={selector.current} selector={selector.current}
content={(!isPause ? ((isPlaying && !hasEnded) ? t('appApi.playing') : t('appApi.play')) : t('appApi.pause')) as string} content={tooltipContent}
className='z-10' className='z-10'
> >
<div <button
disabled={audioState === 'loading'}
className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || '!p-0 rounded-md bg-white'}`} className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || '!p-0 rounded-md bg-white'}`}
onClick={togglePlayPause}> onClick={handleToggle}>
<div className={`w-6 h-6 rounded-md ${!isAudition ? 'w-4 h-4 hover:bg-gray-50' : 'hover:bg-gray-50'} ${(isPlaying && !hasEnded) ? s.pauseIcon : s.playIcon}`}></div> {audioState === 'loading' && <div className='w-6 h-6 rounded-md flex items-center justify-center p-2'><Loading /></div>}
</div> {audioState !== 'loading' && <div className={`w-6 h-6 rounded-md ${!isAudition ? 'w-4 h-4 hover:bg-gray-50' : 'hover:bg-gray-50'} ${(audioState === 'playing') ? s.pauseIcon : s.playIcon}`}></div>}
</button>
</Tooltip> </Tooltip>
<audio ref={audioRef} src='' className='hidden' />
</div> </div>
) )
} }

View File

@ -9,6 +9,7 @@ const translation = {
play: 'Play', play: 'Play',
pause: 'Pause', pause: 'Pause',
playing: 'Playing', playing: 'Playing',
loading: 'Loading',
merMaind: { merMaind: {
rerender: 'Redo Rerender', rerender: 'Redo Rerender',
}, },

View File

@ -9,6 +9,7 @@ const translation = {
play: '播放', play: '播放',
pause: '暂停', pause: '暂停',
playing: '播放中', playing: '播放中',
loading: '加载中',
merMaind: { merMaind: {
rerender: '重新渲染', rerender: '重新渲染',
}, },