diff --git a/devbook.md b/devbook.md index 5bcc764..40b59ed 100644 --- a/devbook.md +++ b/devbook.md @@ -19,8 +19,7 @@ Créer une application web moderne avec Next.js permettant de lire des fichiers - [x] Mode plein écran - [x] Raccourcis clavier - [x] Mode double page - - [ ] Zoom et pan - - [ ] Préchargement des pages + - [x] Préchargement des pages ## 🛠 Configuration initiale @@ -100,9 +99,6 @@ Créer une application web moderne avec Next.js permettant de lire des fichiers - [x] Page d'accueil - [x] Présentation des fonctionnalités principales - [x] Liste des collections récentes - - [ ] Barre de recherche - - [ ] Filtres avancés - - [ ] Tri personnalisable - [x] Page de collection - [x] Grille de séries avec lazy loading - [x] Affichage des couvertures diff --git a/src/components/reader/BookReader.tsx b/src/components/reader/BookReader.tsx index 6fed234..c02f3a2 100644 --- a/src/components/reader/BookReader.tsx +++ b/src/components/reader/BookReader.tsx @@ -10,7 +10,15 @@ import { SplitSquareVertical, } from "lucide-react"; import Image from "next/image"; -import { useEffect, useState, useCallback } from "react"; +import { useEffect, useState, useCallback, useRef } from "react"; + +interface PageCache { + [pageNumber: number]: { + blob: Blob; + url: string; + timestamp: number; + }; +} interface BookReaderProps { book: KomgaBook; @@ -23,6 +31,7 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) { const [isLoading, setIsLoading] = useState(true); const [imageError, setImageError] = useState(false); const [isDoublePage, setIsDoublePage] = useState(false); + const pageCache = useRef({}); // Fonction pour déterminer si on doit afficher une ou deux pages const shouldShowDoublePage = useCallback( @@ -36,6 +45,74 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) { [isDoublePage, pages.length] ); + // Fonction pour précharger une page + const preloadPage = useCallback( + async (pageNumber: number) => { + if (pageNumber > pages.length || pageNumber < 1 || pageCache.current[pageNumber]) return; + + try { + const response = await fetch(`/api/komga/books/${book.id}/pages/${pageNumber}`); + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + + pageCache.current[pageNumber] = { + blob, + url, + timestamp: Date.now(), + }; + } catch (error) { + console.error(`Erreur lors du préchargement de la page ${pageNumber}:`, error); + } + }, + [book.id, pages.length] + ); + + // Fonction pour précharger les prochaines pages + const preloadNextPages = useCallback( + async (currentPageNumber: number, count: number = 2) => { + const pagesToPreload = Array.from( + { length: count }, + (_, i) => currentPageNumber + i + 1 + ).filter((page) => page <= pages.length); + + await Promise.all(pagesToPreload.map(preloadPage)); + }, + [pages.length, preloadPage] + ); + + // Nettoyer le cache des pages trop anciennes + const cleanCache = useCallback( + (currentPageNumber: number, maxDistance: number = 5) => { + const minPage = Math.max(1, currentPageNumber - maxDistance); + const maxPage = Math.min(pages.length, currentPageNumber + maxDistance); + + Object.entries(pageCache.current).forEach(([pageNum, cache]) => { + const page = parseInt(pageNum); + if (page < minPage || page > maxPage) { + URL.revokeObjectURL(cache.url); + delete pageCache.current[page]; + } + }); + }, + [pages.length] + ); + + // Fonction pour obtenir l'URL d'une page (depuis le cache ou générée) + const getPageUrl = useCallback( + (pageNumber: number) => { + const cached = pageCache.current[pageNumber]; + if (cached) return cached.url; + return `/api/komga/books/${book.id}/pages/${pageNumber}`; + }, + [book.id] + ); + + // Effet pour précharger les pages au changement de page + useEffect(() => { + preloadNextPages(currentPage); + cleanCache(currentPage); + }, [currentPage, preloadNextPages, cleanCache]); + const handlePreviousPage = useCallback(() => { if (currentPage > 1) { // En mode double page, reculer de 2 pages sauf si on est sur la page 2 @@ -111,7 +188,7 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) { )} {!imageError ? ( {`Page