'use client'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useDrag, useDrop } from 'react-dnd'; import type { DocumentListDocument } from '@/types/documents'; import { PDFIcon, EPUBIcon, FileIcon } from '@/components/icons/Icons'; import { DocumentPreview } from '@/components/doclist/DocumentPreview'; import { formatDocumentSize } from '@/components/doclist/formatSize'; import { Button, ButtonLink } from '@/components/ui'; import { useDocumentSelection } from '../dnd/DocumentSelectionContext'; import { DND_DOCUMENT, documentIdentityKey, type DocumentDragItem } from '../dnd/dndTypes'; interface GalleryViewProps { documents: DocumentListDocument[]; folderNameById?: Record; onDeleteDoc: (doc: DocumentListDocument) => void; onMergeIntoFolder: (sources: DocumentListDocument[], target: DocumentListDocument) => void; } function formatDateTime(value: number | undefined): string { if (!value || !Number.isFinite(value) || value <= 0) return 'Never'; return new Date(value).toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', }); } function formatParseStatus(status: DocumentListDocument['parseStatus']): string { if (!status) return 'N/A'; if (status === 'pending') return 'Pending'; if (status === 'running') return 'Running'; if (status === 'ready') return 'Ready'; return 'Failed'; } function KindIcon({ doc, className }: { doc: DocumentListDocument; className?: string }) { if (doc.type === 'pdf') return ; if (doc.type === 'epub') return ; return ; } function GalleryThumb({ doc, active, onClick, onMergeIntoFolder, }: { doc: DocumentListDocument; active: boolean; onClick: () => void; onMergeIntoFolder: (sources: DocumentListDocument[], target: DocumentListDocument) => void; }) { const selection = useDocumentSelection(); const isSelected = selection.isSelected(doc); const isInFolder = Boolean(doc.folderId); const [{ isDragging }, dragRef] = useDrag(() => ({ type: DND_DOCUMENT, item: () => { const sel = selection.getSelectedDocs(); const dragging = isSelected && sel.length > 1 ? sel : [doc]; if (!isSelected) selection.replace([doc]); return { items: dragging.map(({ id, type }) => ({ id, type })), docs: dragging, fromFolderId: doc.folderId, }; }, collect: (m) => ({ isDragging: m.isDragging() }), }), [doc, isSelected, selection]); const [{ isOver, canDrop }, dropRef] = useDrop(() => ({ accept: DND_DOCUMENT, canDrop: (item) => !isInFolder && !item.items.some((it) => documentIdentityKey(it) === documentIdentityKey(doc)), drop: (item) => onMergeIntoFolder(item.docs, doc), collect: (m) => ({ isOver: m.isOver({ shallow: true }), canDrop: m.canDrop() }), }), [doc, isInFolder, onMergeIntoFolder]); const setRefs = (node: HTMLDivElement | null) => { dragRef(node); dropRef(node); }; return (
{doc.name}
); } export function GalleryView({ documents, folderNameById, onDeleteDoc, onMergeIntoFolder, }: GalleryViewProps) { const { setVisibleOrder } = useDocumentSelection(); const railRef = useRef(null); const [activeIdx, setActiveIdx] = useState(0); const activeDoc = useMemo(() => documents[activeIdx], [documents, activeIdx]); const openHref = activeDoc ? `/${activeDoc.type}/${encodeURIComponent(activeDoc.id)}` : null; useEffect(() => { setVisibleOrder(documents); }, [documents, setVisibleOrder]); useEffect(() => { if (activeIdx >= documents.length) { setActiveIdx(Math.max(0, documents.length - 1)); } }, [documents.length, activeIdx]); useEffect(() => { const rail = railRef.current; if (!rail) return; rail.scrollLeft = 0; }, [documents.length]); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (documents.length === 0) return; const target = e.target as HTMLElement; if (target?.closest('input, textarea, [contenteditable]')) return; if (e.key === 'ArrowRight') { setActiveIdx((i) => Math.min(documents.length - 1, i + 1)); } else if (e.key === 'ArrowLeft') { setActiveIdx((i) => Math.max(0, i - 1)); } else if (e.key === 'Delete' || e.key === 'Backspace') { const doc = documents[activeIdx]; if (doc) { e.preventDefault(); onDeleteDoc(doc); } } }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [documents, activeIdx, onDeleteDoc]); return (
{activeDoc ? (

{activeDoc.name}

{activeDoc.type.toUpperCase()} • {formatDocumentSize(activeDoc.size)}

Open
Type
{activeDoc.type}
Size
{formatDocumentSize(activeDoc.size)}
Last opened
{formatDateTime(activeDoc.recentlyOpenedAt)}
Last modified
{formatDateTime(activeDoc.lastModified)}
{activeDoc.folderId && ( <>
Folder
{folderNameById?.[activeDoc.folderId] ?? activeDoc.folderId}
)} {activeDoc.type === 'pdf' && ( <>
Parse status
{formatParseStatus(activeDoc.parseStatus)}
)}
) : (

No documents to show

)}
{documents.map((doc, i) => ( setActiveIdx(i)} onMergeIntoFolder={onMergeIntoFolder} /> ))}
); }