diff --git a/.eslintrc.json b/.eslintrc.json index 8f5e4c0..9ccdc42 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,11 +1,38 @@ { "extends": ["next/core-web-vitals", "prettier"], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint"], + "plugins": ["@typescript-eslint", "unused-imports"], "rules": { - "@typescript-eslint/no-unused-vars": "off", - "no-console": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + } + ], + "no-console": [ + "warn", + { + "allow": ["warn", "error"] + } + ], "@next/next/no-html-link-for-pages": "off", - "react/no-unescaped-entities": "off" + "react/no-unescaped-entities": "off", + "no-unreachable": "error", + "no-unused-expressions": "warn", + "no-unused-private-class-members": "warn", + "unused-imports/no-unused-imports": "warn", + "unused-imports/no-unused-vars": [ + "warn", + { + "vars": "all", + "varsIgnorePattern": "^_", + "args": "after-used", + "argsIgnorePattern": "^_" + } + ], + "no-empty-function": "warn", + "no-empty": ["warn", { "allowEmptyCatch": true }] } } diff --git a/package.json b/package.json index 9e7694c..ce36e04 100644 --- a/package.json +++ b/package.json @@ -35,12 +35,13 @@ "@types/node": "20.11.16", "@types/react": "18.2.52", "@types/react-dom": "18.2.18", - "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/eslint-plugin": "^8.24.0", "@typescript-eslint/parser": "6.21.0", "autoprefixer": "10.4.17", "eslint": "8.56.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "10.0.1", + "eslint-plugin-unused-imports": "^4.1.4", "postcss": "8.4.33", "tailwindcss": "3.4.1", "typescript": "5.3.3" diff --git a/src/app/api/komga/config/route.ts b/src/app/api/komga/config/route.ts index c1e3a2c..003e47b 100644 --- a/src/app/api/komga/config/route.ts +++ b/src/app/api/komga/config/route.ts @@ -1,6 +1,8 @@ import { NextResponse } from "next/server"; import { ConfigDBService } from "@/lib/services/config-db.service"; +export const dynamic = "force-dynamic"; + export async function POST(request: Request) { try { const data = await request.json(); diff --git a/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts b/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts index 380cb50..851d3e2 100644 --- a/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts +++ b/src/app/api/komga/images/books/[bookId]/pages/[pageNumber]/thumbnail/route.ts @@ -10,7 +10,7 @@ export async function GET( try { // Convertir le numéro de page en nombre const pageNumber = parseInt(params.pageNumber); - if (isNaN(pageNumber) || pageNumber < 1) { + if (isNaN(pageNumber) || pageNumber < 0) { return NextResponse.json({ error: "Numéro de page invalide" }, { status: 400 }); } @@ -18,8 +18,11 @@ export async function GET( return response; } catch (error) { console.error("API Book Page Thumbnail - Erreur:", error); + if (error instanceof Error) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } return NextResponse.json( - { error: "Erreur lors de la récupération de la miniature" }, + { error: "Une erreur est survenue lors de la récupération de la miniature" }, { status: 500 } ); } diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index ca930ca..47625f0 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -1,5 +1,15 @@ import { ConfigDBService } from "@/lib/services/config-db.service"; import { ClientSettings } from "@/components/settings/ClientSettings"; +import { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Préférences", + description: "Configurez vos préférences StripStream", +}; + +export const viewport = { + colorScheme: "dark light", +}; export default async function SettingsPage() { let config = null; diff --git a/src/components/home/HomeContent.tsx b/src/components/home/HomeContent.tsx index 361aa2c..a6ea7e6 100644 --- a/src/components/home/HomeContent.tsx +++ b/src/components/home/HomeContent.tsx @@ -23,10 +23,6 @@ export function HomeContent({ data }: HomeContentProps) { await router.push(path); }; - const handleSeriesClick = (seriesId: string) => { - router.push(`/series/${seriesId}`); - }; - // Vérification des données pour le debug // console.log("HomeContent - Données reçues:", { // ongoingCount: data.ongoing?.length || 0, diff --git a/src/components/home/MediaRow.tsx b/src/components/home/MediaRow.tsx index adfe2e1..6018ef8 100644 --- a/src/components/home/MediaRow.tsx +++ b/src/components/home/MediaRow.tsx @@ -86,18 +86,9 @@ function MediaCard({ item, onClick }: MediaCardProps) { ? item.metadata.title : item.metadata.title || `Tome ${item.metadata.number}`; - const handleClick = () => { - console.log("MediaCard - handleClick:", { - itemType: isSeries ? "series" : "book", - itemId: item.id, - itemTitle: title, - }); - onClick?.(); - }; - return (
{/* Image de couverture */} diff --git a/src/components/layout/ClientLayout.tsx b/src/components/layout/ClientLayout.tsx index db3670c..3b7bd75 100644 --- a/src/components/layout/ClientLayout.tsx +++ b/src/components/layout/ClientLayout.tsx @@ -6,8 +6,7 @@ import { Header } from "@/components/layout/Header"; import { Sidebar } from "@/components/layout/Sidebar"; import { InstallPWA } from "../ui/InstallPWA"; import { Toaster } from "@/components/ui/toaster"; -import { usePathname, useRouter } from "next/navigation"; -import { authService } from "@/lib/services/auth.service"; +import { usePathname } from "next/navigation"; import { PreferencesProvider } from "@/contexts/PreferencesContext"; // Routes qui ne nécessitent pas d'authentification @@ -15,7 +14,6 @@ const publicRoutes = ["/login", "/register"]; export default function ClientLayout({ children }: { children: React.ReactNode }) { const [isSidebarOpen, setIsSidebarOpen] = useState(false); - const router = useRouter(); const pathname = usePathname(); const handleCloseSidebar = () => { @@ -56,8 +54,8 @@ export default function ClientLayout({ children }: { children: React.ReactNode } if ("serviceWorker" in navigator) { navigator.serviceWorker .register("/sw.js") - .then((registration) => { - console.log("Service Worker enregistré avec succès:", registration); + .then(() => { + // Succès silencieux }) .catch((error) => { console.error("Erreur lors de l'enregistrement du Service Worker:", error); diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 2076cb2..7e741c3 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -1,7 +1,6 @@ "use client"; -import { BookOpen, Home, Library, Settings, LogOut, RefreshCw, Star } from "lucide-react"; -import Link from "next/link"; +import { Home, Library, Settings, LogOut, RefreshCw, Star } from "lucide-react"; import { usePathname, useRouter } from "next/navigation"; import { cn } from "@/lib/utils"; import { authService } from "@/lib/services/auth.service"; @@ -32,7 +31,9 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { const data = await response.json(); setLibraries(data); } catch (error) { - console.error("Erreur:", error); + if (error instanceof Error) { + console.error("Erreur de chargement des bibliothèques:", error.message); + } setLibraries([]); } finally { setIsLoading(false); @@ -43,7 +44,6 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { const fetchFavorites = useCallback(async () => { setIsLoadingFavorites(true); try { - // Récupérer les IDs des favoris depuis l'API const favoritesResponse = await fetch("/api/komga/favorites"); if (!favoritesResponse.ok) { throw new Error("Erreur lors de la récupération des favoris"); @@ -55,7 +55,6 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { return; } - // Récupérer les détails des séries pour chaque ID const promises = favoriteIds.map(async (id: string) => { const response = await fetch(`/api/komga/series/${id}`); if (!response.ok) return null; @@ -65,7 +64,9 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) { const results = await Promise.all(promises); setFavorites(results.filter((series): series is KomgaSeries => series !== null)); } catch (error) { - console.error("Erreur lors de la récupération des favoris:", error); + if (error instanceof Error) { + console.error("Erreur de chargement des favoris:", error.message); + } setFavorites([]); } finally { setIsLoadingFavorites(false); diff --git a/src/components/library/PaginatedSeriesGrid.tsx b/src/components/library/PaginatedSeriesGrid.tsx index 455d41c..6b807cd 100644 --- a/src/components/library/PaginatedSeriesGrid.tsx +++ b/src/components/library/PaginatedSeriesGrid.tsx @@ -6,9 +6,10 @@ import { useRouter, usePathname, useSearchParams } from "next/navigation"; import { useState, useEffect } from "react"; import { Loader2, Filter } from "lucide-react"; import { cn } from "@/lib/utils"; +import { KomgaSeries } from "@/types/komga"; interface PaginatedSeriesGridProps { - series: any[]; + series: KomgaSeries[]; currentPage: number; totalPages: number; totalElements: number; @@ -61,10 +62,6 @@ export function PaginatedSeriesGrid({ await router.push(`${pathname}?${params.toString()}`); }; - const handleSeriesClick = (seriesId: string) => { - router.push(`/series/${seriesId}`); - }; - // Calcul des indices de début et de fin pour l'affichage const startIndex = (currentPage - 1) * pageSize + 1; const endIndex = Math.min(currentPage * pageSize, totalElements); diff --git a/src/components/reader/BookReader.tsx b/src/components/reader/BookReader.tsx index af45a5d..718ad8c 100644 --- a/src/components/reader/BookReader.tsx +++ b/src/components/reader/BookReader.tsx @@ -24,12 +24,9 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) { setIsLoading, secondPageLoading, setSecondPageLoading, - imageError, - setImageError, handlePreviousPage, handleNextPage, shouldShowDoublePage, - syncReadProgress, } = usePageNavigation({ book, pages, @@ -66,9 +63,12 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) { } } } catch (error) { - console.error("Erreur lors du chargement des URLs:", error); - setImageError(true); - } finally { + if (error instanceof Error) { + console.error( + `Erreur de chargement des URLs pour la page ${currentPage}:`, + error.message + ); + } // On s'assure que le chargement est terminé même en cas d'erreur if (isMounted) { setIsLoading(false); @@ -91,7 +91,6 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) { getPageUrl, setIsLoading, setSecondPageLoading, - setImageError, ]); // Effet pour précharger la page courante et les pages adjacentes @@ -205,6 +204,13 @@ export function BookReader({ book, pages, onClose }: BookReaderProps) { {/* Pages */}
+ {/* + Note: Nous utilisons intentionnellement des balises natives au lieu de next/image pour : + 1. Avoir un contrôle précis sur le chargement et le préchargement des pages + 2. Gérer efficacement le mode double page et les transitions + 3. Les images sont déjà optimisées côté serveur + 4. La performance est critique pour une lecture fluide + */} {/* Page courante */}
{ + e.stopPropagation(); + onToggleControls(); + }} >