refactor: streamline ClientLayout and PreferencesContext for improved state management and debugging
This commit is contained in:
@@ -7,7 +7,6 @@ import { PreferencesService } from "@/lib/services/preferences.service";
|
|||||||
import { PreferencesProvider } from "@/contexts/PreferencesContext";
|
import { PreferencesProvider } from "@/contexts/PreferencesContext";
|
||||||
import { I18nProvider } from "@/components/providers/I18nProvider";
|
import { I18nProvider } from "@/components/providers/I18nProvider";
|
||||||
import { AuthProvider } from "@/components/providers/AuthProvider";
|
import { AuthProvider } from "@/components/providers/AuthProvider";
|
||||||
import "@/i18n/i18n"; // Import i18next configuration
|
|
||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
import { defaultPreferences } from "@/types/preferences";
|
import { defaultPreferences } from "@/types/preferences";
|
||||||
import type { UserPreferences } from "@/types/preferences";
|
import type { UserPreferences } from "@/types/preferences";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ThemeProvider } from "next-themes";
|
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 { Header } from "@/components/layout/Header";
|
||||||
import { Sidebar } from "@/components/layout/Sidebar";
|
import { Sidebar } from "@/components/layout/Sidebar";
|
||||||
import { InstallPWA } from "../ui/InstallPWA";
|
import { InstallPWA } from "../ui/InstallPWA";
|
||||||
@@ -28,17 +28,35 @@ export default function ClientLayout({ children, initialLibraries = [], initialF
|
|||||||
const [randomBookId, setRandomBookId] = useState<string | null>(null);
|
const [randomBookId, setRandomBookId] = useState<string | null>(null);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { preferences } = usePreferences();
|
const { preferences } = usePreferences();
|
||||||
|
const prevLibraryIdsRef = useRef<string>("");
|
||||||
|
|
||||||
|
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
|
// Récupérer un book aléatoire pour le background
|
||||||
const fetchRandomBook = useCallback(async () => {
|
const fetchRandomBook = useCallback(async () => {
|
||||||
if (
|
if (backgroundType === "komga-random" && libraryIdsString) {
|
||||||
preferences.background.type === "komga-random" &&
|
|
||||||
preferences.background.komgaLibraries &&
|
|
||||||
preferences.background.komgaLibraries.length > 0
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
const libraryIds = preferences.background.komgaLibraries.join(",");
|
const response = await fetch(`/api/komga/random-book?libraryIds=${libraryIdsString}`);
|
||||||
const response = await fetch(`/api/komga/random-book?libraryIds=${libraryIds}`);
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setRandomBookId(data.bookId);
|
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);
|
console.error("Erreur lors de la récupération d'un book aléatoire:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [preferences.background.type, preferences.background.komgaLibraries]);
|
}, [backgroundType, libraryIdsString]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchRandomBook();
|
if (backgroundType === "komga-random" && libraryIdsString) {
|
||||||
}, [fetchRandomBook]);
|
fetchRandomBook();
|
||||||
|
}
|
||||||
|
}, [backgroundType, libraryIdsString, fetchRandomBook]);
|
||||||
|
|
||||||
const backgroundStyle = useMemo(() => {
|
const backgroundStyle = useMemo(() => {
|
||||||
const bg = preferences.background;
|
const bg = preferences.background;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { CoverClient } from "./cover-client";
|
import { CoverClient } from "./cover-client";
|
||||||
import { ProgressBar } from "./progress-bar";
|
import { ProgressBar } from "./progress-bar";
|
||||||
import type { BookCoverProps } from "./cover-utils";
|
import type { BookCoverProps } from "./cover-utils";
|
||||||
import { getImageUrl } from "./cover-utils";
|
import { getImageUrl } from "@/lib/utils/image-url";
|
||||||
import { useImageUrl } from "@/hooks/useImageUrl";
|
import { useImageUrl } from "@/hooks/useImageUrl";
|
||||||
import { ClientOfflineBookService } from "@/lib/services/client-offlinebook.service";
|
import { ClientOfflineBookService } from "@/lib/services/client-offlinebook.service";
|
||||||
import { MarkAsReadButton } from "./mark-as-read-button";
|
import { MarkAsReadButton } from "./mark-as-read-button";
|
||||||
|
|||||||
@@ -19,14 +19,3 @@ export interface BookCoverProps extends BaseCoverProps {
|
|||||||
export interface SeriesCoverProps extends BaseCoverProps {
|
export interface SeriesCoverProps extends BaseCoverProps {
|
||||||
series: KomgaSeries;
|
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`;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { CoverClient } from "./cover-client";
|
import { CoverClient } from "./cover-client";
|
||||||
import { ProgressBar } from "./progress-bar";
|
import { ProgressBar } from "./progress-bar";
|
||||||
import type { SeriesCoverProps } from "./cover-utils";
|
import type { SeriesCoverProps } from "./cover-utils";
|
||||||
import { getImageUrl } from "./cover-utils";
|
import { getImageUrl } from "@/lib/utils/image-url";
|
||||||
import { useImageUrl } from "@/hooks/useImageUrl";
|
import { useImageUrl } from "@/hooks/useImageUrl";
|
||||||
|
|
||||||
export function SeriesCover({
|
export function SeriesCover({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"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 { useSession } from "next-auth/react";
|
||||||
import { ERROR_CODES } from "../constants/errorCodes";
|
import { ERROR_CODES } from "../constants/errorCodes";
|
||||||
import { AppError } from "../utils/errors";
|
import { AppError } from "../utils/errors";
|
||||||
@@ -27,6 +27,12 @@ export function PreferencesProvider({
|
|||||||
initialPreferences || defaultPreferences
|
initialPreferences || defaultPreferences
|
||||||
);
|
);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
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 () => {
|
const fetchPreferences = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -39,6 +45,7 @@ export function PreferencesProvider({
|
|||||||
...defaultPreferences,
|
...defaultPreferences,
|
||||||
...data,
|
...data,
|
||||||
});
|
});
|
||||||
|
setHasLoadedPrefs(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors de la récupération des préférences:", error);
|
console.error("Erreur lors de la récupération des préférences:", error);
|
||||||
setPreferences(defaultPreferences);
|
setPreferences(defaultPreferences);
|
||||||
@@ -49,15 +56,16 @@ export function PreferencesProvider({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Recharger les préférences quand la session change (connexion/déconnexion)
|
// Recharger les préférences quand la session change (connexion/déconnexion)
|
||||||
if (status === "authenticated") {
|
if (status === "authenticated" && !hasLoadedPrefs) {
|
||||||
fetchPreferences();
|
fetchPreferences();
|
||||||
} else if (status === "unauthenticated") {
|
} else if (status === "unauthenticated") {
|
||||||
// Réinitialiser aux préférences par défaut quand l'utilisateur se déconnecte
|
// Réinitialiser aux préférences par défaut quand l'utilisateur se déconnecte
|
||||||
setPreferences(defaultPreferences);
|
setPreferences(defaultPreferences);
|
||||||
|
setHasLoadedPrefs(false);
|
||||||
}
|
}
|
||||||
}, [status]);
|
}, [status, hasLoadedPrefs]);
|
||||||
|
|
||||||
const updatePreferences = async (newPreferences: Partial<UserPreferences>) => {
|
const updatePreferences = useCallback(async (newPreferences: Partial<UserPreferences>) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/preferences", {
|
const response = await fetch("/api/preferences", {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
@@ -78,17 +86,20 @@ export function PreferencesProvider({
|
|||||||
...updatedPreferences,
|
...updatedPreferences,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await fetchPreferences();
|
|
||||||
|
|
||||||
return updatedPreferences;
|
return updatedPreferences;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors de la mise à jour des préférences:", error);
|
console.error("Erreur lors de la mise à jour des préférences:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
const contextValue = useMemo(
|
||||||
|
() => ({ preferences, updatePreferences, isLoading }),
|
||||||
|
[preferences, updatePreferences, isLoading]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PreferencesContext.Provider value={{ preferences, updatePreferences, isLoading }}>
|
<PreferencesContext.Provider value={contextValue}>
|
||||||
{children}
|
{children}
|
||||||
</PreferencesContext.Provider>
|
</PreferencesContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
11
src/lib/utils/image-url.ts
Normal file
11
src/lib/utils/image-url.ts
Normal file
@@ -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`;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user