feat(video): add error handling and loading state for video creation

handle video creation errors by showing toast notifications and prevent multiple submissions by adding a loading state
This commit is contained in:
icarus 2025-10-12 04:21:54 +08:00
parent 5776512bf6
commit d80eac2fbe
2 changed files with 34 additions and 21 deletions

View File

@ -4649,6 +4649,7 @@
}, },
"video": { "video": {
"error": { "error": {
"create": "Failed to create video",
"invalid": "Invalid video", "invalid": "Invalid video",
"load": { "load": {
"message": "Failed to load the video", "message": "Failed to load the video",

View File

@ -4,6 +4,7 @@ import { useAddOpenAIVideo } from '@renderer/hooks/video/useOpenAIVideos'
import { createVideo } from '@renderer/services/ApiService' import { createVideo } from '@renderer/services/ApiService'
import { Provider } from '@renderer/types' import { Provider } from '@renderer/types'
import { Video } from '@renderer/types/video' import { Video } from '@renderer/types/video'
import { getErrorMessage } from '@renderer/utils'
import { ArrowUp } from 'lucide-react' import { ArrowUp } from 'lucide-react'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -20,25 +21,34 @@ const logger = loggerService.withContext('VideoPanel')
export const VideoPanel = ({ provider, video }: VideoPanelProps) => { export const VideoPanel = ({ provider, video }: VideoPanelProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const [prompt, setPrompt] = useState('') const [prompt, setPrompt] = useState('')
const [isProcessing, setIsProcessing] = useState(false)
const addOpenAIVideo = useAddOpenAIVideo(provider.id) const addOpenAIVideo = useAddOpenAIVideo(provider.id)
const sendRequest = useCallback(async () => { const handleCreateVideo = useCallback(async () => {
const result = await createVideo({ if (isProcessing) return
type: 'openai', setIsProcessing(true)
params: { try {
prompt const result = await createVideo({
}, type: 'openai',
provider params: {
}) prompt
const video = result.video },
switch (result.type) { provider
case 'openai': })
addOpenAIVideo(video) const video = result.video
break switch (result.type) {
default: case 'openai':
logger.error(`Invalid video type ${result.type}.`) addOpenAIVideo(video)
break
default:
logger.error(`Invalid video type ${result.type}.`)
}
} catch (e) {
window.toast.error({ title: t('video.error.create'), description: getErrorMessage(e), timeout: 5000 })
} finally {
setIsProcessing(false)
} }
}, [addOpenAIVideo, prompt, provider]) }, [addOpenAIVideo, isProcessing, prompt, provider, t])
return ( return (
<div className="flex flex-1 flex-col p-2"> <div className="flex flex-1 flex-col p-2">
@ -54,21 +64,23 @@ export const VideoPanel = ({ provider, video }: VideoPanelProps) => {
value={prompt} value={prompt}
onValueChange={setPrompt} onValueChange={setPrompt}
isClearable isClearable
isDisabled={isProcessing}
classNames={{ inputWrapper: 'pb-8' }} classNames={{ inputWrapper: 'pb-8' }}
onKeyDown={(e: React.KeyboardEvent) => { onKeyDown={(e: React.KeyboardEvent) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
e.stopPropagation() e.preventDefault()
sendRequest() handleCreateVideo()
} }
}} }}
/> />
<Button <Button
color="primary" color="primary"
radius="full" radius="full"
startContent={<ArrowUp size={16} className="text-primary-foreground" />}
isIconOnly isIconOnly
className="absolute right-1 bottom-1 h-6 w-6 min-w-0" isLoading={isProcessing}
/> className="absolute right-2 bottom-2 h-6 w-6 min-w-0">
<ArrowUp size={16} className="text-primary-foreground" />
</Button>
</div> </div>
</div> </div>
) )