diff --git a/src/app/libraries/[libraryId]/page.tsx b/src/app/libraries/[libraryId]/page.tsx index a3787eb..c346a51 100644 --- a/src/app/libraries/[libraryId]/page.tsx +++ b/src/app/libraries/[libraryId]/page.tsx @@ -1,5 +1,6 @@ import { PaginatedSeriesGrid } from "@/components/library/PaginatedSeriesGrid"; import { LibraryService } from "@/lib/services/library.service"; +import { PreferencesService } from "@/lib/services/preferences.service"; interface PageProps { params: { libraryId: string }; @@ -28,7 +29,11 @@ async function getLibrarySeries(libraryId: string, page: number = 1, unreadOnly: export default async function LibraryPage({ params, searchParams }: PageProps) { const currentPage = searchParams.page ? parseInt(searchParams.page) : 1; - const unreadOnly = searchParams.unread === "true"; + const preferences = await PreferencesService.getPreferences(); + + // Utiliser le paramètre d'URL s'il existe, sinon utiliser la préférence utilisateur + const unreadOnly = + searchParams.unread !== undefined ? searchParams.unread === "true" : preferences.showOnlyUnread; try { const { data: series, library } = await getLibrarySeries( @@ -53,6 +58,8 @@ export default async function LibraryPage({ params, searchParams }: PageProps) { totalPages={series.totalPages} totalElements={series.totalElements} pageSize={PAGE_SIZE} + defaultShowOnlyUnread={preferences.showOnlyUnread} + showOnlyUnread={unreadOnly} /> ); diff --git a/src/app/series/[seriesId]/page.tsx b/src/app/series/[seriesId]/page.tsx index e82f597..8ff71a4 100644 --- a/src/app/series/[seriesId]/page.tsx +++ b/src/app/series/[seriesId]/page.tsx @@ -1,6 +1,7 @@ import { PaginatedBookGrid } from "@/components/series/PaginatedBookGrid"; import { SeriesHeader } from "@/components/series/SeriesHeader"; import { SeriesService } from "@/lib/services/series.service"; +import { PreferencesService } from "@/lib/services/preferences.service"; interface PageProps { params: { seriesId: string }; @@ -9,18 +10,29 @@ interface PageProps { const PAGE_SIZE = 24; +async function getSeriesBooks(seriesId: string, page: number = 1, unreadOnly: boolean = false) { + try { + const pageIndex = page - 1; + + const books = await SeriesService.getSeriesBooks(seriesId, pageIndex, PAGE_SIZE, unreadOnly); + const series = await SeriesService.getSeries(seriesId); + + return { data: books, series }; + } catch (error) { + throw error instanceof Error ? error : new Error("Erreur lors de la récupération des tomes"); + } +} + export default async function SeriesPage({ params, searchParams }: PageProps) { const currentPage = searchParams.page ? parseInt(searchParams.page) : 1; - const unreadOnly = searchParams.unread === "true"; + const preferences = await PreferencesService.getPreferences(); + + // Utiliser le paramètre d'URL s'il existe, sinon utiliser la préférence utilisateur + const unreadOnly = + searchParams.unread !== undefined ? searchParams.unread === "true" : preferences.showOnlyUnread; try { - const pageIndex = currentPage - 1; - - // Appels API parallèles pour les détails de la série et les tomes - const [series, books] = await Promise.all([ - SeriesService.getSeries(params.seriesId), - SeriesService.getSeriesBooks(params.seriesId, pageIndex, PAGE_SIZE, unreadOnly), - ]); + const { data: books, series } = await getSeriesBooks(params.seriesId, currentPage, unreadOnly); return (
@@ -31,6 +43,8 @@ export default async function SeriesPage({ params, searchParams }: PageProps) { totalPages={books.totalPages} totalElements={books.totalElements} pageSize={PAGE_SIZE} + defaultShowOnlyUnread={preferences.showOnlyUnread} + showOnlyUnread={unreadOnly} />
); diff --git a/src/components/library/PaginatedSeriesGrid.tsx b/src/components/library/PaginatedSeriesGrid.tsx index 6b807cd..77e6c6f 100644 --- a/src/components/library/PaginatedSeriesGrid.tsx +++ b/src/components/library/PaginatedSeriesGrid.tsx @@ -14,6 +14,8 @@ interface PaginatedSeriesGridProps { totalPages: number; totalElements: number; pageSize: number; + defaultShowOnlyUnread: boolean; + showOnlyUnread: boolean; } export function PaginatedSeriesGrid({ @@ -22,26 +24,43 @@ export function PaginatedSeriesGrid({ totalPages, totalElements, pageSize, + defaultShowOnlyUnread, + showOnlyUnread: initialShowOnlyUnread, }: PaginatedSeriesGridProps) { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const [isChangingPage, setIsChangingPage] = useState(false); - const [showOnlyUnread, setShowOnlyUnread] = useState(searchParams.get("unread") === "true"); + const [showOnlyUnread, setShowOnlyUnread] = useState(initialShowOnlyUnread); // Réinitialiser l'état de chargement quand les séries changent useEffect(() => { setIsChangingPage(false); }, [series]); + // Mettre à jour l'état local quand la prop change + useEffect(() => { + setShowOnlyUnread(initialShowOnlyUnread); + }, [initialShowOnlyUnread]); + + // Appliquer le filtre par défaut au chargement initial + useEffect(() => { + if (defaultShowOnlyUnread && !searchParams.has("unread")) { + const params = new URLSearchParams(searchParams.toString()); + params.set("page", "1"); + params.set("unread", "true"); + router.push(`${pathname}?${params.toString()}`); + } + }, [defaultShowOnlyUnread, pathname, router, searchParams]); + const handlePageChange = async (page: number) => { setIsChangingPage(true); // Créer un nouvel objet URLSearchParams pour manipuler les paramètres const params = new URLSearchParams(searchParams.toString()); params.set("page", page.toString()); - if (showOnlyUnread) { - params.set("unread", "true"); - } + + // Conserver l'état du filtre unread + params.set("unread", showOnlyUnread.toString()); // Rediriger vers la nouvelle URL avec les paramètres mis à jour await router.push(`${pathname}?${params.toString()}`); @@ -52,13 +71,12 @@ export function PaginatedSeriesGrid({ const params = new URLSearchParams(searchParams.toString()); params.set("page", "1"); // Retourner à la première page lors du changement de filtre - if (!showOnlyUnread) { - params.set("unread", "true"); - } else { - params.delete("unread"); - } + const newUnreadState = !showOnlyUnread; + setShowOnlyUnread(newUnreadState); + + // Toujours définir explicitement le paramètre unread + params.set("unread", newUnreadState.toString()); - setShowOnlyUnread(!showOnlyUnread); await router.push(`${pathname}?${params.toString()}`); }; diff --git a/src/components/series/PaginatedBookGrid.tsx b/src/components/series/PaginatedBookGrid.tsx index 048dd72..778c2f6 100644 --- a/src/components/series/PaginatedBookGrid.tsx +++ b/src/components/series/PaginatedBookGrid.tsx @@ -14,6 +14,8 @@ interface PaginatedBookGridProps { totalPages: number; totalElements: number; pageSize: number; + defaultShowOnlyUnread: boolean; + showOnlyUnread: boolean; } export function PaginatedBookGrid({ @@ -22,26 +24,43 @@ export function PaginatedBookGrid({ totalPages, totalElements, pageSize, + defaultShowOnlyUnread, + showOnlyUnread: initialShowOnlyUnread, }: PaginatedBookGridProps) { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const [isChangingPage, setIsChangingPage] = useState(false); - const [showOnlyUnread, setShowOnlyUnread] = useState(searchParams.get("unread") === "true"); + const [showOnlyUnread, setShowOnlyUnread] = useState(initialShowOnlyUnread); // Réinitialiser l'état de chargement quand les tomes changent useEffect(() => { setIsChangingPage(false); }, [books]); + // Mettre à jour l'état local quand la prop change + useEffect(() => { + setShowOnlyUnread(initialShowOnlyUnread); + }, [initialShowOnlyUnread]); + + // Appliquer le filtre par défaut au chargement initial + useEffect(() => { + if (defaultShowOnlyUnread && !searchParams.has("unread")) { + const params = new URLSearchParams(searchParams.toString()); + params.set("page", "1"); + params.set("unread", "true"); + router.push(`${pathname}?${params.toString()}`); + } + }, [defaultShowOnlyUnread, pathname, router, searchParams]); + const handlePageChange = async (page: number) => { setIsChangingPage(true); // Créer un nouvel objet URLSearchParams pour manipuler les paramètres const params = new URLSearchParams(searchParams.toString()); params.set("page", page.toString()); - if (showOnlyUnread) { - params.set("unread", "true"); - } + + // Conserver l'état du filtre unread + params.set("unread", showOnlyUnread.toString()); // Rediriger vers la nouvelle URL avec les paramètres mis à jour await router.push(`${pathname}?${params.toString()}`); @@ -52,13 +71,12 @@ export function PaginatedBookGrid({ const params = new URLSearchParams(searchParams.toString()); params.set("page", "1"); // Retourner à la première page lors du changement de filtre - if (!showOnlyUnread) { - params.set("unread", "true"); - } else { - params.delete("unread"); - } + const newUnreadState = !showOnlyUnread; + setShowOnlyUnread(newUnreadState); + + // Toujours définir explicitement le paramètre unread + params.set("unread", newUnreadState.toString()); - setShowOnlyUnread(!showOnlyUnread); await router.push(`${pathname}?${params.toString()}`); }; diff --git a/src/components/settings/ClientSettings.tsx b/src/components/settings/ClientSettings.tsx index 5da6692..dfe2aa1 100644 --- a/src/components/settings/ClientSettings.tsx +++ b/src/components/settings/ClientSettings.tsx @@ -306,6 +306,39 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin onCheckedChange={handleToggleThumbnails} /> +
+
+ +

+ Afficher uniquement les séries non lues par défaut +

+
+ { + try { + await updatePreferences({ showOnlyUnread: checked }); + toast({ + title: "Préférences sauvegardées", + description: `Le filtre "À lire" par défaut est maintenant ${ + checked ? "activé" : "désactivé" + }`, + }); + } catch (error) { + console.error("Erreur détaillée:", error); + toast({ + variant: "destructive", + title: "Erreur", + description: + error instanceof Error + ? error.message + : "Une erreur est survenue lors de la mise à jour des préférences", + }); + } + }} + /> +
diff --git a/src/contexts/PreferencesContext.tsx b/src/contexts/PreferencesContext.tsx index 747756b..ed7a401 100644 --- a/src/contexts/PreferencesContext.tsx +++ b/src/contexts/PreferencesContext.tsx @@ -5,11 +5,13 @@ import React, { createContext, useContext, useEffect, useState } from "react"; export interface UserPreferences { showThumbnails: boolean; cacheMode: "memory" | "file"; + showOnlyUnread: boolean; } const defaultPreferences: UserPreferences = { showThumbnails: true, cacheMode: "memory", + showOnlyUnread: false, }; interface PreferencesContextType { @@ -30,7 +32,10 @@ export function PreferencesProvider({ children }: { children: React.ReactNode }) const response = await fetch("/api/preferences"); if (!response.ok) throw new Error("Erreur lors de la récupération des préférences"); const data = await response.json(); - setPreferences(data); + setPreferences({ + ...defaultPreferences, + ...data, + }); } catch (error) { console.error("Erreur lors de la récupération des préférences:", error); // En cas d'erreur, on garde les préférences par défaut @@ -56,7 +61,15 @@ export function PreferencesProvider({ children }: { children: React.ReactNode }) if (!response.ok) throw new Error("Erreur lors de la mise à jour des préférences"); const updatedPreferences = await response.json(); - setPreferences(updatedPreferences); + + setPreferences((prev) => { + const newState = { + ...prev, + ...updatedPreferences, + }; + console.log("Nouvel état des préférences:", newState); + return newState; + }); } catch (error) { console.error("Erreur lors de la mise à jour des préférences:", error); throw error; diff --git a/src/lib/models/preferences.model.ts b/src/lib/models/preferences.model.ts index bba84ab..d367ad1 100644 --- a/src/lib/models/preferences.model.ts +++ b/src/lib/models/preferences.model.ts @@ -16,11 +16,47 @@ const preferencesSchema = new mongoose.Schema( enum: ["memory", "file"], default: "memory", }, + showOnlyUnread: { + type: Boolean, + default: false, + required: false, + }, }, { timestamps: true, + strict: true, + toObject: { + transform: function (doc, ret) { + // Assurez-vous que showOnlyUnread est toujours un booléen + ret.showOnlyUnread = ret.showOnlyUnread === true; + return ret; + }, + }, } ); +// Middleware pour s'assurer que showOnlyUnread est toujours un booléen +preferencesSchema.pre("save", function (next) { + if (this.showOnlyUnread === undefined) { + this.showOnlyUnread = false; + } + this.showOnlyUnread = this.showOnlyUnread === true; + next(); +}); + +preferencesSchema.pre("findOneAndUpdate", function (next) { + const update = this.getUpdate() as mongoose.UpdateQuery; + if ( + update && + "$set" in update && + update.$set && + typeof update.$set === "object" && + "showOnlyUnread" in update.$set + ) { + update.$set.showOnlyUnread = update.$set.showOnlyUnread === true; + } + next(); +}); + export const PreferencesModel = mongoose.models.Preferences || mongoose.model("Preferences", preferencesSchema); diff --git a/src/lib/services/preferences.service.ts b/src/lib/services/preferences.service.ts index 574780d..46af9f9 100644 --- a/src/lib/services/preferences.service.ts +++ b/src/lib/services/preferences.service.ts @@ -9,8 +9,15 @@ interface User { export interface UserPreferences { showThumbnails: boolean; cacheMode: "memory" | "file"; + showOnlyUnread: boolean; } +const defaultPreferences: UserPreferences = { + showThumbnails: true, + cacheMode: "memory", + showOnlyUnread: false, +}; + export class PreferencesService { static async getCurrentUser(): Promise { const userCookie = cookies().get("stripUser"); @@ -32,26 +39,21 @@ export class PreferencesService { const user = await this.getCurrentUser(); const preferences = await PreferencesModel.findOne({ userId: user.id }); if (!preferences) { - return { - showThumbnails: true, - cacheMode: "memory", - }; + return defaultPreferences; } return { - showThumbnails: preferences.showThumbnails, - cacheMode: preferences.cacheMode || "memory", + ...defaultPreferences, + ...preferences.toObject(), }; } catch (error) { console.error("Error getting preferences:", error); - return { - showThumbnails: true, - cacheMode: "memory", - }; + return defaultPreferences; } } static async updatePreferences(preferences: Partial): Promise { try { + console.log("Service - Préférences reçues pour mise à jour:", preferences); const user = await this.getCurrentUser(); const updatedPreferences = await PreferencesModel.findOneAndUpdate( { userId: user.id }, @@ -59,10 +61,13 @@ export class PreferencesService { { new: true, upsert: true } ); - return { - showThumbnails: updatedPreferences.showThumbnails, - cacheMode: updatedPreferences.cacheMode || "memory", + console.log("Service - Document MongoDB après mise à jour:", updatedPreferences); + const result = { + ...defaultPreferences, + ...updatedPreferences.toObject(), }; + console.log("Service - Résultat final:", result); + return result; } catch (error) { console.error("Error updating preferences:", error); throw error;