"use client"; import { useEffect, useState } from "react"; import { PaginatedSeriesGrid } from "@/components/library/PaginatedSeriesGrid"; import { RefreshButton } from "@/components/library/RefreshButton"; import { LibraryHeader } from "@/components/library/LibraryHeader"; import { ErrorMessage } from "@/components/ui/ErrorMessage"; import { PullToRefreshIndicator } from "@/components/common/PullToRefreshIndicator"; import { usePullToRefresh } from "@/hooks/usePullToRefresh"; import { useTranslate } from "@/hooks/useTranslate"; import { OptimizedSkeleton } from "@/components/skeletons/OptimizedSkeletons"; import type { LibraryResponse } from "@/types/library"; import type { KomgaSeries, KomgaLibrary } from "@/types/komga"; import type { UserPreferences } from "@/types/preferences"; import { Container } from "@/components/ui/container"; import { Section } from "@/components/ui/section"; import logger from "@/lib/logger"; interface ClientLibraryPageProps { currentPage: number; libraryId: string; preferences: UserPreferences; unreadOnly: boolean; search?: string; pageSize?: number; } const DEFAULT_PAGE_SIZE = 20; export function ClientLibraryPage({ currentPage, libraryId, preferences, unreadOnly, search, pageSize, }: ClientLibraryPageProps) { const { t } = useTranslate(); const [library, setLibrary] = useState(null); const [series, setSeries] = useState | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const effectivePageSize = pageSize || preferences.displayMode?.itemsPerPage || DEFAULT_PAGE_SIZE; useEffect(() => { const abortController = new AbortController(); const fetchData = async () => { setLoading(true); setError(null); try { const params = new URLSearchParams({ page: String(currentPage - 1), size: String(effectivePageSize), unread: String(unreadOnly), }); if (search) { params.append("search", search); } const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`, { signal: abortController.signal, }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error?.code || "SERIES_FETCH_ERROR"); } const data = await response.json(); setLibrary(data.library); setSeries(data.series); } catch (err) { // Ignore abort errors (caused by StrictMode cleanup) if (err instanceof Error && err.name === "AbortError") { return; } logger.error({ err }, "Error fetching library series"); setError(err instanceof Error ? err.message : "SERIES_FETCH_ERROR"); } finally { if (!abortController.signal.aborted) { setLoading(false); } } }; fetchData(); return () => { abortController.abort(); }; }, [libraryId, currentPage, unreadOnly, search, effectivePageSize]); const handleRefresh = async (libraryId: string) => { try { const params = new URLSearchParams({ page: String(currentPage - 1), size: String(effectivePageSize), unread: String(unreadOnly), }); if (search) { params.append("search", search); } const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`, { cache: "reload", }); if (!response.ok) { throw new Error("Error refreshing library"); } const data = await response.json(); setLibrary(data.library); setSeries(data.series); return { success: true }; } catch (error) { logger.error({ err: error }, "Error during refresh:"); return { success: false, error: "Error refreshing library" }; } }; const handleRetry = async () => { setError(null); setLoading(true); try { const params = new URLSearchParams({ page: String(currentPage - 1), size: String(effectivePageSize), unread: String(unreadOnly), }); if (search) { params.append("search", search); } const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`, { cache: "reload", }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error?.code || "SERIES_FETCH_ERROR"); } const data = await response.json(); setLibrary(data.library); setSeries(data.series); } catch (err) { logger.error({ err }, "Error fetching library series"); setError(err instanceof Error ? err.message : "SERIES_FETCH_ERROR"); } finally { setLoading(false); } }; const pullToRefresh = usePullToRefresh({ onRefresh: async () => { await handleRefresh(libraryId); }, enabled: !loading && !error && !!library && !!series, }); if (loading) { return ( <> {/* Header skeleton */}
{/* Filters */}
{/* Grid */}
{Array.from({ length: effectivePageSize }).map((_, i) => ( ))}
{/* Pagination */}
); } if (error) { return (
} /> ); } if (!library || !series) { return ( ); } return ( <> ); }