From c5a1a3afeb3df63a696af772d139491fefd6b918 Mon Sep 17 00:00:00 2001 From: Zeedif Date: Fri, 29 May 2026 00:58:04 -0500 Subject: [PATCH] feat(opds): add facet navigation and quick catalog registration in header (#4348) * feat(opds): add facet navigation and quick catalog registration to header - Add an options dropdown in the header to navigate OPDS feed facets on compact viewports. - Implement an "Add to My Catalogs" dialog to save the current feed, inheriting credentials, headers, and config. - Render a standalone shortcut button in the header when no facets are present, hiding the dropdown. * fix(opds): scope window rounding to full route + guard duplicate add Reviewing the facet-navigation feature surfaced three issues in the quick-add flow and an unrelated window-rounding change: - Restore the standard full-screen-route rounding pattern on the OPDS browser. The header change had dropped the `isRoundedWindow` guard (rounding maximized/fullscreen windows leaves gaps at the edges) and switched to left-only corners (a docked-sidebar pattern). Match the library/auth/user/reader pages: `isRoundedWindow && window-border rounded-window`. - Guard "Add to My Catalogs" against re-adding a catalog whose URL is already saved. `addCatalog` dedups by contentId and would silently overwrite the existing entry while toasting "added successfully"; now it detects the duplicate via `findByUrl` and shows an info toast. - Replace `feed!.facets!` non-null assertions with optional chaining. Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Huang Xin Co-authored-by: Claude Opus 4.8 (1M context) --- .../src/app/opds/components/FeedView.tsx | 17 ++- .../src/app/opds/components/Navigation.tsx | 104 +++++++++++++++++- apps/readest-app/src/app/opds/page.tsx | 96 +++++++++++++++- 3 files changed, 212 insertions(+), 5 deletions(-) diff --git a/apps/readest-app/src/app/opds/components/FeedView.tsx b/apps/readest-app/src/app/opds/components/FeedView.tsx index 3823f160..85009f9b 100644 --- a/apps/readest-app/src/app/opds/components/FeedView.tsx +++ b/apps/readest-app/src/app/opds/components/FeedView.tsx @@ -2,7 +2,7 @@ import { useMemo, useCallback } from 'react'; import { VirtuosoGrid } from 'react-virtuoso'; -import { IoChevronBack, IoChevronForward, IoFilter } from 'react-icons/io5'; +import { IoAdd, IoChevronBack, IoChevronForward, IoFilter } from 'react-icons/io5'; import { useTranslation } from '@/hooks/useTranslation'; import { OPDSFeed, OPDSGenericLink } from '@/types/opds'; import { PublicationCard } from './PublicationCard'; @@ -17,6 +17,7 @@ interface FeedViewProps { onPublicationSelect: (groupIndex: number, itemIndex: number) => void; onGenerateCachedImageUrl: (url: string) => Promise; isOPDSCatalog: (type?: string) => boolean; + onAddCatalog?: () => void; } const gridClassName = 'grid grid-cols-3 gap-4 px-4 sm:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6'; @@ -30,6 +31,7 @@ export function FeedView({ onNavigate, onPublicationSelect, onGenerateCachedImageUrl, + onAddCatalog, }: FeedViewProps) { const _ = useTranslation(); const linksByRel = useMemo(() => groupByArray(feed.links, (link) => link.rel), [feed.links]); @@ -80,9 +82,20 @@ export function FeedView({ {hasFacets && (