diff --git a/src/app/api/komga/books/[bookId]/read-progress/route.ts b/src/app/api/komga/books/[bookId]/read-progress/route.ts index fac8df0..1ce426f 100644 --- a/src/app/api/komga/books/[bookId]/read-progress/route.ts +++ b/src/app/api/komga/books/[bookId]/read-progress/route.ts @@ -1,6 +1,7 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { BookService } from "@/lib/services/book.service"; +import { SeriesService } from "@/lib/services/series.service"; import { ERROR_CODES } from "@/constants/errorCodes"; import { getErrorMessage } from "@/utils/errors"; import { AppError } from "@/utils/errors"; @@ -28,6 +29,17 @@ export async function PATCH( } await BookService.updateReadProgress(bookId, page, completed); + + // Invalider le cache de la série après avoir mis à jour la progression + try { + const seriesId = await BookService.getBookSeriesId(bookId); + await SeriesService.invalidateSeriesBooksCache(seriesId); + await SeriesService.invalidateSeriesCache(seriesId); + } catch (cacheError) { + // Ne pas faire échouer la requête si l'invalidation du cache échoue + logger.error({ err: cacheError }, "Erreur lors de l'invalidation du cache de la série:"); + } + return NextResponse.json({ message: "📖 Progression mise à jour avec succès" }); } catch (error) { logger.error({ err: error }, "Erreur lors de la mise à jour de la progression:"); @@ -64,6 +76,17 @@ export async function DELETE( const bookId: string = (await params).bookId; await BookService.deleteReadProgress(bookId); + + // Invalider le cache de la série après avoir supprimé la progression + try { + const seriesId = await BookService.getBookSeriesId(bookId); + await SeriesService.invalidateSeriesBooksCache(seriesId); + await SeriesService.invalidateSeriesCache(seriesId); + } catch (cacheError) { + // Ne pas faire échouer la requête si l'invalidation du cache échoue + logger.error({ err: cacheError }, "Erreur lors de l'invalidation du cache de la série:"); + } + return NextResponse.json({ message: "🗑️ Progression supprimée avec succès" }); } catch (error) { logger.error({ err: error }, "Erreur lors de la suppression de la progression:"); diff --git a/src/app/series/[seriesId]/ClientSeriesPage.tsx b/src/app/series/[seriesId]/ClientSeriesPage.tsx index 1678db1..e528c21 100644 --- a/src/app/series/[seriesId]/ClientSeriesPage.tsx +++ b/src/app/series/[seriesId]/ClientSeriesPage.tsx @@ -199,6 +199,7 @@ export function ClientSeriesPage({ totalElements={books.totalElements} defaultShowOnlyUnread={preferences.showOnlyUnread} showOnlyUnread={unreadOnly} + onRefresh={() => handleRefresh(seriesId)} /> diff --git a/src/components/series/BookGrid.tsx b/src/components/series/BookGrid.tsx index b114a3f..be4e7c6 100644 --- a/src/components/series/BookGrid.tsx +++ b/src/components/series/BookGrid.tsx @@ -2,7 +2,7 @@ import type { KomgaBook } from "@/types/komga"; import { BookCover } from "@/components/ui/book-cover"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { useTranslate } from "@/hooks/useTranslate"; import { cn } from "@/lib/utils"; import { useBookOfflineStatus } from "@/hooks/useBookOfflineStatus"; @@ -11,6 +11,7 @@ interface BookGridProps { books: KomgaBook[]; onBookClick: (book: KomgaBook) => void; isCompact?: boolean; + onRefresh?: () => void; } interface BookCardProps { @@ -61,12 +62,18 @@ function BookCard({ book, onBookClick, onSuccess, isCompact }: BookCardProps) { ); } -export function BookGrid({ books, onBookClick, isCompact = false }: BookGridProps) { +export function BookGrid({ books, onBookClick, isCompact = false, onRefresh }: BookGridProps) { const [localBooks, setLocalBooks] = useState(books); const { t } = useTranslate(); + const previousBookIdsRef = useRef(books.map((b) => b.id).join(",")); useEffect(() => { - setLocalBooks(books); + // Ne réinitialiser que si les IDs des livres ont changé (nouvelle page, nouveau filtre, etc.) + const newIds = books.map((b) => b.id).join(","); + if (previousBookIdsRef.current !== newIds) { + setLocalBooks(books); + previousBookIdsRef.current = newIds; + } }, [books]); if (!localBooks.length) { @@ -107,6 +114,8 @@ export function BookGrid({ books, onBookClick, isCompact = false }: BookGridProp ) ); } + // Rafraîchir les données après avoir marqué comme lu/non lu + onRefresh?.(); }; return ( diff --git a/src/components/series/BookList.tsx b/src/components/series/BookList.tsx index 1e5e8b9..c3a1fbb 100644 --- a/src/components/series/BookList.tsx +++ b/src/components/series/BookList.tsx @@ -2,7 +2,7 @@ import type { KomgaBook } from "@/types/komga"; import { BookCover } from "@/components/ui/book-cover"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { useTranslate } from "@/hooks/useTranslate"; import { cn } from "@/lib/utils"; import { useBookOfflineStatus } from "@/hooks/useBookOfflineStatus"; @@ -18,6 +18,7 @@ interface BookListProps { books: KomgaBook[]; onBookClick: (book: KomgaBook) => void; isCompact?: boolean; + onRefresh?: () => void; } interface BookListItemProps { @@ -288,12 +289,18 @@ function BookListItem({ book, onBookClick, onSuccess, isCompact = false }: BookL ); } -export function BookList({ books, onBookClick, isCompact = false }: BookListProps) { +export function BookList({ books, onBookClick, isCompact = false, onRefresh }: BookListProps) { const [localBooks, setLocalBooks] = useState(books); const { t } = useTranslate(); + const previousBookIdsRef = useRef(books.map((b) => b.id).join(",")); useEffect(() => { - setLocalBooks(books); + // Ne réinitialiser que si les IDs des livres ont changé (nouvelle page, nouveau filtre, etc.) + const newIds = books.map((b) => b.id).join(","); + if (previousBookIdsRef.current !== newIds) { + setLocalBooks(books); + previousBookIdsRef.current = newIds; + } }, [books]); if (!localBooks.length) { @@ -334,6 +341,8 @@ export function BookList({ books, onBookClick, isCompact = false }: BookListProp ) ); } + // Rafraîchir les données après avoir marqué comme lu/non lu + onRefresh?.(); }; return ( diff --git a/src/components/series/PaginatedBookGrid.tsx b/src/components/series/PaginatedBookGrid.tsx index a59bb54..44c1de7 100644 --- a/src/components/series/PaginatedBookGrid.tsx +++ b/src/components/series/PaginatedBookGrid.tsx @@ -21,6 +21,7 @@ interface PaginatedBookGridProps { totalElements: number; defaultShowOnlyUnread: boolean; showOnlyUnread: boolean; + onRefresh?: () => void; } export function PaginatedBookGrid({ @@ -30,6 +31,7 @@ export function PaginatedBookGrid({ totalElements, defaultShowOnlyUnread, showOnlyUnread: initialShowOnlyUnread, + onRefresh, }: PaginatedBookGridProps) { const router = useRouter(); const pathname = usePathname(); @@ -130,9 +132,9 @@ export function PaginatedBookGrid({ {viewMode === "grid" ? ( - + ) : ( - + )}
diff --git a/src/lib/services/book.service.ts b/src/lib/services/book.service.ts index 3ba8bb5..d691c94 100644 --- a/src/lib/services/book.service.ts +++ b/src/lib/services/book.service.ts @@ -61,6 +61,16 @@ export class BookService extends BaseApiService { } } + static async getBookSeriesId(bookId: string): Promise { + try { + // Récupérer le livre sans cache pour éviter les données obsolètes + const book = await this.fetchFromApi({ path: `books/${bookId}` }); + return book.seriesId; + } catch (error) { + throw new AppError(ERROR_CODES.BOOK.NOT_FOUND, {}, error); + } + } + static async updateReadProgress( bookId: string, page: number,