Files
makelore/src/pages/ImageCanvas/DesignAssetThumbnail.tsx
T

81 lines
2.3 KiB
TypeScript

import { useEffect, useState } from 'react';
import { Film, ImageIcon } from 'lucide-react';
import { resolveImageWorkspaceAssetUrl } from '@/lib/image-workspace';
import { cn } from '@/lib/utils';
import type { DesignAsset } from '../../../shared/image-workspace';
export function DesignAssetThumbnail({
asset,
className,
alt,
controls = false,
}: {
asset: DesignAsset | null;
className?: string;
alt?: string;
controls?: boolean;
}) {
const [resolution, setResolution] = useState<{
contentPath: string | null;
source: string | null;
failed: boolean;
}>({ contentPath: asset?.contentPath ?? null, source: null, failed: false });
const currentResolution = resolution.contentPath === (asset?.contentPath ?? null)
? resolution
: { contentPath: asset?.contentPath ?? null, source: null, failed: false };
useEffect(() => {
if (!asset) return;
let active = true;
void resolveImageWorkspaceAssetUrl(asset.contentPath)
.then((url) => {
if (active) setResolution({ contentPath: asset.contentPath, source: url, failed: false });
})
.catch(() => {
if (active) setResolution({ contentPath: asset.contentPath, source: null, failed: true });
});
return () => {
active = false;
};
}, [asset]);
if (!asset || !currentResolution.source || currentResolution.failed) {
return (
<span
className={cn(
'flex shrink-0 items-center justify-center overflow-hidden bg-surface-subtle text-muted-foreground',
className,
)}
aria-hidden="true"
>
{asset?.mediaType === 'video'
? <Film className="h-5 w-5" />
: <ImageIcon className="h-5 w-5" />}
</span>
);
}
if (asset.mediaType === 'video') {
return (
<video
src={currentResolution.source}
className={cn('shrink-0 bg-surface-subtle object-cover', className)}
aria-label={alt ?? '视频作品预览'}
muted
preload="metadata"
controls={controls}
playsInline
/>
);
}
return (
<img
src={currentResolution.source}
alt={alt ?? (asset.role === 'uploaded' ? '参考图片预览' : 'AI 作品预览')}
className={cn('shrink-0 bg-surface-subtle object-cover', className)}
loading="lazy"
/>
);
}