import axios from "axios"; import { useEffect, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router"; import "./reader.css"; import OutlinedButton from "../md-components/OutlinedButton"; import FilledButton from "../md-components/FilledButton"; import Contents from "../components/contents/Contents"; import Icon from "../md-components/Icon"; import { Link } from "react-router-dom"; import Skeleton from "react-loading-skeleton"; import LinearProgress from "../md-components/LinearProgress"; import { addBook, getBook, getReadBook, putReadBook, removeBook, saveReadBook, updateReadBook, } from "../db"; import IconButton from "../md-components/IconButton"; const ReaderFB2 = () => { const { id } = useParams(); const readerRef = useRef(); const containerRef = useRef(); const progressRef = useRef(); const contentsRef = useRef(); const contentsBackdropRef = useRef(); const navigate = useNavigate(); const [totalHeight, setTotalHeight] = useState(0); const [currentHeight, setCurrentHeight] = useState(0); const [readerHeight, setReaderHeight] = useState(0); const [totalPages, setTotalPages] = useState(1); const [currentPage, setCurrentPage] = useState(0); const [isLocal, setIsLocal] = useState(false); const [onBookshelf, setOnBookshelf] = useState(false); const [bookInfo, setBookInfo] = useState({ title: "" }); const [contents, setContents] = useState([]); function nextPage() { setCurrentPage((prev) => { if (totalPages - prev < 1) { return prev; } return prev + 1; }); } function prevPage() { setCurrentPage((prev) => { if (prev <= 1) { return prev; } return prev - 1; }); } const leftRef = useRef(); const rightRef = useRef(); function keyboardPagination(e) { if (!(leftRef.current && rightRef.current)) { console.log("harakiri"); document.removeEventListener("keydown", keyboardPagination, false); return; } if (e.key === "ArrowRight") { rightRef.current.click(); } if (e.key === "ArrowLeft") { leftRef.current.click(); } } useEffect(() => { if (!(leftRef.current && rightRef.current)) return; console.log("add listener"); document.addEventListener("keydown", keyboardPagination, false); }, [leftRef, rightRef]); function toggleFloatingContents() { if (!contentsRef.current || !contentsBackdropRef.current) return; contentsRef.current.classList.toggle("show"); contentsBackdropRef.current.classList.toggle("show"); } useEffect(() => { if (currentPage === 0) return; if (readerHeight === totalHeight) return; setCurrentHeight(readerHeight * (currentPage - 1)); readerRef.current.scrollTop = readerHeight * (currentPage - 1); progressRef.current.style.width = `${(currentPage / totalPages) * 100}%`; //console.log(readerHeight * (currentPage-1) / totalHeight, "update read book", currentPage) //console.log(readerHeight, currentPage, totalHeight) updateReadBook(id, (readerHeight * (currentPage - 1)) / totalHeight); }, [currentPage]); async function loadBook() { let readBook = await getReadBook(id); if (readBook) { setOnBookshelf(readBook.onBookshelf === true); } let caches = undefined; let localHTML = undefined; if (window.caches !== undefined) { caches = await window.caches.open("books"); localHTML = await caches.match(`/api/book/${id}/reader`); } let localBook = await getBook(id); let innerHTML = ""; if (localBook !== undefined && localHTML !== undefined) { setIsLocal(true); setBookInfo(localBook); setContents(localBook.contents); await localHTML.text().then((html) => { innerHTML = html; }); } else { if (!window.onLine) { localStorage.removeItem("lastReadBook"); navigate("/offline"); } setIsLocal(false); axios .get(`/book/${id}`) .then((res) => setBookInfo(res.data)) .catch(() => navigate("/notfound")); axios.get(`/book/${id}/contents`).then((res) => setContents(res.data)); await axios.get(`/book/${id}/reader`).then((res) => { innerHTML = res.data; }); } localStorage.setItem("lastReadBook", id); readerRef.current.innerHTML = innerHTML + "
".repeat(Math.floor(containerRef.current.clientHeight / 23) * 2); // заполнение двух страниц пустотой для возможности прокрутки не до конца resizingReader(true); } async function resizingReader(firstLoad) { if (!readerRef.current) return; if (readerRef.current.scrollHeight === 0) return; let progressRatio; if (firstLoad) { let readBook = await getReadBook(id); if (readBook === undefined) { progressRatio = 0; updateReadBook(id, 0); } else { progressRatio = readBook.progress; } } readerRef.current.style.height = `${ Math.floor(containerRef.current.clientHeight / 23) * 23 - 23 }px`; // подгоняем блок контента под окно ридера, и не забывает учитывать прогресс setTotalHeight(readerRef.current.scrollHeight); setReaderHeight(readerRef.current.clientHeight); setTotalPages( Math.ceil( readerRef.current.scrollHeight / readerRef.current.clientHeight ) - 2 ); setCurrentPage( Math.ceil( Math.floor(progressRatio * readerRef.current.scrollHeight) / readerRef.current.clientHeight ) + 1 ); } useEffect(() => { if (!readerRef.current) return; loadBook(); }, [id]); // изменение размера изображений для соответствия с шириной строки (23px) useEffect(() => { if (!readerRef.current) return; let images = Array.from(readerRef.current.getElementsByTagName("img")); images.map((img) => { img.height = Math.floor(img.height / 23) * 23; }); }, [totalHeight]); return ( <>
{!(contents.length === 1 && contents[0].title === "") && contents.length !== 0 ? ( article Содержание ) : ( <> )}

arrow_back {currentPage} из {totalPages} arrow_forward
); }; export default ReaderFB2;