mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-12 07:39:00 +08:00
fixed the issue of missing cleanup function in the AudioBtn component (#3133)
This commit is contained in:
parent
269432a5e6
commit
29918c498c
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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',
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@ const translation = {
|
|||||||
play: '播放',
|
play: '播放',
|
||||||
pause: '暂停',
|
pause: '暂停',
|
||||||
playing: '播放中',
|
playing: '播放中',
|
||||||
|
loading: '加载中',
|
||||||
merMaind: {
|
merMaind: {
|
||||||
rerender: '重新渲染',
|
rerender: '重新渲染',
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user