mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 13:59:28 +08:00
refactor(video): extract video status components and fix type names
Extract inline video status rendering logic into separate components for better maintainability. Also fix inconsistent type naming (Videodownloaded -> VideoDownloaded, VideoFailed -> VideoFailedBase) and properly type VideoFailed as OpenAIVideoFailed.
This commit is contained in:
parent
1e1bfafb88
commit
f54e583f34
@ -9,9 +9,9 @@ import {
|
|||||||
Spinner,
|
Spinner,
|
||||||
useDisclosure
|
useDisclosure
|
||||||
} from '@heroui/react'
|
} from '@heroui/react'
|
||||||
import { Video } from '@renderer/types/video'
|
import { Video, VideoFailed } from '@renderer/types/video'
|
||||||
import { CheckCircleIcon, CircleXIcon } from 'lucide-react'
|
import { CheckCircleIcon, CircleXIcon } from 'lucide-react'
|
||||||
import { useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
export interface VideoViewerProps {
|
export interface VideoViewerProps {
|
||||||
@ -21,49 +21,14 @@ export interface VideoViewerProps {
|
|||||||
export const VideoViewer = ({ video }: VideoViewerProps) => {
|
export const VideoViewer = ({ video }: VideoViewerProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [loadSuccess, setLoadSuccess] = useState<boolean | undefined>(undefined)
|
const [loadSuccess, setLoadSuccess] = useState<boolean | undefined>(undefined)
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex h-full w-full items-center justify-center rounded-2xl bg-foreground-200">
|
<div className="flex h-full w-full items-center justify-center rounded-2xl bg-foreground-200">
|
||||||
{video === undefined && t('video.undefined')}
|
{video === undefined && t('video.undefined')}
|
||||||
{video && video.status === 'queued' && (
|
{video && video.status === 'queued' && <QueuedVideo />}
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl">
|
{video && video.status === 'in_progress' && <InProgressVideo progress={video.progress} />}
|
||||||
<Spinner variant="dots" />
|
{video && video.status === 'completed' && <CompletedVideo />}
|
||||||
<span>{t('video.status.queued')}</span>
|
{video && video.status === 'downloading' && <DownloadingVideo progress={video.progress} />}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{video && video.status === 'in_progress' && (
|
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl">
|
|
||||||
<Progress
|
|
||||||
label={t('video.status.in_progress')}
|
|
||||||
aria-label={t('video.status.in_progress')}
|
|
||||||
className="max-w-md"
|
|
||||||
color="primary"
|
|
||||||
showValueLabel={true}
|
|
||||||
size="md"
|
|
||||||
value={video.progress}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{video && video.status === 'completed' && (
|
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl bg-success-200">
|
|
||||||
<CheckCircleIcon size={64} className="text-success" />
|
|
||||||
<span className="font-bold text-2xl">{t('video.status.completed')}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{video && video.status === 'downloading' && (
|
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl">
|
|
||||||
<Progress
|
|
||||||
label={t('video.status.downloading')}
|
|
||||||
aria-label={t('video.status.downloading')}
|
|
||||||
className="max-w-md"
|
|
||||||
color="primary"
|
|
||||||
showValueLabel={true}
|
|
||||||
size="md"
|
|
||||||
value={video.progress}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{video && video.status === 'downloaded' && loadSuccess !== false && (
|
{video && video.status === 'downloaded' && loadSuccess !== false && (
|
||||||
<video
|
<video
|
||||||
controls
|
controls
|
||||||
@ -73,41 +38,111 @@ export const VideoViewer = ({ video }: VideoViewerProps) => {
|
|||||||
<source src="video.mp4" type="video/mp4" />
|
<source src="video.mp4" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
)}
|
)}
|
||||||
{video && video.status === 'failed' && (
|
{video && video.status === 'failed' && <FailedVideo error={video.error} />}
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl bg-danger-200">
|
{video && video.status === 'downloaded' && loadSuccess === false && <LoadFailedVideo />}
|
||||||
<CircleXIcon size={64} className="fill-danger text-danger-200" />
|
|
||||||
<span className="font-bold text-2xl">{t('video.status.failed')}</span>
|
|
||||||
<div className="my-2 flex justify-between gap-2">
|
|
||||||
<Button onPress={onOpen}>{t('common.detail')}</Button>
|
|
||||||
<Modal isOpen={isOpen} onClose={onClose}>
|
|
||||||
<ModalBody>
|
|
||||||
<ModalContent>
|
|
||||||
<div className="p-4">
|
|
||||||
{video.error === null ? (
|
|
||||||
<Alert color="danger" title={t('error.unknown')} />
|
|
||||||
) : (
|
|
||||||
<Alert color="danger" title={video.error.code} description={video.error.message} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ModalContent>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter></ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
<Button onPress={() => window.toast.info('Not implemented')}>{t('common.retry')}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{video && video.status === 'downloaded' && loadSuccess === false && (
|
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl bg-danger-200">
|
|
||||||
<CircleXIcon size={64} className="fill-danger text-danger-200" />
|
|
||||||
<span className="font-bold text-2xl">{t('video.error.load.message')}</span>
|
|
||||||
<span>{t('video.error.load.reason')}</span>
|
|
||||||
<div className="my-2 flex justify-between gap-2">
|
|
||||||
<Button onPress={() => window.toast.info('Not implemented')}>{t('common.redownload')}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QueuedVideo = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl">
|
||||||
|
<Spinner variant="dots" />
|
||||||
|
<span>{t('video.status.queued')}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const InProgressVideo = ({ progress }: { progress: number }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl">
|
||||||
|
<Progress
|
||||||
|
label={t('video.status.in_progress')}
|
||||||
|
aria-label={t('video.status.in_progress')}
|
||||||
|
className="max-w-md"
|
||||||
|
color="primary"
|
||||||
|
showValueLabel={true}
|
||||||
|
size="md"
|
||||||
|
value={progress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompletedVideo = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl bg-success-200">
|
||||||
|
<CheckCircleIcon size={64} className="text-success" />
|
||||||
|
<span className="font-bold text-2xl">{t('video.status.completed')}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const DownloadingVideo = ({ progress }: { progress?: number }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl">
|
||||||
|
<Progress
|
||||||
|
label={t('video.status.downloading')}
|
||||||
|
aria-label={t('video.status.downloading')}
|
||||||
|
className="max-w-md"
|
||||||
|
color="primary"
|
||||||
|
showValueLabel={true}
|
||||||
|
size="md"
|
||||||
|
value={progress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const FailedVideo = ({ error }: { error: VideoFailed['error'] }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||||
|
|
||||||
|
const alert = useMemo(() => {
|
||||||
|
if (error === null) {
|
||||||
|
return <Alert color="danger" title={t('error.unknown')} />
|
||||||
|
} else {
|
||||||
|
return <Alert color="danger" title={error.code} description={error.message} />
|
||||||
|
}
|
||||||
|
}, [error, t])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl bg-danger-200">
|
||||||
|
<CircleXIcon size={64} className="fill-danger text-danger-200" />
|
||||||
|
<span className="font-bold text-2xl">{t('video.status.failed')}</span>
|
||||||
|
<div className="my-2 flex justify-between gap-2">
|
||||||
|
<Button onPress={onOpen}>{t('common.detail')}</Button>
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<ModalBody>
|
||||||
|
<ModalContent>
|
||||||
|
<div className="p-4">{alert}</div>
|
||||||
|
</ModalContent>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter></ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
<Button onPress={() => window.toast.info('Not implemented')}>{t('common.retry')}</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoadFailedVideo = ({ onRedownload }: { onRedownload?: () => void }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl bg-danger-200">
|
||||||
|
<CircleXIcon size={64} className="fill-danger text-danger-200" />
|
||||||
|
<span className="font-bold text-2xl">{t('video.error.load.message')}</span>
|
||||||
|
<span>{t('video.error.load.reason')}</span>
|
||||||
|
<div className="my-2 flex justify-between gap-2">
|
||||||
|
<Button onPress={() => onRedownload?.() || window.toast.info('Not implemented')}>
|
||||||
|
{t('common.redownload')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -49,14 +49,14 @@ export interface VideoDownloading extends VideoBase {
|
|||||||
/** integer percent */
|
/** integer percent */
|
||||||
progress: number
|
progress: number
|
||||||
}
|
}
|
||||||
export interface Videodownloaded extends VideoBase {
|
export interface VideoDownloaded extends VideoBase {
|
||||||
status: 'downloaded'
|
status: 'downloaded'
|
||||||
thumbnail: string
|
thumbnail: string
|
||||||
/** Managed by fileManager */
|
/** Managed by fileManager */
|
||||||
fileId: string
|
fileId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoFailed extends VideoBase {
|
export interface VideoFailedBase extends VideoBase {
|
||||||
status: 'failed'
|
status: 'failed'
|
||||||
error: unknown
|
error: unknown
|
||||||
}
|
}
|
||||||
@ -65,11 +65,13 @@ export interface OpenAIVideoQueued extends VideoQueued, OpenAIVideoBase {}
|
|||||||
export interface OpenAIVideoInProgress extends VideoInProgress, OpenAIVideoBase {}
|
export interface OpenAIVideoInProgress extends VideoInProgress, OpenAIVideoBase {}
|
||||||
export interface OpenAIVideoCompleted extends VideoCompleted, OpenAIVideoBase {}
|
export interface OpenAIVideoCompleted extends VideoCompleted, OpenAIVideoBase {}
|
||||||
export interface OpenAIVideoDownloading extends VideoDownloading, OpenAIVideoBase {}
|
export interface OpenAIVideoDownloading extends VideoDownloading, OpenAIVideoBase {}
|
||||||
export interface OpenAIVideoDownloaded extends Videodownloaded, OpenAIVideoBase {}
|
export interface OpenAIVideoDownloaded extends VideoDownloaded, OpenAIVideoBase {}
|
||||||
export interface OpenAIVideoFailed extends VideoFailed, OpenAIVideoBase {
|
export interface OpenAIVideoFailed extends VideoFailedBase, OpenAIVideoBase {
|
||||||
error: OpenAI.Videos.Video['error']
|
error: OpenAI.Videos.Video['error']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VideoFailed = OpenAIVideoFailed
|
||||||
|
|
||||||
export type OpenAIVideo =
|
export type OpenAIVideo =
|
||||||
| OpenAIVideoQueued
|
| OpenAIVideoQueued
|
||||||
| OpenAIVideoInProgress
|
| OpenAIVideoInProgress
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user