feat: implement view mode toggle functionality in PaginatedBookGrid and PaginatedSeriesGrid components
This commit is contained in:
36
src/components/common/ViewModeButton.tsx
Normal file
36
src/components/common/ViewModeButton.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { useDisplayPreferences } from "@/hooks/useDisplayPreferences";
|
||||||
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
|
import { LayoutGrid, List } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
interface ViewModeButtonProps {
|
||||||
|
onToggle?: (viewMode: "grid" | "list") => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ViewModeButton({ onToggle }: ViewModeButtonProps) {
|
||||||
|
const { viewMode, handleViewModeToggle } = useDisplayPreferences();
|
||||||
|
const { t } = useTranslate();
|
||||||
|
|
||||||
|
const handleClick = async () => {
|
||||||
|
const newViewMode = viewMode === "grid" ? "list" : "grid";
|
||||||
|
await handleViewModeToggle(newViewMode);
|
||||||
|
onToggle?.(newViewMode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Icon = viewMode === "grid" ? List : LayoutGrid;
|
||||||
|
const label = viewMode === "grid" ? t("books.display.list") : t("books.display.grid");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleClick}
|
||||||
|
title={label}
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<Icon className="h-4 w-4" />
|
||||||
|
<span className="hidden sm:inline ml-2">{label}</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -87,12 +87,6 @@ export function PaginatedSeriesGrid({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCompactToggle = async (newCompactState: boolean) => {
|
|
||||||
await updateUrlParams({
|
|
||||||
page: "1",
|
|
||||||
compact: newCompactState.toString(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePageSizeChange = async (size: number) => {
|
const handlePageSizeChange = async (size: number) => {
|
||||||
await updateUrlParams({
|
await updateUrlParams({
|
||||||
@@ -125,7 +119,7 @@ export function PaginatedSeriesGrid({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<PageSizeSelect onSizeChange={handlePageSizeChange} />
|
<PageSizeSelect onSizeChange={handlePageSizeChange} />
|
||||||
<CompactModeButton onToggle={handleCompactToggle} />
|
<CompactModeButton />
|
||||||
<UnreadFilterButton showOnlyUnread={showOnlyUnread} onToggle={handleUnreadFilter} />
|
<UnreadFilterButton showOnlyUnread={showOnlyUnread} onToggle={handleUnreadFilter} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
275
src/components/series/BookList.tsx
Normal file
275
src/components/series/BookList.tsx
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import type { KomgaBook } from "@/types/komga";
|
||||||
|
import { BookCover } from "@/components/ui/book-cover";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useTranslate } from "@/hooks/useTranslate";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { useBookOfflineStatus } from "@/hooks/useBookOfflineStatus";
|
||||||
|
import { formatDate } from "@/lib/utils";
|
||||||
|
import { ClientOfflineBookService } from "@/lib/services/client-offlinebook.service";
|
||||||
|
import { Progress } from "@/components/ui/progress";
|
||||||
|
import { Calendar, FileText, User, Tag } from "lucide-react";
|
||||||
|
import { MarkAsReadButton } from "@/components/ui/mark-as-read-button";
|
||||||
|
import { MarkAsUnreadButton } from "@/components/ui/mark-as-unread-button";
|
||||||
|
import { BookOfflineButton } from "@/components/ui/book-offline-button";
|
||||||
|
|
||||||
|
interface BookListProps {
|
||||||
|
books: KomgaBook[];
|
||||||
|
onBookClick: (book: KomgaBook) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BookListItemProps {
|
||||||
|
book: KomgaBook;
|
||||||
|
onBookClick: (book: KomgaBook) => void;
|
||||||
|
onSuccess: (book: KomgaBook, action: "read" | "unread") => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function BookListItem({ book, onBookClick, onSuccess }: BookListItemProps) {
|
||||||
|
const { t } = useTranslate();
|
||||||
|
const { isAccessible } = useBookOfflineStatus(book.id);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (!isAccessible) return;
|
||||||
|
onBookClick(book);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isRead = book.readProgress?.completed || false;
|
||||||
|
const hasReadProgress = book.readProgress !== null;
|
||||||
|
const currentPage = ClientOfflineBookService.getCurrentPage(book);
|
||||||
|
const totalPages = book.media.pagesCount;
|
||||||
|
const progressPercentage = totalPages > 0 ? (currentPage / totalPages) * 100 : 0;
|
||||||
|
|
||||||
|
const getStatusInfo = () => {
|
||||||
|
if (!book.readProgress) {
|
||||||
|
return {
|
||||||
|
label: t("books.status.unread"),
|
||||||
|
className: "bg-yellow-500/10 text-yellow-500",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (book.readProgress.completed) {
|
||||||
|
const readDate = book.readProgress.readDate ? formatDate(book.readProgress.readDate) : null;
|
||||||
|
return {
|
||||||
|
label: readDate ? t("books.status.readDate", { date: readDate }) : t("books.status.read"),
|
||||||
|
className: "bg-green-500/10 text-green-500",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPage > 0) {
|
||||||
|
return {
|
||||||
|
label: t("books.status.progress", {
|
||||||
|
current: currentPage,
|
||||||
|
total: totalPages,
|
||||||
|
}),
|
||||||
|
className: "bg-blue-500/10 text-blue-500",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: t("books.status.unread"),
|
||||||
|
className: "bg-yellow-500/10 text-yellow-500",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusInfo = getStatusInfo();
|
||||||
|
const title = book.metadata.title ||
|
||||||
|
(book.metadata.number
|
||||||
|
? t("navigation.volume", { number: book.metadata.number })
|
||||||
|
: book.name);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"group relative flex gap-4 p-4 rounded-lg border bg-card hover:bg-accent/50 transition-colors",
|
||||||
|
!isAccessible && "opacity-60"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* Couverture */}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"relative w-20 h-28 sm:w-24 sm:h-36 flex-shrink-0 rounded overflow-hidden bg-muted",
|
||||||
|
isAccessible && "cursor-pointer"
|
||||||
|
)}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<BookCover
|
||||||
|
book={book}
|
||||||
|
alt={t("books.coverAlt", { title })}
|
||||||
|
showControls={false}
|
||||||
|
showOverlay={false}
|
||||||
|
className="w-full h-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contenu */}
|
||||||
|
<div className="flex-1 min-w-0 flex flex-col gap-2">
|
||||||
|
{/* Titre et numéro */}
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3
|
||||||
|
className={cn(
|
||||||
|
"font-semibold text-base sm:text-lg line-clamp-2",
|
||||||
|
isAccessible && "cursor-pointer hover:text-primary transition-colors"
|
||||||
|
)}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
{book.metadata.number && (
|
||||||
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
|
{t("navigation.volume", { number: book.metadata.number })}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Badge de statut */}
|
||||||
|
<span className={cn("px-2 py-1 rounded-full text-xs font-medium flex-shrink-0", statusInfo.className)}>
|
||||||
|
{statusInfo.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Résumé */}
|
||||||
|
{book.metadata.summary && (
|
||||||
|
<p className="text-sm text-muted-foreground line-clamp-2 hidden sm:block">
|
||||||
|
{book.metadata.summary}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Métadonnées */}
|
||||||
|
<div className="flex flex-wrap items-center gap-4 text-xs text-muted-foreground">
|
||||||
|
{/* Pages */}
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<FileText className="h-3 w-3" />
|
||||||
|
<span>
|
||||||
|
{totalPages} {totalPages > 1 ? t("books.pages_plural") : t("books.pages")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Auteurs */}
|
||||||
|
{book.metadata.authors && book.metadata.authors.length > 0 && (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<User className="h-3 w-3" />
|
||||||
|
<span className="line-clamp-1">
|
||||||
|
{book.metadata.authors.map(a => a.name).join(", ")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Date de sortie */}
|
||||||
|
{book.metadata.releaseDate && (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Calendar className="h-3 w-3" />
|
||||||
|
<span>{formatDate(book.metadata.releaseDate)}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Tags */}
|
||||||
|
{book.metadata.tags && book.metadata.tags.length > 0 && (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Tag className="h-3 w-3" />
|
||||||
|
<span className="line-clamp-1">
|
||||||
|
{book.metadata.tags.slice(0, 3).join(", ")}
|
||||||
|
{book.metadata.tags.length > 3 && ` +${book.metadata.tags.length - 3}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Barre de progression */}
|
||||||
|
{hasReadProgress && !isRead && currentPage > 0 && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Progress value={progressPercentage} className="h-2" />
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{Math.round(progressPercentage)}% {t("books.completed")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div className="flex items-center gap-2 mt-auto pt-2">
|
||||||
|
{!isRead && (
|
||||||
|
<MarkAsReadButton
|
||||||
|
bookId={book.id}
|
||||||
|
pagesCount={book.media.pagesCount}
|
||||||
|
isRead={isRead}
|
||||||
|
onSuccess={() => onSuccess(book, "read")}
|
||||||
|
className="text-xs"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{hasReadProgress && (
|
||||||
|
<MarkAsUnreadButton
|
||||||
|
bookId={book.id}
|
||||||
|
onSuccess={() => onSuccess(book, "unread")}
|
||||||
|
className="text-xs"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<BookOfflineButton book={book} className="text-xs" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BookList({ books, onBookClick }: BookListProps) {
|
||||||
|
const [localBooks, setLocalBooks] = useState(books);
|
||||||
|
const { t } = useTranslate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalBooks(books);
|
||||||
|
}, [books]);
|
||||||
|
|
||||||
|
if (!localBooks.length) {
|
||||||
|
return (
|
||||||
|
<div className="text-center p-8">
|
||||||
|
<p className="text-muted-foreground whitespace-pre-line">{t("books.empty")}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnSuccess = (book: KomgaBook, action: "read" | "unread") => {
|
||||||
|
if (action === "read") {
|
||||||
|
setLocalBooks(
|
||||||
|
localBooks.map((previousBook) =>
|
||||||
|
previousBook.id === book.id
|
||||||
|
? {
|
||||||
|
...previousBook,
|
||||||
|
readProgress: {
|
||||||
|
completed: true,
|
||||||
|
page: previousBook.media.pagesCount,
|
||||||
|
readDate: new Date().toISOString(),
|
||||||
|
created: new Date().toISOString(),
|
||||||
|
lastModified: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: previousBook
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (action === "unread") {
|
||||||
|
setLocalBooks(
|
||||||
|
localBooks.map((previousBook) =>
|
||||||
|
previousBook.id === book.id
|
||||||
|
? {
|
||||||
|
...previousBook,
|
||||||
|
readProgress: null,
|
||||||
|
}
|
||||||
|
: previousBook
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{localBooks.map((book) => (
|
||||||
|
<BookListItem
|
||||||
|
key={book.id}
|
||||||
|
book={book}
|
||||||
|
onBookClick={onBookClick}
|
||||||
|
onSuccess={handleOnSuccess}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { BookGrid } from "./BookGrid";
|
import { BookGrid } from "./BookGrid";
|
||||||
|
import { BookList } from "./BookList";
|
||||||
import { Pagination } from "@/components/ui/Pagination";
|
import { Pagination } from "@/components/ui/Pagination";
|
||||||
import { useRouter, usePathname, useSearchParams } from "next/navigation";
|
import { useRouter, usePathname, useSearchParams } from "next/navigation";
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
@@ -9,6 +10,7 @@ import { useTranslate } from "@/hooks/useTranslate";
|
|||||||
import { useDisplayPreferences } from "@/hooks/useDisplayPreferences";
|
import { useDisplayPreferences } from "@/hooks/useDisplayPreferences";
|
||||||
import { PageSizeSelect } from "@/components/common/PageSizeSelect";
|
import { PageSizeSelect } from "@/components/common/PageSizeSelect";
|
||||||
import { CompactModeButton } from "@/components/common/CompactModeButton";
|
import { CompactModeButton } from "@/components/common/CompactModeButton";
|
||||||
|
import { ViewModeButton } from "@/components/common/ViewModeButton";
|
||||||
import { UnreadFilterButton } from "@/components/common/UnreadFilterButton";
|
import { UnreadFilterButton } from "@/components/common/UnreadFilterButton";
|
||||||
|
|
||||||
interface PaginatedBookGridProps {
|
interface PaginatedBookGridProps {
|
||||||
@@ -32,7 +34,7 @@ export function PaginatedBookGrid({
|
|||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [showOnlyUnread, setShowOnlyUnread] = useState(initialShowOnlyUnread);
|
const [showOnlyUnread, setShowOnlyUnread] = useState(initialShowOnlyUnread);
|
||||||
const { isCompact, itemsPerPage } = useDisplayPreferences();
|
const { isCompact, itemsPerPage, viewMode } = useDisplayPreferences();
|
||||||
const { t } = useTranslate();
|
const { t } = useTranslate();
|
||||||
|
|
||||||
const updateUrlParams = useCallback(async (
|
const updateUrlParams = useCallback(async (
|
||||||
@@ -81,13 +83,6 @@ export function PaginatedBookGrid({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCompactToggle = async (newCompactState: boolean) => {
|
|
||||||
await updateUrlParams({
|
|
||||||
page: "1",
|
|
||||||
compact: newCompactState.toString(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePageSizeChange = async (size: number) => {
|
const handlePageSizeChange = async (size: number) => {
|
||||||
await updateUrlParams({
|
await updateUrlParams({
|
||||||
page: "1",
|
page: "1",
|
||||||
@@ -119,12 +114,17 @@ export function PaginatedBookGrid({
|
|||||||
<p className="text-sm text-muted-foreground text-right">{getShowingText()}</p>
|
<p className="text-sm text-muted-foreground text-right">{getShowingText()}</p>
|
||||||
<div className="flex items-center justify-end gap-2">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<PageSizeSelect onSizeChange={handlePageSizeChange} />
|
<PageSizeSelect onSizeChange={handlePageSizeChange} />
|
||||||
<CompactModeButton onToggle={handleCompactToggle} />
|
<ViewModeButton />
|
||||||
|
{viewMode === "grid" && <CompactModeButton />}
|
||||||
<UnreadFilterButton showOnlyUnread={showOnlyUnread} onToggle={handleUnreadFilter} />
|
<UnreadFilterButton showOnlyUnread={showOnlyUnread} onToggle={handleUnreadFilter} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{viewMode === "grid" ? (
|
||||||
<BookGrid books={books} onBookClick={handleBookClick} isCompact={isCompact} />
|
<BookGrid books={books} onBookClick={handleBookClick} isCompact={isCompact} />
|
||||||
|
) : (
|
||||||
|
<BookList books={books} onBookClick={handleBookClick} />
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-between">
|
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-between">
|
||||||
<p className="text-sm text-muted-foreground order-2 sm:order-1">
|
<p className="text-sm text-muted-foreground order-2 sm:order-1">
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ export function PreferencesProvider({
|
|||||||
setPreferences({
|
setPreferences({
|
||||||
...defaultPreferences,
|
...defaultPreferences,
|
||||||
...data,
|
...data,
|
||||||
|
displayMode: {
|
||||||
|
...defaultPreferences.displayMode,
|
||||||
|
...(data.displayMode || {}),
|
||||||
|
viewMode: data.displayMode?.viewMode || defaultPreferences.displayMode.viewMode,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error({ err: error }, "Erreur lors de la récupération des préférences");
|
logger.error({ err: error }, "Erreur lors de la récupération des préférences");
|
||||||
|
|||||||
@@ -30,10 +30,25 @@ export function useDisplayPreferences() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleViewModeToggle = async (viewMode: "grid" | "list") => {
|
||||||
|
try {
|
||||||
|
await updatePreferences({
|
||||||
|
displayMode: {
|
||||||
|
...preferences.displayMode,
|
||||||
|
viewMode,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error({ err: error }, "Erreur lors de la mise à jour du mode d'affichage");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isCompact: preferences.displayMode.compact,
|
isCompact: preferences.displayMode.compact,
|
||||||
itemsPerPage: preferences.displayMode.itemsPerPage,
|
itemsPerPage: preferences.displayMode.itemsPerPage,
|
||||||
|
viewMode: preferences.displayMode.viewMode || "grid",
|
||||||
handleCompactToggle,
|
handleCompactToggle,
|
||||||
handlePageSizeChange,
|
handlePageSizeChange,
|
||||||
|
handleViewModeToggle,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -325,9 +325,14 @@
|
|||||||
"progress": "Page {{current}}/{{total}}",
|
"progress": "Page {{current}}/{{total}}",
|
||||||
"offline": "Unavailable offline"
|
"offline": "Unavailable offline"
|
||||||
},
|
},
|
||||||
|
"pages": "page",
|
||||||
|
"pages_plural": "pages",
|
||||||
|
"completed": "completed",
|
||||||
"display": {
|
"display": {
|
||||||
"showing": "Showing books {start} to {end} of {total}",
|
"showing": "Showing books {start} to {end} of {total}",
|
||||||
"page": "Page {current} of {total}"
|
"page": "Page {current} of {total}",
|
||||||
|
"grid": "Grid view",
|
||||||
|
"list": "List view"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"showAll": "Show all",
|
"showAll": "Show all",
|
||||||
|
|||||||
@@ -323,9 +323,14 @@
|
|||||||
"progress": "Page {{current}}/{{total}}",
|
"progress": "Page {{current}}/{{total}}",
|
||||||
"offline": "Indisponible hors ligne"
|
"offline": "Indisponible hors ligne"
|
||||||
},
|
},
|
||||||
|
"pages": "page",
|
||||||
|
"pages_plural": "pages",
|
||||||
|
"completed": "complété",
|
||||||
"display": {
|
"display": {
|
||||||
"showing": "Affichage des tomes {start} à {end} sur {total}",
|
"showing": "Affichage des tomes {start} à {end} sur {total}",
|
||||||
"page": "Page {current} sur {total}"
|
"page": "Page {current} sur {total}",
|
||||||
|
"grid": "Vue grille",
|
||||||
|
"list": "Vue liste"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"showAll": "Afficher tout",
|
"showAll": "Afficher tout",
|
||||||
|
|||||||
@@ -29,11 +29,17 @@ export class PreferencesService {
|
|||||||
return { ...defaultPreferences };
|
return { ...defaultPreferences };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const displayMode = preferences.displayMode as UserPreferences["displayMode"];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
showThumbnails: preferences.showThumbnails,
|
showThumbnails: preferences.showThumbnails,
|
||||||
cacheMode: preferences.cacheMode as "memory" | "file",
|
cacheMode: preferences.cacheMode as "memory" | "file",
|
||||||
showOnlyUnread: preferences.showOnlyUnread,
|
showOnlyUnread: preferences.showOnlyUnread,
|
||||||
displayMode: preferences.displayMode as UserPreferences["displayMode"],
|
displayMode: {
|
||||||
|
...defaultPreferences.displayMode,
|
||||||
|
...displayMode,
|
||||||
|
viewMode: displayMode?.viewMode || defaultPreferences.displayMode.viewMode,
|
||||||
|
},
|
||||||
background: preferences.background as unknown as BackgroundPreferences,
|
background: preferences.background as unknown as BackgroundPreferences,
|
||||||
komgaMaxConcurrentRequests: preferences.komgaMaxConcurrentRequests,
|
komgaMaxConcurrentRequests: preferences.komgaMaxConcurrentRequests,
|
||||||
readerPrefetchCount: preferences.readerPrefetchCount,
|
readerPrefetchCount: preferences.readerPrefetchCount,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export interface UserPreferences {
|
|||||||
displayMode: {
|
displayMode: {
|
||||||
compact: boolean;
|
compact: boolean;
|
||||||
itemsPerPage: number;
|
itemsPerPage: number;
|
||||||
|
viewMode: "grid" | "list";
|
||||||
};
|
};
|
||||||
background: BackgroundPreferences;
|
background: BackgroundPreferences;
|
||||||
komgaMaxConcurrentRequests: number;
|
komgaMaxConcurrentRequests: number;
|
||||||
@@ -36,6 +37,7 @@ export const defaultPreferences: UserPreferences = {
|
|||||||
displayMode: {
|
displayMode: {
|
||||||
compact: false,
|
compact: false,
|
||||||
itemsPerPage: 20,
|
itemsPerPage: 20,
|
||||||
|
viewMode: "grid",
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
type: "default",
|
type: "default",
|
||||||
|
|||||||
Reference in New Issue
Block a user