import { ArrowLeft, ChevronLeft, ChevronRight, Home } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router"; import { useMarkAsRead, } from "@/api/generated/manga-chapter/manga-chapter.ts"; import { ThemeToggle } from "@/components/ThemeToggle.tsx"; import { Button } from "@/components/ui/button"; import { useReadingTracker } from "@/features/chapter/hooks/useReadingTracker.ts"; import {useGetMangaContentImages} from "@/api/generated/content/content.ts"; const Chapter = () => { const { setCurrentChapterPage, getCurrentChapterPage } = useReadingTracker(); const navigate = useNavigate(); const params = useParams(); const mangaId = Number(params.mangaId); const chapterNumber = Number(params.chapterId); const [currentPage, setCurrentPage] = useState( getCurrentChapterPage(chapterNumber) ?? 1, ); const [infiniteScroll, setInfiniteScroll] = useState(true); const { data, isLoading } = useGetMangaContentImages(chapterNumber); const { mutate } = useMarkAsRead(); // For infinite scroll mode const [visibleCount, setVisibleCount] = useState(1); const loadMoreRef = useRef(null); /** Mark chapter as read when last page reached */ useEffect(() => { if (!data || isLoading) return; if (currentPage === data.data?.contentImageKeys.length) { mutate({ chapterId: chapterNumber }); } }, [data, mutate, currentPage]); /** Persist reading progress */ useEffect(() => { setCurrentChapterPage(chapterNumber, currentPage); }, [chapterNumber, currentPage]); /** Restore stored page */ useEffect(() => { if (!isLoading && data?.data) { const stored = getCurrentChapterPage(chapterNumber); if (stored) { setCurrentPage(stored); setVisibleCount(stored); // for infinite scroll } } }, [isLoading, data?.data]); /** Infinite scroll observer */ useEffect(() => { if (!infiniteScroll) return; if (!loadMoreRef.current) return; const obs = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { setVisibleCount((count) => Math.min(count + 2, data?.data?.contentImageKeys.length ?? 0), ); } }); obs.observe(loadMoreRef.current); return () => obs.disconnect(); }, [infiniteScroll, data?.data]); /** Track which image is currently visible (for progress update) */ useEffect(() => { if (!infiniteScroll) return; const imgs = document.querySelectorAll("[data-page]"); const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { const el = entry.target as HTMLElement; // <-- FIX if (entry.isIntersecting) { const pageNum = Number(el.dataset.page); // <-- SAFE setCurrentPage(pageNum); } }); }, { threshold: 0.5 }, ); imgs.forEach((img) => observer.observe(img)); return () => observer.disconnect(); }, [infiniteScroll, visibleCount]); useEffect(() => { if (!data?.data) return; // When switching modes: if (infiniteScroll) { // Scroll mode → show saved progress setVisibleCount(currentPage); setTimeout(() => { const el = document.querySelector(`[data-page="${currentPage}"]`); el?.scrollIntoView({ behavior: "smooth", block: "start" }); }, 50); } else { // Single page mode → scroll to top window.scrollTo({ top: 0 }); } }, [infiniteScroll]); if (!data?.data) { return (

Manga not found

); } const images = data.data.contentImageKeys; /** Standard navigation (non-infinite mode) */ const goToNextPage = () => { if (currentPage < images.length) { setCurrentPage((p) => p + 1); window.scrollTo({ top: 0, behavior: "smooth" }); } }; const goToPreviousPage = () => { if (currentPage > 1) { setCurrentPage((p) => p - 1); window.scrollTo({ top: 0, behavior: "smooth" }); } }; return (
{/* HEADER */}

{data.data.mangaTitle}

Chapter {chapterNumber}

Page {currentPage} / {images.length}
{/* MAIN */}

{data.data.mangaTitle}

Chapter {chapterNumber}

{/* ------------------------------------------------------------------ */} {/* MODE 1 --- INFINITE SCROLL MODE */} {/* ------------------------------------------------------------------ */} {infiniteScroll ? (
{images.slice(0, visibleCount).map((key, idx) => ( {`Page ))} {/* LOAD MORE SENTINEL */}
) : ( /* ------------------------------------------------------------------ */ /* MODE 2 --- STANDARD SINGLE-PAGE MODE */ /* ------------------------------------------------------------------ */ <>
{`Page
{/* NAVIGATION BUTTONS */}
{currentPage} / {images.length}
)}
); }; export default Chapter;