diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index c323d9e..92400bc 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -3,10 +3,17 @@ import { Inter } from "next/font/google";
import "@/styles/globals.css";
import { cn } from "@/lib/utils";
import ClientLayout from "@/components/layout/ClientLayout";
+import { PreferencesService } from "@/lib/services/preferences.service";
import { PreferencesProvider } from "@/contexts/PreferencesContext";
import { I18nProvider } from "@/components/providers/I18nProvider";
import "@/i18n/i18n"; // Import i18next configuration
import { cookies } from "next/headers";
+import { defaultPreferences } from "@/types/preferences";
+import type { UserPreferences } from "@/types/preferences";
+import type { KomgaLibrary, KomgaSeries } from "@/types/komga";
+import { FavoriteService } from "@/lib/services/favorite.service";
+import { LibraryService } from "@/lib/services/library.service";
+import { SeriesService } from "@/lib/services/series.service";
const inter = Inter({ subsets: ["latin"] });
@@ -62,6 +69,41 @@ export default async function RootLayout({ children }: { children: React.ReactNo
const cookieStore = await cookies();
const locale = cookieStore.get("NEXT_LOCALE")?.value || "fr";
+ // Récupération des données pour la sidebar côté serveur
+ let libraries: KomgaLibrary[] = [];
+ let favorites: KomgaSeries[] = [];
+ let preferences: UserPreferences = defaultPreferences;
+
+ try {
+ // Tentative de chargement des données. Si l'utilisateur n'est pas authentifié,
+ // les services lanceront une erreur mais l'application continuera de fonctionner
+ const [librariesData, favoritesData, preferencesData] = await Promise.allSettled([
+ LibraryService.getLibraries(),
+ FavoriteService.getAllFavoriteIds(),
+ PreferencesService.getPreferences(),
+ ]);
+
+ if (librariesData.status === "fulfilled") {
+ libraries = librariesData.value;
+ }
+
+ if (favoritesData.status === "fulfilled") {
+ favorites = await SeriesService.getMultipleSeries(favoritesData.value);
+ }
+
+ if (preferencesData.status === "fulfilled") {
+ const { showThumbnails, cacheMode, showOnlyUnread, debug } = preferencesData.value;
+ preferences = {
+ showThumbnails,
+ cacheMode,
+ showOnlyUnread,
+ debug,
+ };
+ }
+ } catch (error) {
+ console.error("Erreur lors du chargement des données de la sidebar:", error);
+ }
+
return (
@@ -123,8 +165,10 @@ export default async function RootLayout({ children }: { children: React.ReactNo
className={cn("min-h-screen bg-background font-sans antialiased h-full", inter.className)}
>
-
- {children}
+
+
+ {children}
+
diff --git a/src/components/layout/ClientLayout.tsx b/src/components/layout/ClientLayout.tsx
index 924c968..f7aa78f 100644
--- a/src/components/layout/ClientLayout.tsx
+++ b/src/components/layout/ClientLayout.tsx
@@ -7,16 +7,22 @@ import { Sidebar } from "@/components/layout/Sidebar";
import { InstallPWA } from "../ui/InstallPWA";
import { Toaster } from "@/components/ui/toaster";
import { usePathname } from "next/navigation";
-import { PreferencesProvider } from "@/contexts/PreferencesContext";
import { registerServiceWorker } from "@/lib/registerSW";
import { NetworkStatus } from "../ui/NetworkStatus";
import { LoadingBar } from "@/components/ui/loading-bar";
import { DebugWrapper } from "@/components/debug/DebugWrapper";
+import type { KomgaLibrary, KomgaSeries } from "@/types/komga";
// Routes qui ne nécessitent pas d'authentification
const publicRoutes = ["/login", "/register"];
-export default function ClientLayout({ children }: { children: React.ReactNode }) {
+interface ClientLayoutProps {
+ children: React.ReactNode;
+ initialLibraries: KomgaLibrary[];
+ initialFavorites: KomgaSeries[];
+}
+
+export default function ClientLayout({ children, initialLibraries = [], initialFavorites = [] }: ClientLayoutProps) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const pathname = usePathname();
@@ -63,18 +69,23 @@ export default function ClientLayout({ children }: { children: React.ReactNode }
return (
-
{!isPublicRoute && }
- {!isPublicRoute && }
+ {!isPublicRoute && (
+
+ )}
{children}
-
);
}
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index 7543a10..7e69c90 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -16,23 +16,23 @@ import { useTranslate } from "@/hooks/useTranslate";
interface SidebarProps {
isOpen: boolean;
onClose: () => void;
+ initialLibraries: KomgaLibrary[];
+ initialFavorites: KomgaSeries[];
}
-export function Sidebar({ isOpen, onClose }: SidebarProps) {
+export function Sidebar({ isOpen, onClose, initialLibraries, initialFavorites }: SidebarProps) {
const { t } = useTranslate();
const pathname = usePathname();
const router = useRouter();
const { preferences } = usePreferences();
- const [libraries, setLibraries] = useState([]);
- const [favorites, setFavorites] = useState([]);
- const [isLoading, setIsLoading] = useState(true);
+ const [libraries, setLibraries] = useState(initialLibraries || []);
+ const [favorites, setFavorites] = useState(initialFavorites || []);
const [isRefreshing, setIsRefreshing] = useState(false);
- const [isLoadingFavorites, setIsLoadingFavorites] = useState(true);
const { toast } = useToast();
- const fetchLibraries = useCallback(async () => {
- setIsLoading(true);
+ const refreshLibraries = useCallback(async () => {
+ setIsRefreshing(true);
try {
const response = await fetch("/api/komga/libraries");
if (!response.ok) {
@@ -50,15 +50,12 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
: getErrorMessage(ERROR_CODES.LIBRARY.FETCH_ERROR),
variant: "destructive",
});
- setLibraries([]);
} finally {
- setIsLoading(false);
setIsRefreshing(false);
}
}, [toast]);
- const fetchFavorites = useCallback(async () => {
- setIsLoadingFavorites(true);
+ const refreshFavorites = useCallback(async () => {
try {
const favoritesResponse = await fetch("/api/komga/favorites");
if (!favoritesResponse.ok) {
@@ -91,28 +88,20 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
: getErrorMessage(ERROR_CODES.FAVORITE.FETCH_ERROR),
variant: "destructive",
});
- setFavorites([]);
- } finally {
- setIsLoadingFavorites(false);
}
}, [toast]);
- // Chargement initial des données
useEffect(() => {
- fetchLibraries();
- fetchFavorites();
- }, [fetchLibraries, fetchFavorites]);
-
- // Rafraîchir les données quand les préférences changent
- useEffect(() => {
- fetchLibraries();
- fetchFavorites();
- }, [preferences, fetchLibraries, fetchFavorites]);
+ if (Object.keys(preferences).length > 0) {
+ refreshLibraries();
+ refreshFavorites();
+ }
+ }, [preferences, refreshLibraries, refreshFavorites]);
// Mettre à jour les favoris quand ils changent
useEffect(() => {
const handleFavoritesChange = () => {
- fetchFavorites();
+ refreshFavorites();
};
window.addEventListener("favoritesChanged", handleFavoritesChange);
@@ -120,11 +109,11 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
return () => {
window.removeEventListener("favoritesChanged", handleFavoritesChange);
};
- }, [fetchFavorites]);
+ }, [refreshFavorites]);
const handleRefresh = async () => {
setIsRefreshing(true);
- await Promise.all([fetchLibraries(), fetchFavorites()]);
+ await Promise.all([refreshLibraries(), refreshFavorites()]);
};
const handleLogout = async () => {
@@ -217,7 +206,7 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
{favorites.length}
- {isLoadingFavorites ? (
+ {isRefreshing ? (
{t("sidebar.favorites.loading")}
@@ -258,7 +247,7 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
- {isLoading ? (
+ {isRefreshing ? (
{t("sidebar.libraries.loading")}
diff --git a/src/components/layout/SidebarWrapper.tsx b/src/components/layout/SidebarWrapper.tsx
deleted file mode 100644
index 6ac09a2..0000000
--- a/src/components/layout/SidebarWrapper.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { FavoriteService } from "@/lib/services/favorite.service";
-import { LibraryService } from "@/lib/services/library.service";
-import { SeriesService } from "@/lib/services/series.service";
-
-export async function SidebarWrapper() {
- // Récupérer les favoris depuis le serveur
- const favoriteIds = await FavoriteService.getAllFavoriteIds();
-
- // Récupérer les détails des séries favorites
- const favorites = await SeriesService.getMultipleSeries(favoriteIds);
-
- // Récupérer les bibliothèques
- const libraries = await LibraryService.getLibraries();
-
- return { favorites, libraries };
-}
diff --git a/src/contexts/PreferencesContext.tsx b/src/contexts/PreferencesContext.tsx
index caeb5a1..e919bcb 100644
--- a/src/contexts/PreferencesContext.tsx
+++ b/src/contexts/PreferencesContext.tsx
@@ -1,9 +1,9 @@
"use client";
-import React, { createContext, useContext, useEffect, useState } from "react";
+import React, { createContext, useContext, useState } from "react";
import { ERROR_CODES } from "../constants/errorCodes";
import { AppError } from "../utils/errors";
-import type { UserPreferences} from "@/types/preferences";
+import type { UserPreferences } from "@/types/preferences";
import { defaultPreferences } from "@/types/preferences";
interface PreferencesContextType {
@@ -14,8 +14,16 @@ interface PreferencesContextType {
const PreferencesContext = createContext(undefined);
-export function PreferencesProvider({ children }: { children: React.ReactNode }) {
- const [preferences, setPreferences] = useState(defaultPreferences);
+export function PreferencesProvider({
+ children,
+ initialPreferences,
+}: {
+ children: React.ReactNode;
+ initialPreferences?: UserPreferences;
+}) {
+ const [preferences, setPreferences] = useState(
+ initialPreferences || defaultPreferences
+ );
const [isLoading, setIsLoading] = useState(true);
const fetchPreferences = async () => {
@@ -37,10 +45,6 @@ export function PreferencesProvider({ children }: { children: React.ReactNode })
}
};
- useEffect(() => {
- fetchPreferences();
- }, []);
-
const updatePreferences = async (newPreferences: Partial) => {
try {