diff --git a/web/app/components/base/audio-btn/index.tsx b/web/app/components/base/audio-btn/index.tsx index 676b85414b..8aee0b5f51 100644 --- a/web/app/components/base/audio-btn/index.tsx +++ b/web/app/components/base/audio-btn/index.tsx @@ -1,11 +1,12 @@ 'use client' -import { useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { t } from 'i18next' import { useParams, usePathname } from 'next/navigation' import s from './style.module.css' import Tooltip from '@/app/components/base/tooltip' import { randomString } from '@/utils' import { textToAudio } from '@/service/share' +import Loading from '@/app/components/base/loading' type AudioBtnProps = { value: string @@ -14,6 +15,8 @@ type AudioBtnProps = { isAudition?: boolean } +type AudioState = 'initial' | 'loading' | 'playing' | 'paused' | 'ended' + const AudioBtn = ({ value, voice, @@ -21,9 +24,8 @@ const AudioBtn = ({ isAudition, }: AudioBtnProps) => { const audioRef = useRef(null) - const [isPlaying, setIsPlaying] = useState(false) - const [isPause, setPause] = useState(false) - const [hasEnded, setHasEnded] = useState(false) + const [audioState, setAudioState] = useState('initial') + const selector = useRef(`play-tooltip-${randomString(4)}`) const params = useParams() const pathname = usePathname() @@ -34,9 +36,11 @@ const AudioBtn = ({ return '' } - const playAudio = async () => { + const loadAudio = async () => { const formData = new FormData() if (value !== '') { + setAudioState('loading') + formData.append('text', removeCodeBlocks(value)) formData.append('voice', removeCodeBlocks(voice)) @@ -59,67 +63,80 @@ const AudioBtn = ({ const blob_bytes = Buffer.from(audioResponse.data, 'latin1') const blob = new Blob([blob_bytes], { type: 'audio/wav' }) const audioUrl = URL.createObjectURL(blob) - const audio = new Audio(audioUrl) - audioRef.current = audio - audio.play().then(() => {}).catch(() => { - setIsPlaying(false) - URL.revokeObjectURL(audioUrl) - }) - audio.onended = () => { - setHasEnded(true) - setIsPlaying(false) - } + audioRef.current!.src = audioUrl } catch (error) { - setIsPlaying(false) + setAudioState('initial') console.error('Error playing audio:', error) } } } - const togglePlayPause = () => { + + const handleToggle = () => { + if (audioState === 'initial') + loadAudio() if (audioRef.current) { - if (isPlaying) { - if (!hasEnded) { - setPause(false) - audioRef.current.play() - } - if (!isPause) { - setPause(true) - audioRef.current.pause() - } + if (audioState === 'playing') { + audioRef.current.pause() + setAudioState('paused') } - else if (!isPlaying) { - if (isPause) { - setPause(false) - audioRef.current.play() - } - else { - setHasEnded(false) - playAudio().then() - } + else if (audioState === 'paused' || audioState === 'ended') { + audioRef.current.play() + setAudioState('playing') } - 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 ( -
+
-
-
-
+ onClick={handleToggle}> + {audioState === 'loading' &&
} + {audioState !== 'loading' &&
} +
+
) } diff --git a/web/app/components/base/audio-btn/style.module.css b/web/app/components/base/audio-btn/style.module.css index 7e3175aa13..b8a4da6b68 100644 --- a/web/app/components/base/audio-btn/style.module.css +++ b/web/app/components/base/audio-btn/style.module.css @@ -7,4 +7,4 @@ background-image: url(~@/app/components/develop/secret-key/assets/pause.svg); background-position: center; background-repeat: no-repeat; -} +} \ No newline at end of file diff --git a/web/i18n/en-US/app-api.ts b/web/i18n/en-US/app-api.ts index 31d9a9f477..f36708c1d0 100644 --- a/web/i18n/en-US/app-api.ts +++ b/web/i18n/en-US/app-api.ts @@ -9,6 +9,7 @@ const translation = { play: 'Play', pause: 'Pause', playing: 'Playing', + loading: 'Loading', merMaind: { rerender: 'Redo Rerender', }, diff --git a/web/i18n/zh-Hans/app-api.ts b/web/i18n/zh-Hans/app-api.ts index bc59186c1a..f8f6ab7083 100644 --- a/web/i18n/zh-Hans/app-api.ts +++ b/web/i18n/zh-Hans/app-api.ts @@ -9,6 +9,7 @@ const translation = { play: '播放', pause: '暂停', playing: '播放中', + loading: '加载中', merMaind: { rerender: '重新渲染', },