feat: pref for default filter showUnread
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<div className="container py-8 space-y-8">
|
||||
@@ -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}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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()}`);
|
||||
};
|
||||
|
||||
|
||||
@@ -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()}`);
|
||||
};
|
||||
|
||||
|
||||
@@ -306,6 +306,39 @@ export function ClientSettings({ initialConfig, initialTTLConfig }: ClientSettin
|
||||
onCheckedChange={handleToggleThumbnails}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="unread-filter">Filtre "À lire" par défaut</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Afficher uniquement les séries non lues par défaut
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="unread-filter"
|
||||
checked={preferences.showOnlyUnread}
|
||||
onCheckedChange={async (checked) => {
|
||||
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",
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<any>;
|
||||
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);
|
||||
|
||||
@@ -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<User> {
|
||||
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<UserPreferences>): Promise<UserPreferences> {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user