import React, { useCallback, useEffect, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { booksApi } from "../api/books"; import type { EBookDetail } from "../types/book"; export function ReaderPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [book, setBook] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const progressTimer = useRef | null>(null); const scrollRef = useRef(null); const bookId = Number(id); const saveProgress = useCallback(async () => { if (!scrollRef.current || !book) return; const { scrollTop, scrollHeight, clientHeight } = scrollRef.current; const position = Math.min(100, Math.round((scrollTop / (scrollHeight - clientHeight)) * 100)); try { await booksApi.updateProgress(bookId, { current_position: position }); } catch { /* silent */ } }, [bookId, book]); useEffect(() => { const loadBook = async () => { setLoading(true); setError(null); try { const data = await booksApi.getEBook(bookId); setBook(data); document.title = data.title; } catch (err) { setError(err instanceof Error ? err.message : "Failed to load book"); } finally { setLoading(false); } }; void loadBook(); return () => { document.title = "Cloud Reader"; }; }, [bookId]); useEffect(() => { progressTimer.current = setInterval(() => { void saveProgress(); }, 5000); return () => { if (progressTimer.current) clearInterval(progressTimer.current); }; }, [saveProgress]); useEffect(() => { const handleBeforeUnload = () => { void saveProgress(); }; window.addEventListener("beforeunload", handleBeforeUnload); return () => window.removeEventListener("beforeunload", handleBeforeUnload); }, [saveProgress]); if (loading) return

Loading book...

; if (error) return

{error}

; if (!book) return null; return (

{book.title}

{book.author}

{book.file_url ?