import { useCallback, useMemo, useState, type KeyboardEvent } from 'react' import { convertFileSrc } from '@tauri-apps/api/core' import { ArrowSquareOut, ClipboardText, FileDashed, FolderOpen, ImageSquare, WarningCircle } from '@phosphor-icons/react' import type { VaultEntry } from '../types' import { isImagePreviewEntry, previewFileTypeLabel } from '../utils/filePreview' import { focusNoteListContainer } from '../utils/neighborhoodHistory' import { openLocalFile } from '../utils/url' import { Button } from './ui/button' interface FilePreviewProps { entry: VaultEntry onCopyFilePath?: (path: string) => void onOpenExternalFile?: (path: string) => void onRevealFile?: (path: string) => void } interface FilePreviewFallbackProps { icon: 'warning' | 'file' title: string description: string onOpenExternal: () => void } function FilePreviewFallback({ icon, title, description, onOpenExternal }: FilePreviewFallbackProps) { const Icon = icon === 'warning' ? WarningCircle : FileDashed return (
) } function FilePreviewHeader({ entry, isImage, fileTypeLabel, onOpenExternal, onRevealFile, onCopyFilePath, }: { entry: VaultEntry isImage: boolean fileTypeLabel: string onOpenExternal: () => void onRevealFile?: () => void onCopyFilePath?: () => void }) { const HeaderIcon = isImage ? ImageSquare : FileDashed return (
{onRevealFile && ( )} {onCopyFilePath && ( )}
) } function FilePreviewImage({ entry, imageSrc, onImageError, }: { entry: VaultEntry imageSrc: string onImageError: () => void }) { return (
{entry.title}
) } function shouldRenderImagePreview(isImage: boolean, imageSrc: string | null, imageFailed: boolean): imageSrc is string { return isImage && imageSrc !== null && !imageFailed } function FilePreviewBody({ entry, isImage, imageSrc, imageFailed, onImageError, onOpenExternal, }: { entry: VaultEntry isImage: boolean imageSrc: string | null imageFailed: boolean onImageError: () => void onOpenExternal: () => void }) { if (shouldRenderImagePreview(isImage, imageSrc, imageFailed)) { return } return ( ) } export function FilePreview({ entry, onCopyFilePath, onOpenExternalFile, onRevealFile, }: FilePreviewProps) { const [imageFailed, setImageFailed] = useState(false) const isImage = isImagePreviewEntry(entry) const imageSrc = useMemo(() => (isImage ? convertFileSrc(entry.path) : null), [entry.path, isImage]) const fileTypeLabel = previewFileTypeLabel(entry) const handleImageError = useCallback(() => setImageFailed(true), []) const handleOpenExternal = useCallback(() => { if (onOpenExternalFile) { onOpenExternalFile(entry.path) return } void openLocalFile(entry.path).catch((error) => { console.warn('Failed to open file with default app:', error) }) }, [entry.path, onOpenExternalFile]) const handleRevealFile = useCallback(() => { onRevealFile?.(entry.path) }, [entry.path, onRevealFile]) const handleCopyFilePath = useCallback(() => { onCopyFilePath?.(entry.path) }, [entry.path, onCopyFilePath]) const handleKeyDown = useCallback((event: KeyboardEvent) => { if (event.key !== 'Escape') return event.preventDefault() focusNoteListContainer(document) }, []) return (
) }