refacto(thumbnails): revew thumbnails and caching

This commit is contained in:
Julien Froidefond
2025-02-16 16:02:07 +01:00
parent 683a4821f3
commit 938e0461ae
2 changed files with 57 additions and 11 deletions

View File

@@ -2,7 +2,7 @@ import { ThumbnailProps } from "../types";
import { ImageLoader } from "@/components/ui/image-loader";
import { cn } from "@/lib/utils";
import Image from "next/image";
import { forwardRef, useEffect, useState } from "react";
import { forwardRef, useEffect, useState, useCallback, useRef } from "react";
export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>(
(
@@ -19,23 +19,60 @@ export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>(
) => {
const [imageUrl, setImageUrl] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
const loadAttempts = useRef(0);
const maxAttempts = 3;
const resetLoadingState = useCallback(() => {
setIsLoading(true);
setHasError(false);
loadAttempts.current = 0;
}, []);
useEffect(() => {
if (isVisible) {
try {
const url = getThumbnailUrl(pageNumber);
setImageUrl(url);
// On réinitialise le loading uniquement si la miniature n'est pas déjà chargée
if (!loadedThumbnails[pageNumber]) {
setIsLoading(true);
resetLoadingState();
} else {
setIsLoading(false);
}
} catch (error) {
console.error(`Erreur lors du chargement de la miniature ${pageNumber}:`, error);
setHasError(true);
setIsLoading(false);
}
}, [isVisible, pageNumber, getThumbnailUrl, loadedThumbnails]);
}, [pageNumber, getThumbnailUrl, loadedThumbnails, resetLoadingState]);
const handleImageLoad = () => {
const handleImageLoad = useCallback(() => {
setIsLoading(false);
setHasError(false);
if (!loadedThumbnails[pageNumber]) {
onThumbnailLoad(pageNumber);
}
};
}, [loadedThumbnails, pageNumber, onThumbnailLoad]);
const handleImageError = useCallback(() => {
loadAttempts.current += 1;
if (loadAttempts.current < maxAttempts) {
// Réessayer avec un délai croissant
const delay = Math.min(1000 * Math.pow(2, loadAttempts.current - 1), 5000);
console.log(
`Réessai ${loadAttempts.current}/${maxAttempts} dans ${delay}ms pour la page ${pageNumber}`
);
setTimeout(() => {
setImageUrl((prev) => (prev ? `${prev}?retry=${loadAttempts.current}` : null));
}, delay);
} else {
console.error(
`Échec du chargement de l'image pour la page ${pageNumber} après ${maxAttempts} tentatives`
);
setHasError(true);
setIsLoading(false);
}
}, [pageNumber]);
return (
<button
@@ -43,14 +80,16 @@ export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>(
data-page={pageNumber}
id={`thumbnail-${pageNumber}`}
onClick={() => onPageChange(pageNumber)}
className={`relative h-56 w-40 flex-shrink-0 rounded-md overflow-hidden transition-all cursor-pointer snap-center ${
className={cn(
"relative h-56 w-40 flex-shrink-0 rounded-md overflow-hidden transition-all cursor-pointer snap-center",
currentPage === pageNumber
? "ring-2 ring-primary scale-110 z-10"
: "opacity-80 hover:opacity-100 hover:scale-105"
}`}
: "opacity-80 hover:opacity-100 hover:scale-105",
hasError && "bg-muted"
)}
>
<ImageLoader isLoading={isLoading} />
{isVisible && imageUrl && (
{imageUrl && !hasError && (
<Image
src={imageUrl}
alt={`Miniature page ${pageNumber}`}
@@ -63,8 +102,14 @@ export const Thumbnail = forwardRef<HTMLButtonElement, ThumbnailProps>(
loading="lazy"
quality={50}
onLoad={handleImageLoad}
onError={handleImageError}
/>
)}
{hasError && (
<div className="absolute inset-0 flex items-center justify-center bg-muted">
<span className="text-sm text-muted-foreground">Erreur</span>
</div>
)}
<div className="absolute bottom-0 inset-x-0 h-8 bg-gradient-to-t from-black/60 to-transparent flex items-center justify-center">
<span className="text-sm text-white font-medium">{pageNumber}</span>
</div>

View File

@@ -12,6 +12,7 @@ class ServerCacheService {
private static readonly tenMinutes = 10 * 60;
private static readonly twentyFourHours = 24 * 60 * 60;
private static readonly oneMinute = 1 * 60;
private static readonly oneWeek = 7 * 24 * 60 * 60;
private static readonly noCache = 0;
// Configuration des temps de cache en secondes
@@ -21,7 +22,7 @@ class ServerCacheService {
LIBRARIES: ServerCacheService.twentyFourHours,
SERIES: ServerCacheService.fiveMinutes,
BOOKS: ServerCacheService.fiveMinutes,
IMAGES: ServerCacheService.twentyFourHours,
IMAGES: ServerCacheService.oneWeek,
};
private constructor() {