diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3d4fd57..2a86b96 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,7 +7,6 @@ import { PreferencesService } from "@/lib/services/preferences.service"; import { PreferencesProvider } from "@/contexts/PreferencesContext"; import { I18nProvider } from "@/components/providers/I18nProvider"; import { AuthProvider } from "@/components/providers/AuthProvider"; -import "@/i18n/i18n"; // Import i18next configuration import { cookies } from "next/headers"; import { defaultPreferences } from "@/types/preferences"; import type { UserPreferences } from "@/types/preferences"; diff --git a/src/components/layout/ClientLayout.tsx b/src/components/layout/ClientLayout.tsx index 954d41b..2ac6de3 100644 --- a/src/components/layout/ClientLayout.tsx +++ b/src/components/layout/ClientLayout.tsx @@ -1,7 +1,7 @@ "use client"; import { ThemeProvider } from "next-themes"; -import { useState, useEffect, useMemo, useCallback } from "react"; +import { useState, useEffect, useMemo, useCallback, useRef } from "react"; import { Header } from "@/components/layout/Header"; import { Sidebar } from "@/components/layout/Sidebar"; import { InstallPWA } from "../ui/InstallPWA"; @@ -28,17 +28,35 @@ export default function ClientLayout({ children, initialLibraries = [], initialF const [randomBookId, setRandomBookId] = useState(null); const pathname = usePathname(); const { preferences } = usePreferences(); + const prevLibraryIdsRef = useRef(""); + + const backgroundType = preferences.background.type; + const komgaLibraries = preferences.background.komgaLibraries; + + // Debug: log renders + useEffect(() => { + console.log('🎨 [ClientLayout] Render:', { + pathname, + backgroundType, + hasLibraries: !!komgaLibraries?.length, + preferencesKeys: Object.keys(preferences) + }); + }); + + // Stabiliser libraryIds - ne change que si le contenu change vraiment + const libraryIdsString = useMemo(() => { + const newIds = komgaLibraries?.join(",") || ""; + if (newIds !== prevLibraryIdsRef.current) { + prevLibraryIdsRef.current = newIds; + } + return prevLibraryIdsRef.current; + }, [komgaLibraries]); // Récupérer un book aléatoire pour le background const fetchRandomBook = useCallback(async () => { - if ( - preferences.background.type === "komga-random" && - preferences.background.komgaLibraries && - preferences.background.komgaLibraries.length > 0 - ) { + if (backgroundType === "komga-random" && libraryIdsString) { try { - const libraryIds = preferences.background.komgaLibraries.join(","); - const response = await fetch(`/api/komga/random-book?libraryIds=${libraryIds}`); + const response = await fetch(`/api/komga/random-book?libraryIds=${libraryIdsString}`); if (response.ok) { const data = await response.json(); setRandomBookId(data.bookId); @@ -47,11 +65,13 @@ export default function ClientLayout({ children, initialLibraries = [], initialF console.error("Erreur lors de la récupération d'un book aléatoire:", error); } } - }, [preferences.background.type, preferences.background.komgaLibraries]); + }, [backgroundType, libraryIdsString]); useEffect(() => { - fetchRandomBook(); - }, [fetchRandomBook]); + if (backgroundType === "komga-random" && libraryIdsString) { + fetchRandomBook(); + } + }, [backgroundType, libraryIdsString, fetchRandomBook]); const backgroundStyle = useMemo(() => { const bg = preferences.background; diff --git a/src/components/ui/book-cover.tsx b/src/components/ui/book-cover.tsx index 2a7525e..ff21bcb 100644 --- a/src/components/ui/book-cover.tsx +++ b/src/components/ui/book-cover.tsx @@ -3,7 +3,7 @@ import { CoverClient } from "./cover-client"; import { ProgressBar } from "./progress-bar"; import type { BookCoverProps } from "./cover-utils"; -import { getImageUrl } from "./cover-utils"; +import { getImageUrl } from "@/lib/utils/image-url"; import { useImageUrl } from "@/hooks/useImageUrl"; import { ClientOfflineBookService } from "@/lib/services/client-offlinebook.service"; import { MarkAsReadButton } from "./mark-as-read-button"; diff --git a/src/components/ui/cover-utils.tsx b/src/components/ui/cover-utils.tsx index 4873841..1b06c8b 100644 --- a/src/components/ui/cover-utils.tsx +++ b/src/components/ui/cover-utils.tsx @@ -19,14 +19,3 @@ export interface BookCoverProps extends BaseCoverProps { export interface SeriesCoverProps extends BaseCoverProps { series: KomgaSeries; } - -/** - * Génère l'URL de base pour une image (sans cache version) - * Utilisez useImageUrl() dans les composants pour obtenir l'URL avec cache busting - */ -export function getImageUrl(type: "series" | "book", id: string) { - if (type === "series") { - return `/api/komga/images/series/${id}/thumbnail`; - } - return `/api/komga/images/books/${id}/thumbnail`; -} diff --git a/src/components/ui/series-cover.tsx b/src/components/ui/series-cover.tsx index 2a6af6f..a14c162 100644 --- a/src/components/ui/series-cover.tsx +++ b/src/components/ui/series-cover.tsx @@ -3,7 +3,7 @@ import { CoverClient } from "./cover-client"; import { ProgressBar } from "./progress-bar"; import type { SeriesCoverProps } from "./cover-utils"; -import { getImageUrl } from "./cover-utils"; +import { getImageUrl } from "@/lib/utils/image-url"; import { useImageUrl } from "@/hooks/useImageUrl"; export function SeriesCover({ diff --git a/src/contexts/PreferencesContext.tsx b/src/contexts/PreferencesContext.tsx index 58e2d8d..a53f4cf 100644 --- a/src/contexts/PreferencesContext.tsx +++ b/src/contexts/PreferencesContext.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { createContext, useContext, useState, useEffect } from "react"; +import React, { createContext, useContext, useState, useEffect, useMemo, useCallback } from "react"; import { useSession } from "next-auth/react"; import { ERROR_CODES } from "../constants/errorCodes"; import { AppError } from "../utils/errors"; @@ -27,6 +27,12 @@ export function PreferencesProvider({ initialPreferences || defaultPreferences ); const [isLoading, setIsLoading] = useState(false); + const [hasLoadedPrefs, setHasLoadedPrefs] = useState(!!initialPreferences); + + // Debug: log state changes + useEffect(() => { + console.log('⚙️ [PreferencesContext] Update:', { status, hasLoadedPrefs, isLoading }); + }, [status, hasLoadedPrefs, isLoading]); const fetchPreferences = async () => { try { @@ -39,6 +45,7 @@ export function PreferencesProvider({ ...defaultPreferences, ...data, }); + setHasLoadedPrefs(true); } catch (error) { console.error("Erreur lors de la récupération des préférences:", error); setPreferences(defaultPreferences); @@ -49,15 +56,16 @@ export function PreferencesProvider({ useEffect(() => { // Recharger les préférences quand la session change (connexion/déconnexion) - if (status === "authenticated") { + if (status === "authenticated" && !hasLoadedPrefs) { fetchPreferences(); } else if (status === "unauthenticated") { // Réinitialiser aux préférences par défaut quand l'utilisateur se déconnecte setPreferences(defaultPreferences); + setHasLoadedPrefs(false); } - }, [status]); + }, [status, hasLoadedPrefs]); - const updatePreferences = async (newPreferences: Partial) => { + const updatePreferences = useCallback(async (newPreferences: Partial) => { try { const response = await fetch("/api/preferences", { method: "PUT", @@ -78,17 +86,20 @@ export function PreferencesProvider({ ...updatedPreferences, })); - await fetchPreferences(); - return updatedPreferences; } catch (error) { console.error("Erreur lors de la mise à jour des préférences:", error); throw error; } - }; + }, []); + + const contextValue = useMemo( + () => ({ preferences, updatePreferences, isLoading }), + [preferences, updatePreferences, isLoading] + ); return ( - + {children} ); diff --git a/src/lib/utils/image-url.ts b/src/lib/utils/image-url.ts new file mode 100644 index 0000000..ec62949 --- /dev/null +++ b/src/lib/utils/image-url.ts @@ -0,0 +1,11 @@ +/** + * Génère l'URL de base pour une image (sans cache version) + * Utilisez useImageUrl() dans les composants pour obtenir l'URL avec cache busting + */ +export function getImageUrl(type: "series" | "book", id: string) { + if (type === "series") { + return `/api/komga/images/series/${id}/thumbnail`; + } + return `/api/komga/images/books/${id}/thumbnail`; +} +