refactor: Simplify AudioPlayer component by removing mobile detection logic and streamline download handling; enhance Home layout for better responsiveness

This commit is contained in:
2026-02-05 23:30:07 +08:00
parent d01fbe8171
commit f9eaf88807
2 changed files with 45 additions and 70 deletions

View File

@@ -12,35 +12,16 @@ interface AudioPlayerProps {
jobId: number jobId: number
} }
const isMobileDevice = () => {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
}
const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => { const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => {
const { t } = useTranslation('common') const { t } = useTranslation('common')
const [blobUrl, setBlobUrl] = useState<string>('') const [blobUrl, setBlobUrl] = useState<string>('')
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [loadError, setLoadError] = useState<string | null>(null) const [loadError, setLoadError] = useState<string | null>(null)
const [useMobileMode, setUseMobileMode] = useState(false)
const previousAudioUrlRef = useRef<string>('') const previousAudioUrlRef = useRef<string>('')
useEffect(() => {
setUseMobileMode(isMobileDevice())
}, [])
useEffect(() => { useEffect(() => {
if (!audioUrl || audioUrl === previousAudioUrlRef.current) return if (!audioUrl || audioUrl === previousAudioUrlRef.current) return
if (useMobileMode) {
const token = localStorage.getItem('token')
const separator = audioUrl.includes('?') ? '&' : '?'
const urlWithToken = token ? `${audioUrl}${separator}token=${token}` : audioUrl
setBlobUrl(urlWithToken)
previousAudioUrlRef.current = audioUrl
setIsLoading(false)
return
}
let active = true let active = true
const prevBlobUrl = blobUrl const prevBlobUrl = blobUrl
@@ -76,7 +57,7 @@ const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => {
return () => { return () => {
active = false active = false
} }
}, [audioUrl, useMobileMode]) }, [audioUrl, blobUrl, t])
useEffect(() => { useEffect(() => {
return () => { return () => {
@@ -85,15 +66,11 @@ const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => {
}, []) }, [])
const handleDownload = useCallback(() => { const handleDownload = useCallback(() => {
if (useMobileMode) { const link = document.createElement('a')
window.open(blobUrl || audioUrl, '_blank') link.href = blobUrl || audioUrl
} else { link.download = `tts-${jobId}-${Date.now()}.wav`
const link = document.createElement('a') link.click()
link.href = blobUrl || audioUrl }, [blobUrl, audioUrl, jobId])
link.download = `tts-${jobId}-${Date.now()}.wav`
link.click()
}
}, [blobUrl, audioUrl, jobId, useMobileMode])
if (isLoading) { if (isLoading) {
return ( return (

View File

@@ -1,7 +1,7 @@
import { useState, useRef, lazy, Suspense, useEffect } from 'react' import { useState, useRef, lazy, Suspense, useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Navbar } from '@/components/Navbar' import { Navbar } from '@/components/Navbar'
import { Card, CardContent, CardHeader } from '@/components/ui/card' import { Card, CardContent } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { User, Palette, Copy } from 'lucide-react' import { User, Palette, Copy } from 'lucide-react'
import type { CustomVoiceFormHandle } from '@/components/tts/CustomVoiceForm' import type { CustomVoiceFormHandle } from '@/components/tts/CustomVoiceForm'
@@ -47,49 +47,47 @@ function Home() {
onOpenChange={setSidebarOpen} onOpenChange={setSidebarOpen}
/> />
<main className="flex-1 overflow-y-auto container mx-auto p-3 md:p-6 max-w-[800px] md:max-w-[700px]"> <main className="flex-1 overflow-y-auto flex items-start md:items-center justify-center">
<Card> <div className="w-full container mx-auto p-3 md:p-6 max-w-[800px] md:max-w-[700px]">
<CardHeader> <Tabs value={currentTab} onValueChange={setCurrentTab}>
<Tabs value={currentTab} onValueChange={setCurrentTab}> <TabsList className="grid w-full grid-cols-3 h-9 mb-3">
<TabsList className="grid w-full grid-cols-3 h-9"> <TabsTrigger value="custom-voice" variant="default">
<TabsTrigger value="custom-voice" variant="default"> <User className="h-4 w-4 md:mr-2" />
<User className="h-4 w-4 md:mr-2" /> <span className="hidden md:inline">{t('customVoiceTab')}</span>
<span className="hidden md:inline">{t('customVoiceTab')}</span> </TabsTrigger>
</TabsTrigger> <TabsTrigger value="voice-design" variant="secondary">
<TabsTrigger value="voice-design" variant="secondary"> <Palette className="h-4 w-4 md:mr-2" />
<Palette className="h-4 w-4 md:mr-2" /> <span className="hidden md:inline">{t('voiceDesignTab')}</span>
<span className="hidden md:inline">{t('voiceDesignTab')}</span> </TabsTrigger>
</TabsTrigger> <TabsTrigger value="voice-clone" variant="outline">
<TabsTrigger value="voice-clone" variant="outline"> <Copy className="h-4 w-4 md:mr-2" />
<Copy className="h-4 w-4 md:mr-2" /> <span className="hidden md:inline">{t('voiceCloneTab')}</span>
<span className="hidden md:inline">{t('voiceCloneTab')}</span> </TabsTrigger>
</TabsTrigger> </TabsList>
</TabsList>
</Tabs>
</CardHeader>
<CardContent className="pt-0 px-3 md:px-6"> <Card>
<Tabs value={currentTab}> <CardContent className="pt-6 px-3 md:px-6 pb-6">
<TabsContent value="custom-voice" className="mt-0"> <TabsContent value="custom-voice" className="mt-0">
<Suspense fallback={<FormSkeleton />}> <Suspense fallback={<FormSkeleton />}>
<CustomVoiceForm ref={customVoiceFormRef} /> <CustomVoiceForm ref={customVoiceFormRef} />
</Suspense> </Suspense>
</TabsContent> </TabsContent>
<TabsContent value="voice-design" className="mt-0"> <TabsContent value="voice-design" className="mt-0">
<Suspense fallback={<FormSkeleton />}> <Suspense fallback={<FormSkeleton />}>
<VoiceDesignForm ref={voiceDesignFormRef} /> <VoiceDesignForm ref={voiceDesignFormRef} />
</Suspense> </Suspense>
</TabsContent> </TabsContent>
<TabsContent value="voice-clone" className="mt-0"> <TabsContent value="voice-clone" className="mt-0">
<Suspense fallback={<FormSkeleton />}> <Suspense fallback={<FormSkeleton />}>
<VoiceCloneForm /> <VoiceCloneForm />
</Suspense> </Suspense>
</TabsContent> </TabsContent>
</Tabs> </CardContent>
</CardContent> </Card>
</Card> </Tabs>
</div>
</main> </main>
</div> </div>
</div> </div>