diff --git a/src/app/(app)/gallery/[gid]/read/page.tsx b/src/app/(app)/gallery/[gid]/read/page.tsx index da267a9..33ac874 100644 --- a/src/app/(app)/gallery/[gid]/read/page.tsx +++ b/src/app/(app)/gallery/[gid]/read/page.tsx @@ -29,12 +29,51 @@ export default function ReaderPage() { const [mode, setMode] = useState<"paged" | "webtoon">("paged") const [showControls, setShowControls] = useState(true) const [isFullscreen, setIsFullscreen] = useState(false) + const [resumeNotice, setResumeNotice] = useState(null) const hideTimer = useRef | undefined>(undefined) const touchStartX = useRef(null) const touchStartY = useRef(null) const containerRef = useRef(null) + // ---- Persistencia del progreso de lectura (localStorage, sin backend) ---- + const PROGRESS_KEY = `ws_progress_${gid}` + + function saveProgress(p: number, m: "paged" | "webtoon") { + try { + localStorage.setItem(PROGRESS_KEY, JSON.stringify({ page: p, mode: m, at: Date.now() })) + } catch {} + } + + function loadProgress(): { page: number; mode: "paged" | "webtoon" } | null { + try { + const raw = localStorage.getItem(PROGRESS_KEY) + if (!raw) return null + const parsed = JSON.parse(raw) + if (typeof parsed.page === "number" && (parsed.mode === "paged" || parsed.mode === "webtoon")) { + return parsed + } + return null + } catch { + return null + } + } + + // Restaura el progreso guardado (página + modo) al abrir el lector. + function restoreProgress(total: number) { + const saved = loadProgress() + if (!saved) return + // Solo restaurar si es una página intermedia (no la primera ni la última). + if (saved.page > 0 && saved.page < total - 1) { + setPage(saved.page) + if (saved.mode) setMode(saved.mode) + setResumeNotice(`Continuaste en la página ${saved.page + 1} de ${total}`) + } else if (saved.page >= total - 1) { + // Llegó al final la última vez — empezar de nuevo. + saveProgress(0, "paged") + } + } + // Fetch page list useEffect(() => { async function load() { @@ -50,6 +89,7 @@ export default function ReaderPage() { const urls = Array.from({ length: total }, (_, i) => `/api/proxy/galleries/${gid}/page/${i + 1}`) setPageUrls(urls) setLoading(false) + restoreProgress(total) return } } @@ -65,6 +105,7 @@ export default function ReaderPage() { const urls = Array.from({ length: summary.num_pages }, (_, i) => `/api/proxy/galleries/${gid}/page/${i + 1}`) setPageUrls(urls) setLoading(false) + restoreProgress(summary.num_pages) return } } catch {} @@ -77,6 +118,7 @@ export default function ReaderPage() { const urls = Array.from({ length: rendered.length }, (_, i) => `/api/proxy/galleries/${gid}/page/${i + 1}`) setPageUrls(urls) setLoading(false) + restoreProgress(rendered.length) return } } catch {} @@ -103,10 +145,11 @@ export default function ReaderPage() { (n: number) => { setPage((prev) => { const next = Math.max(0, Math.min(n, totalPages - 1)) + saveProgress(next, mode) return next }) }, - [totalPages] + [totalPages, mode] ) // Preload next 2 images @@ -133,6 +176,18 @@ export default function ReaderPage() { return () => window.removeEventListener("keydown", onKey) }, [page, goTo]) + // Guardar progreso al salir del lector (cierre de pestaña o navegación). + useEffect(() => { + function onExit() { + saveProgress(page, mode) + } + window.addEventListener("pagehide", onExit) + return () => { + window.removeEventListener("pagehide", onExit) + saveProgress(page, mode) + } + }, [page, mode]) + const handleUserActivity = useCallback(() => { setShowControls(true) if (hideTimer.current) clearTimeout(hideTimer.current) @@ -223,6 +278,30 @@ export default function ReaderPage() { onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} > + {/* Barra de progreso de lectura (siempre visible, arriba) */} + {totalPages > 1 && ( +
+
+
+ )} + + {/* Aviso de "Continuaste leyendo" */} + {resumeNotice && ( +
+ {resumeNotice} + +
+ )} + {/* Floating Top Controls Header */}