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";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
import type { KomgaBook } from "@/types/komga";
|
import type { KomgaBook } from "@/types/komga";
|
||||||
import { PhotoswipeReader } from "./PhotoswipeReader";
|
import { PhotoswipeReader } from "./PhotoswipeReader";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
@@ -13,12 +15,31 @@ interface ClientBookWrapperProps {
|
|||||||
|
|
||||||
export function ClientBookWrapper({ book, pages, nextBook }: ClientBookWrapperProps) {
|
export function ClientBookWrapper({ book, pages, nextBook }: ClientBookWrapperProps) {
|
||||||
const router = useRouter();
|
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) => {
|
const handleCloseReader = (currentPage: number) => {
|
||||||
ClientOfflineBookService.setCurrentPage(book, currentPage);
|
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 (
|
return (
|
||||||
<PhotoswipeReader book={book} pages={pages} onClose={handleCloseReader} nextBook={nextBook} />
|
<PhotoswipeReader book={book} pages={pages} onClose={handleCloseReader} nextBook={nextBook} />
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
|||||||
imageBlobUrls,
|
imageBlobUrls,
|
||||||
prefetchPages,
|
prefetchPages,
|
||||||
prefetchNextBook,
|
prefetchNextBook,
|
||||||
|
cancelAllPrefetches,
|
||||||
handleForceReload,
|
handleForceReload,
|
||||||
getPageUrl,
|
getPageUrl,
|
||||||
prefetchCount,
|
prefetchCount,
|
||||||
@@ -105,6 +106,14 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Keyboard events
|
// Keyboard events
|
||||||
|
const handleCloseReader = useCallback(
|
||||||
|
(page: number) => {
|
||||||
|
cancelAllPrefetches();
|
||||||
|
onClose?.(page);
|
||||||
|
},
|
||||||
|
[cancelAllPrefetches, onClose]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.key === "ArrowLeft") {
|
if (e.key === "ArrowLeft") {
|
||||||
@@ -123,7 +132,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
|||||||
}
|
}
|
||||||
} else if (e.key === "Escape" && onClose) {
|
} else if (e.key === "Escape" && onClose) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onClose(currentPage);
|
handleCloseReader(currentPage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,7 +141,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("keydown", handleKeyDown);
|
window.removeEventListener("keydown", handleKeyDown);
|
||||||
};
|
};
|
||||||
}, [handleNextPage, handlePreviousPage, onClose, isRTL, currentPage]);
|
}, [handleNextPage, handlePreviousPage, onClose, isRTL, currentPage, handleCloseReader]);
|
||||||
|
|
||||||
const handleContainerClick = useCallback(
|
const handleContainerClick = useCallback(
|
||||||
(e: React.MouseEvent) => {
|
(e: React.MouseEvent) => {
|
||||||
@@ -173,7 +182,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
|||||||
<ReaderContainer onContainerClick={handleContainerClick}>
|
<ReaderContainer onContainerClick={handleContainerClick}>
|
||||||
<EndOfSeriesModal
|
<EndOfSeriesModal
|
||||||
show={showEndMessage}
|
show={showEndMessage}
|
||||||
onClose={onClose || (() => undefined)}
|
onClose={handleCloseReader}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -183,7 +192,7 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP
|
|||||||
onPreviousPage={handlePreviousPage}
|
onPreviousPage={handlePreviousPage}
|
||||||
onNextPage={handleNextPage}
|
onNextPage={handleNextPage}
|
||||||
onPageChange={navigateToPage}
|
onPageChange={navigateToPage}
|
||||||
onClose={onClose}
|
onClose={handleCloseReader}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
totalPages={pages.length}
|
totalPages={pages.length}
|
||||||
isDoublePage={isDoublePage}
|
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(
|
const runWithConcurrency = useCallback(
|
||||||
async <T,>(items: T[], worker: (item: T) => Promise<void>, concurrency = PREFETCH_CONCURRENCY) => {
|
async <T,>(items: T[], worker: (item: T) => Promise<void>, concurrency = PREFETCH_CONCURRENCY) => {
|
||||||
for (let i = 0; i < items.length; i += concurrency) {
|
for (let i = 0; i < items.length; i += concurrency) {
|
||||||
|
if (!isMountedRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const batch = items.slice(i, i + concurrency);
|
const batch = items.slice(i, i + concurrency);
|
||||||
await Promise.all(batch.map((item) => worker(item)));
|
await Promise.all(batch.map((item) => worker(item)));
|
||||||
}
|
}
|
||||||
@@ -71,6 +80,10 @@ export function useImageLoader({
|
|||||||
// Prefetch image and store dimensions
|
// Prefetch image and store dimensions
|
||||||
const prefetchImage = useCallback(
|
const prefetchImage = useCallback(
|
||||||
async (pageNum: number) => {
|
async (pageNum: number) => {
|
||||||
|
if (!isMountedRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we already have both dimensions and blob URL
|
// Check if we already have both dimensions and blob URL
|
||||||
const hasDimensions = loadedImagesRef.current[pageNum];
|
const hasDimensions = loadedImagesRef.current[pageNum];
|
||||||
const hasBlobUrl = imageBlobUrlsRef.current[pageNum];
|
const hasBlobUrl = imageBlobUrlsRef.current[pageNum];
|
||||||
@@ -193,6 +206,10 @@ export function useImageLoader({
|
|||||||
// Let all prefetch requests run - server queue handles concurrency
|
// Let all prefetch requests run - server queue handles concurrency
|
||||||
if (pagesToPrefetch.length > 0) {
|
if (pagesToPrefetch.length > 0) {
|
||||||
runWithConcurrency(pagesToPrefetch, async ({ pageNum, nextBookPageKey }) => {
|
runWithConcurrency(pagesToPrefetch, async ({ pageNum, nextBookPageKey }) => {
|
||||||
|
if (!isMountedRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Mark as pending
|
// Mark as pending
|
||||||
pendingFetchesRef.current.add(nextBookPageKey);
|
pendingFetchesRef.current.add(nextBookPageKey);
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
@@ -329,6 +346,7 @@ export function useImageLoader({
|
|||||||
prefetchImage,
|
prefetchImage,
|
||||||
prefetchPages,
|
prefetchPages,
|
||||||
prefetchNextBook,
|
prefetchNextBook,
|
||||||
|
cancelAllPrefetches,
|
||||||
handleForceReload,
|
handleForceReload,
|
||||||
getPageUrl,
|
getPageUrl,
|
||||||
prefetchCount,
|
prefetchCount,
|
||||||
|
|||||||
Reference in New Issue
Block a user