diff --git a/src/components/reader/PhotoswipeReader.tsx b/src/components/reader/PhotoswipeReader.tsx index 5d64b7b..09bf816 100644 --- a/src/components/reader/PhotoswipeReader.tsx +++ b/src/components/reader/PhotoswipeReader.tsx @@ -29,6 +29,8 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP const [showThumbnails, setShowThumbnails] = useState(false); const [showEndMessage, setShowEndMessage] = useState(false); const [loadedImages, setLoadedImages] = useState>({}); + const [isLoading, setIsLoading] = useState(true); + const [secondPageLoading, setSecondPageLoading] = useState(true); const isLandscape = useOrientation(); const { direction, toggleDirection, isRTL } = useReadingDirection(); const { isFullscreen, toggleFullscreen } = useFullscreen(); @@ -48,6 +50,12 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP setIsDoublePage(isLandscape); }, [isLandscape]); + // Reset loading quand le mode double page change + useEffect(() => { + setIsLoading(true); + setSecondPageLoading(true); + }, [isDoublePage]); + const shouldShowDoublePage = useCallback( (pageNumber: number) => { const isMobile = window.innerHeight < 700; @@ -91,6 +99,8 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP (page: number) => { if (page >= 1 && page <= pages.length) { setCurrentPage(page); + setIsLoading(true); + setSecondPageLoading(true); // Mettre à jour le localStorage immédiatement ClientOfflineBookService.setCurrentPage(book, page); // Débouncer seulement l'API Komga @@ -371,11 +381,30 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP } )} > + {isLoading && ( +
+
+
+
+
+
+ )} {`Page setIsLoading(false)} + onError={() => setIsLoading(false)} + ref={(img) => { + // Si l'image est déjà en cache, onLoad ne sera pas appelé + if (img?.complete && img?.naturalHeight !== 0) { + setIsLoading(false); + } + }} /> @@ -390,11 +419,30 @@ export function PhotoswipeReader({ book, pages, onClose, nextBook }: BookReaderP } )} > + {secondPageLoading && ( +
+
+
+
+
+
+ )} {`Page setSecondPageLoading(false)} + onError={() => setSecondPageLoading(false)} + ref={(img) => { + // Si l'image est déjà en cache, onLoad ne sera pas appelé + if (img?.complete && img?.naturalHeight !== 0) { + setSecondPageLoading(false); + } + }} /> )} diff --git a/src/styles/globals.css b/src/styles/globals.css index b0ee781..ab98bc4 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -116,4 +116,18 @@ body { backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); } + + /* Fade-in animation */ + @keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + .animate-fade-in { + animation: fade-in 0.3s ease-in forwards; + } }