"use client" import { useEffect, useRef, useState, useCallback } from "react" import { useParams } from "next/navigation" import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Loader2 } from "lucide-react" export default function ReaderPage() { const { gid } = useParams<{ gid: string }>() const [page, setPage] = useState(0) const [pageUrls, setPageUrls] = useState([]) const [loading, setLoading] = useState(true) const [zoom, setZoom] = useState(1) const [error, setError] = useState("") const [title, setTitle] = useState("") const containerRef = useRef(null) const [showControls, setShowControls] = useState(true) const hideTimer = useRef | undefined>(undefined) useEffect(() => { async function load() { setLoading(true) setError("") try { const res = await fetch(`/api/proxy/galleries/${gid}/summary`) const data = await res.json() const summary = data?.data setTitle(summary?.title || `g/${gid}`) if (!summary) { setError("Gallery no encontrada"); setLoading(false); return } if (summary.num_pages && summary.num_pages > 0) { const urls: string[] = [] for (let i = 0; i < summary.num_pages; i++) { urls.push(`/api/proxy/galleries/${gid}/cover`) } setPageUrls(urls) setLoading(false) return } } catch {} try { const artRes = await fetch(`/api/proxy/galleries/${gid}/artifacts`) const artData = await artRes.json() const rendered = artData?.data?.files?.rendered if (rendered?.length) { const urls = rendered.map(() => `/api/proxy/galleries/${gid}/cover`) setPageUrls(urls) setLoading(false) return } } catch {} try { const coverRes = await fetch(`/api/proxy/galleries/${gid}/cover`, { method: "HEAD" }) if (coverRes.ok) { setPageUrls([`/api/proxy/galleries/${gid}/cover`]) setLoading(false) return } } catch {} setError("No hay imágenes disponibles. La API del pipeline solo expone la portada.") setLoading(false) } load() }, [gid]) const totalPages = pageUrls.length const goTo = useCallback((n: number) => setPage(Math.max(0, Math.min(n, totalPages - 1))), [totalPages]) useEffect(() => { function onKey(e: KeyboardEvent) { if (e.key === "ArrowLeft") goTo(page - 1) if (e.key === "ArrowRight") goTo(page + 1) if (e.key === "+" || e.key === "=") setZoom((z) => Math.min(z + 0.25, 3)) if (e.key === "-") setZoom((z) => Math.max(z - 0.25, 0.25)) } window.addEventListener("keydown", onKey) return () => window.removeEventListener("keydown", onKey) }, [page, goTo]) const handleMouseMove = useCallback(() => { setShowControls(true) if (hideTimer.current) clearTimeout(hideTimer.current) hideTimer.current = setTimeout(() => setShowControls(false), 2000) }, []) if (loading) { return (
) } if (error) { return (

{error}

La API del pipeline solo sirve la página 1 vía /galleries/{"{gid}"}/cover.

Volver al detalle
) } return (
{title}
{Math.round(zoom * 100)}% {totalPages > 1 && ( {page + 1} / {totalPages} )}
{totalPages > 1 && ( )} {totalPages 1 ? `Página ${page + 1}` : "Portada"} style={{ transform: `scale(${zoom})` }} className="max-h-[calc(100vh-8rem)] max-w-full origin-center object-contain transition-transform" draggable={false} /> {totalPages > 1 && ( )}
{totalPages > 1 && (
{Array.from({ length: Math.min(totalPages, 100) }).map((_, i) => (
)} {totalPages === 1 && (
Solo portada disponible. El pipeline solo expone la página 1 vía /cover.
)}
) }