import { useCallback, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { ChevronLeft, ChevronRight, X, Download, ImageOff } from 'lucide-react'; import { authFetch } from '../../lib/auth'; import { useAuthedFileUrl } from '../../lib/authedFile'; /** One lightbox entry: the (possibly data:/`/api/files`) URL plus the human filename, * so downloads and alt text use the real name rather than a URL stamp. */ export interface LightboxImage { url: string; name?: string; } interface Props { images: LightboxImage[]; index: number; onClose: () => void; onNavigate: (index: number) => void; } export default function ImageLightbox({ images, index, onClose, onNavigate }: Props) { const current = images[index]; const goPrev = useCallback(() => { if (index > 0) onNavigate(index - 1); }, [index, onNavigate]); const goNext = useCallback(() => { if (index < images.length - 1) onNavigate(index + 1); }, [index, images.length, onNavigate]); // /api/files/* needs the auth token, which a native can't send — resolve // the currently-shown image to a blob URL fetched with the Authorization header. const { url: resolvedSrc, status } = useAuthedFileUrl(current?.url); useEffect(() => { const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); if (e.key === 'ArrowLeft') goPrev(); if (e.key === 'ArrowRight') goNext(); }; window.addEventListener('keydown', handleKey); return () => window.removeEventListener('keydown', handleKey); }, [onClose, goPrev, goNext]); return ( {/* Action buttons */}
{/* Image counter */} {images.length > 1 && (
{index + 1} / {images.length}
)} {/* Left arrow */} {index > 0 && ( )} {/* Right arrow */} {index < images.length - 1 && ( )} {/* Image */} {status === 'error' ? (
e.stopPropagation()} > {current?.name || 'Image not found'}
) : resolvedSrc ? ( {current?.name e.stopPropagation()} /> ) : (
e.stopPropagation()} /> )} ); }