fix: close reader immediately while cancelling prefetches
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m21s

This commit is contained in:
2026-03-01 21:36:40 +01:00
parent fead5ff6a0
commit 4441c59584
3 changed files with 53 additions and 5 deletions

View File

@@ -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} />
);

View File

@@ -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}

View File

@@ -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,