67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
import { memo, useEffect, useState } from 'react';
|
|
import { FileImage } from 'lucide-react';
|
|
import { loadCodingAttachmentPreview } from '@/lib/coding-attachments';
|
|
|
|
export const CodingAttachmentPreview = memo(function CodingAttachmentPreview({
|
|
attachmentId,
|
|
mime,
|
|
caption = '对话图片附件',
|
|
}: {
|
|
attachmentId: string;
|
|
mime: string;
|
|
caption?: string;
|
|
}) {
|
|
const [state, setState] = useState<{
|
|
attachmentId: string;
|
|
previewUrl: string | null;
|
|
failed: boolean;
|
|
}>({ attachmentId, previewUrl: null, failed: false });
|
|
|
|
useEffect(() => {
|
|
let active = true;
|
|
let objectUrl: string | null = null;
|
|
void loadCodingAttachmentPreview(attachmentId)
|
|
.then((preview) => {
|
|
if (!active) return;
|
|
const bytes = Uint8Array.from(preview.bytes);
|
|
objectUrl = URL.createObjectURL(new Blob([bytes], { type: preview.mime }));
|
|
setState({ attachmentId, previewUrl: objectUrl, failed: false });
|
|
})
|
|
.catch(() => {
|
|
if (active) setState({ attachmentId, previewUrl: null, failed: true });
|
|
});
|
|
return () => {
|
|
active = false;
|
|
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
};
|
|
}, [attachmentId]);
|
|
|
|
const previewUrl = state.attachmentId === attachmentId ? state.previewUrl : null;
|
|
const failed = state.attachmentId === attachmentId && state.failed;
|
|
|
|
return (
|
|
<figure
|
|
className="max-w-sm overflow-hidden rounded-2xl bg-foreground/[0.04] shadow-[0_0_0_1px_rgba(0,0,0,0.06)]"
|
|
data-attachment-id={attachmentId}
|
|
>
|
|
{previewUrl
|
|
? (
|
|
<img
|
|
src={previewUrl}
|
|
alt={caption}
|
|
className="max-h-80 w-full object-contain outline outline-1 -outline-offset-1 outline-black/10"
|
|
/>
|
|
)
|
|
: (
|
|
<div className="flex min-h-28 items-center justify-center gap-2 px-4 text-sm text-muted-foreground">
|
|
<FileImage className="h-5 w-5" aria-hidden="true" />
|
|
{failed ? '附件预览不可用' : '正在加载附件…'}
|
|
</div>
|
|
)}
|
|
<figcaption className="truncate px-3 py-2 font-mono text-[11px] text-muted-foreground">
|
|
{mime} · {attachmentId}
|
|
</figcaption>
|
|
</figure>
|
|
);
|
|
});
|