fix: close reader immediately while cancelling prefetches
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m21s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m21s
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import type { KomgaBook } from "@/types/komga";
|
||||
import { PhotoswipeReader } from "./PhotoswipeReader";
|
||||
import { useRouter } from "next/navigation";
|
||||
@@ -13,12 +15,31 @@ interface ClientBookWrapperProps {
|
||||
|
||||
export function ClientBookWrapper({ book, pages, nextBook }: ClientBookWrapperProps) {
|
||||
const router = useRouter();
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const [targetPath, setTargetPath] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isClosing || !targetPath) return;
|
||||
router.push(targetPath);
|
||||
}, [isClosing, targetPath, router]);
|
||||
|
||||
const handleCloseReader = (currentPage: number) => {
|
||||
ClientOfflineBookService.setCurrentPage(book, currentPage);
|
||||
router.push(`/series/${book.seriesId}`);
|
||||
setTargetPath(`/series/${book.seriesId}`);
|
||||
setIsClosing(true);
|
||||
};
|
||||
|
||||
if (isClosing) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
<div className="flex items-center gap-3 rounded-full border border-border/60 bg-background/80 px-4 py-2 text-sm text-muted-foreground shadow-sm">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<span>Fermeture du lecteur...</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PhotoswipeReader book={book} pages={pages} onClose={handleCloseReader} nextBook={nextBook} />
|
||||
);
|
||||
|
||||
@@ -34,6 +34,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
||||
imageBlobUrls,
|
||||
prefetchPages,
|
||||
prefetchNextBook,
|
||||
cancelAllPrefetches,
|
||||
handleForceReload,
|
||||
getPageUrl,
|
||||
prefetchCount,
|
||||
@@ -105,6 +106,14 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
||||
]);
|
||||
|
||||
// Keyboard events
|
||||
const handleCloseReader = useCallback(
|
||||
(page: number) => {
|
||||
cancelAllPrefetches();
|
||||
onClose?.(page);
|
||||
},
|
||||
[cancelAllPrefetches, onClose]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "ArrowLeft") {
|
||||
@@ -123,7 +132,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
||||
}
|
||||
} else if (e.key === "Escape" && onClose) {
|
||||
e.preventDefault();
|
||||
onClose(currentPage);
|
||||
handleCloseReader(currentPage);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,7 +141,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, [handleNextPage, handlePreviousPage, onClose, isRTL, currentPage]);
|
||||
}, [handleNextPage, handlePreviousPage, onClose, isRTL, currentPage, handleCloseReader]);
|
||||
|
||||
const handleContainerClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
@@ -173,7 +182,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
||||
<ReaderContainer onContainerClick={handleContainerClick}>
|
||||
<EndOfSeriesModal
|
||||
show={showEndMessage}
|
||||
onClose={onClose || (() => undefined)}
|
||||
onClose={handleCloseReader}
|
||||
currentPage={currentPage}
|
||||
/>
|
||||
|
||||
@@ -183,7 +192,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
||||
onPreviousPage={handlePreviousPage}
|
||||
onNextPage={handleNextPage}
|
||||
onPageChange={navigateToPage}
|
||||
onClose={onClose}
|
||||
onClose={handleCloseReader}
|
||||
currentPage={currentPage}
|
||||
totalPages={pages.length}
|
||||
isDoublePage={isDoublePage}
|
||||
|
||||
@@ -53,9 +53,18 @@ export function useImageLoader({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const cancelAllPrefetches = useCallback(() => {
|
||||
abortControllersRef.current.forEach((controller) => controller.abort());
|
||||
abortControllersRef.current.clear();
|
||||
pendingFetchesRef.current.clear();
|
||||
}, []);
|
||||
|
||||
const runWithConcurrency = useCallback(
|
||||
async <T,>(items: T[], worker: (item: T) => Promise<void>, concurrency = PREFETCH_CONCURRENCY) => {
|
||||
for (let i = 0; i < items.length; i += concurrency) {
|
||||
if (!isMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
const batch = items.slice(i, i + concurrency);
|
||||
await Promise.all(batch.map((item) => worker(item)));
|
||||
}
|
||||
@@ -71,6 +80,10 @@ export function useImageLoader({
|
||||
// Prefetch image and store dimensions
|
||||
const prefetchImage = useCallback(
|
||||
async (pageNum: number) => {
|
||||
if (!isMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we already have both dimensions and blob URL
|
||||
const hasDimensions = loadedImagesRef.current[pageNum];
|
||||
const hasBlobUrl = imageBlobUrlsRef.current[pageNum];
|
||||
@@ -193,6 +206,10 @@ export function useImageLoader({
|
||||
// Let all prefetch requests run - server queue handles concurrency
|
||||
if (pagesToPrefetch.length > 0) {
|
||||
runWithConcurrency(pagesToPrefetch, async ({ pageNum, nextBookPageKey }) => {
|
||||
if (!isMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as pending
|
||||
pendingFetchesRef.current.add(nextBookPageKey);
|
||||
const controller = new AbortController();
|
||||
@@ -329,6 +346,7 @@ export function useImageLoader({
|
||||
prefetchImage,
|
||||
prefetchPages,
|
||||
prefetchNextBook,
|
||||
cancelAllPrefetches,
|
||||
handleForceReload,
|
||||
getPageUrl,
|
||||
prefetchCount,
|
||||
|
||||
Reference in New Issue
Block a user