refacto(bookreader): refacto read progress call in debounce

This commit is contained in:
Julien Froidefond
2025-02-16 15:13:04 +01:00
parent abf1132ae0
commit 48472eaaf7
2 changed files with 83 additions and 36 deletions

View File

@@ -17,7 +17,7 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
const { const {
currentPage, currentPage,
setCurrentPage, navigateToPage,
isLoading, isLoading,
setIsLoading, setIsLoading,
secondPageLoading, secondPageLoading,
@@ -134,16 +134,6 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
[currentPage] [currentPage]
); );
const handlePageChange = useCallback(
(page: number) => {
setCurrentPage(page);
setIsLoading(true);
setImageError(false);
syncReadProgress(page);
},
[setCurrentPage, setIsLoading, setImageError, syncReadProgress]
);
return ( return (
<div className="fixed inset-0 bg-background/95 backdrop-blur-sm z-50"> <div className="fixed inset-0 bg-background/95 backdrop-blur-sm z-50">
<div <div
@@ -207,7 +197,7 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) {
<NavigationBar <NavigationBar
currentPage={currentPage} currentPage={currentPage}
pages={pages} pages={pages}
onPageChange={handlePageChange} onPageChange={navigateToPage}
showControls={showControls} showControls={showControls}
book={book} book={book}
/> />

View File

@@ -1,4 +1,4 @@
import { useState, useCallback, useEffect } from "react"; import { useState, useCallback, useEffect, useRef } from "react";
import { KomgaBook } from "@/types/komga"; import { KomgaBook } from "@/types/komga";
interface UsePageNavigationProps { interface UsePageNavigationProps {
@@ -12,6 +12,13 @@ export const usePageNavigation = ({ book, pages, isDoublePage }: UsePageNavigati
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [secondPageLoading, setSecondPageLoading] = useState(true); const [secondPageLoading, setSecondPageLoading] = useState(true);
const [imageError, setImageError] = useState(false); const [imageError, setImageError] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const touchStartXRef = useRef<number | null>(null);
const currentPageRef = useRef(currentPage);
useEffect(() => {
currentPageRef.current = currentPage;
}, [currentPage]);
const syncReadProgress = useCallback( const syncReadProgress = useCallback(
async (page: number) => { async (page: number) => {
@@ -31,6 +38,20 @@ export const usePageNavigation = ({ book, pages, isDoublePage }: UsePageNavigati
[book.id, pages.length] [book.id, pages.length]
); );
const debouncedSyncReadProgress = useCallback(
(page: number) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
syncReadProgress(page);
timeoutRef.current = null;
}, 2000);
},
[syncReadProgress]
);
const shouldShowDoublePage = useCallback( const shouldShowDoublePage = useCallback(
(pageNumber: number) => { (pageNumber: number) => {
if (!isDoublePage) return false; if (!isDoublePage) return false;
@@ -40,35 +61,55 @@ export const usePageNavigation = ({ book, pages, isDoublePage }: UsePageNavigati
[isDoublePage, pages.length] [isDoublePage, pages.length]
); );
const handlePreviousPage = useCallback(() => { const navigateToPage = useCallback(
if (currentPage > 1) { (page: number) => {
const newPage = isDoublePage && currentPage > 2 ? currentPage - 2 : currentPage - 1; setCurrentPage(page);
setCurrentPage(newPage);
setIsLoading(true); setIsLoading(true);
setSecondPageLoading(true); setSecondPageLoading(true);
setImageError(false); setImageError(false);
debouncedSyncReadProgress(page);
},
[debouncedSyncReadProgress]
);
const timer = setTimeout(() => { const handlePreviousPage = useCallback(() => {
syncReadProgress(newPage); if (currentPage > 1) {
}, 300); const newPage = isDoublePage && currentPage > 2 ? currentPage - 2 : currentPage - 1;
return () => clearTimeout(timer); navigateToPage(newPage);
} }
}, [currentPage, isDoublePage, syncReadProgress]); }, [currentPage, isDoublePage, navigateToPage]);
const handleNextPage = useCallback(() => { const handleNextPage = useCallback(() => {
if (currentPage < pages.length) { if (currentPage < pages.length) {
const newPage = isDoublePage ? Math.min(currentPage + 2, pages.length) : currentPage + 1; const newPage = isDoublePage ? Math.min(currentPage + 2, pages.length) : currentPage + 1;
setCurrentPage(newPage); navigateToPage(newPage);
setIsLoading(true);
setSecondPageLoading(true);
setImageError(false);
const timer = setTimeout(() => {
syncReadProgress(newPage);
}, 300);
return () => clearTimeout(timer);
} }
}, [currentPage, pages.length, isDoublePage, syncReadProgress]); }, [currentPage, pages.length, isDoublePage, navigateToPage]);
const handleTouchStart = useCallback((event: TouchEvent) => {
touchStartXRef.current = event.touches[0].clientX;
}, []);
const handleTouchEnd = useCallback(
(event: TouchEvent) => {
if (touchStartXRef.current === null) return;
const touchEndX = event.changedTouches[0].clientX;
const deltaX = touchEndX - touchStartXRef.current;
const minSwipeDistance = 50;
if (Math.abs(deltaX) > minSwipeDistance) {
if (deltaX > 0) {
handlePreviousPage();
} else {
handleNextPage();
}
}
touchStartXRef.current = null;
},
[handlePreviousPage, handleNextPage]
);
useEffect(() => { useEffect(() => {
setIsLoading(true); setIsLoading(true);
@@ -85,12 +126,28 @@ export const usePageNavigation = ({ book, pages, isDoublePage }: UsePageNavigati
}; };
window.addEventListener("keydown", handleKeyDown); window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown); window.addEventListener("touchstart", handleTouchStart);
}, [handlePreviousPage, handleNextPage]); window.addEventListener("touchend", handleTouchEnd);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("touchstart", handleTouchStart);
window.removeEventListener("touchend", handleTouchEnd);
};
}, [handlePreviousPage, handleNextPage, handleTouchStart, handleTouchEnd]);
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
syncReadProgress(currentPageRef.current);
}
};
}, [syncReadProgress]);
return { return {
currentPage, currentPage,
setCurrentPage, navigateToPage,
isLoading, isLoading,
setIsLoading, setIsLoading,
secondPageLoading, secondPageLoading,
@@ -100,6 +157,6 @@ export const usePageNavigation = ({ book, pages, isDoublePage }: UsePageNavigati
handlePreviousPage, handlePreviousPage,
handleNextPage, handleNextPage,
shouldShowDoublePage, shouldShowDoublePage,
syncReadProgress, syncReadProgress: debouncedSyncReadProgress,
}; };
}; };