import { useEffect, useRef, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { api, type BrowseEntry, type MediaItem } from "../../api/client"; import TagPanel from "../TagPanel/TagPanel"; interface Props { mediaId: number; siblings: BrowseEntry[]; onClose: () => void; onNavigate: (id: number) => void; } export default function MediaViewer({ mediaId, siblings, onClose, onNavigate }: Props) { const [showTags, setShowTags] = useState(() => window.innerWidth >= 768); const touchStartX = useRef(null); const { data: item } = useQuery({ queryKey: ["media", mediaId], queryFn: () => api.media.get(mediaId), }); const mediaSiblings = siblings.filter((e) => e.media_item_id != null); const currentIndex = mediaSiblings.findIndex((e) => e.media_item_id === mediaId); const prevId = currentIndex > 0 ? mediaSiblings[currentIndex - 1].media_item_id : null; const nextId = currentIndex < mediaSiblings.length - 1 ? mediaSiblings[currentIndex + 1].media_item_id : null; useEffect(() => { function onKey(e: KeyboardEvent) { if (e.key === "Escape") onClose(); if (e.key === "ArrowLeft" && prevId) onNavigate(prevId); if (e.key === "ArrowRight" && nextId) onNavigate(nextId); } const onTouchStart = (e: TouchEvent) => { touchStartX.current = e.touches[0].clientX; }; const onTouchEnd = (e: TouchEvent) => { if (touchStartX.current === null) return; const delta = touchStartX.current - e.changedTouches[0].clientX; if (Math.abs(delta) > 50) { if (delta > 0 && nextId) onNavigate(nextId); if (delta < 0 && prevId) onNavigate(prevId); } touchStartX.current = null; }; window.addEventListener("keydown", onKey); window.addEventListener("touchstart", onTouchStart); window.addEventListener("touchend", onTouchEnd); return () => { window.removeEventListener("keydown", onKey); window.removeEventListener("touchstart", onTouchStart); window.removeEventListener("touchend", onTouchEnd); }; }, [prevId, nextId, onClose, onNavigate]); return ( <> {/* Backdrop */}
{/* Prev */} {/* Next */} {/* Media card */}
e.stopPropagation()} style={{ position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)", zIndex: 101, background: "#1a1a1a", borderRadius: 8, padding: 16, display: "flex", flexDirection: "column", alignItems: "center", gap: 12, maxWidth: "80vw", maxHeight: "90vh", overflow: "auto" }} > {item?.filename && (
{item.filename}
)} {item?.media_type === "image" && ( {item.filename} )} {item?.media_type === "video" && (
{/* Top-right controls */}
{/* Tag panel */} {showTags && item && (
e.stopPropagation()} style={{ position: "fixed", top: 0, right: 0, height: "100%", width: 260, background: "#1a1a1a", borderLeft: "1px solid #333", padding: "48px 16px 16px", zIndex: 101, overflowY: "auto" }} >
)} ); }