import { useMemo, useState } from 'react'; import { FileIcon, Globe, Loader2, Plus, X } from 'lucide-react'; import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'; import { Skeleton } from '@/components/ui/skeleton'; import { cn } from '@/lib/utils'; import type { LinkPreviewEntry } from '@/hooks/use-link-metadata'; import { domainFromUrl } from '@/lib/link-metadata'; import { AttachmentLightbox, getAttachmentHandler, type AttachmentItem, } from '@/features/attachments/attachment-lightbox'; import { platform } from '@/lib/platform'; export interface PendingAttachment { id: string; file: File; thumbnailUrl?: string; status: 'pending' | 'uploading' | 'uploaded' | 'error'; } interface AttachmentStripProps { attachments: PendingAttachment[]; onRemove: (id: string) => void; onAddClick: () => void; linkPreviews?: LinkPreviewEntry[]; } function pendingToItem(p: PendingAttachment): AttachmentItem { return { id: p.id, filename: p.file.name, mimeType: p.file.type || 'application/octet-stream', sizeBytes: p.file.size, source: { kind: 'local', file: p.file }, }; } function formatFileSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } function AttachmentThumbnail({ attachment, onRemove, onPreview, }: { attachment: PendingAttachment; onRemove: () => void; onPreview?: () => void; }) { const isImage = attachment.file.type.startsWith('image/'); const isUploading = attachment.status === 'uploading'; const isError = attachment.status === 'error'; const previewable = getAttachmentHandler(attachment.file.type) === 'lightbox'; return (
{isImage && attachment.thumbnailUrl ? ( {attachment.file.name} ) : (
{attachment.file.name} {formatFileSize(attachment.file.size)}
)} {isUploading && (
)}
); } function LinkPreviewThumbnail({ entry }: { entry: LinkPreviewEntry }) { if (entry.isLoading) { return (
); } // Metadata fetch can fail; fall back to the bare URL so the link stays usable. const { metadata } = entry; const domain = metadata?.domain ?? domainFromUrl(entry.url); const title = metadata?.title ?? entry.url; return ( ); } export function AttachmentStrip({ attachments, onRemove, onAddClick, linkPreviews, }: AttachmentStripProps) { const hasLinks = linkPreviews && linkPreviews.length > 0; // Lightbox state — only previewable attachments go in. const previewable = useMemo( () => attachments.filter( (a) => getAttachmentHandler(a.file.type) === 'lightbox', ), [attachments], ); const items = useMemo(() => previewable.map(pendingToItem), [previewable]); const [openIndex, setOpenIndex] = useState(null); if (attachments.length === 0 && !hasLinks) return null; return ( <>
{attachments.map((a) => ( onRemove(a.id)} onPreview={() => { const idx = previewable.indexOf(a); if (idx >= 0) setOpenIndex(idx); }} /> ))} {linkPreviews?.map((entry) => ( ))}
{items.length > 0 && ( onRemove(item.id)} /> )} ); }