From b8e979be55d411433c7ab73dace83c9440f9ae26 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Fri, 21 Nov 2025 12:53:42 +0530 Subject: [PATCH] fix(layout): fixed some rendering problems on Next 16 (#2495) --- .../src/app/library/components/Bookshelf.tsx | 93 ++++++++++--------- .../reader/components/sidebar/BookCard.tsx | 29 +++--- apps/readest-app/src/components/BookCover.tsx | 3 + 3 files changed, 68 insertions(+), 57 deletions(-) diff --git a/apps/readest-app/src/app/library/components/Bookshelf.tsx b/apps/readest-app/src/app/library/components/Bookshelf.tsx index 9737d904..958c7efa 100644 --- a/apps/readest-app/src/app/library/components/Bookshelf.tsx +++ b/apps/readest-app/src/app/library/components/Bookshelf.tsx @@ -129,54 +129,59 @@ const Bookshelf: React.FC = ({ const sort = searchParams?.get('sort') || settings.librarySortBy; const order = searchParams?.get('order') || (settings.librarySortAscending ? 'asc' : 'desc'); const cover = searchParams?.get('cover') || settings.libraryCoverFit; - const params = new URLSearchParams(searchParams?.toString()); - if (query) { - params.set('q', query); - setQueryTerm(query); - } else { - params.delete('q'); - setQueryTerm(null); - } - if (sort) { - params.set('sort', sort); - setSortBy(sort); - } else { - params.delete('sort'); - } - if (order) { - params.set('order', order); - setSortOrder(order); - } else { - params.delete('order'); - } - if (view) { - params.set('view', view); - setViewMode(view); - } else { - params.delete('view'); - } - setCoverFit(cover); - if (cover === 'crop') { - params.delete('cover'); - } - if (sort === 'updated' && order === 'desc' && view === 'grid') { - params.delete('sort'); - params.delete('order'); - params.delete('view'); - } + setGroupId(group); - if (group) { - if (currentBookshelfItems.length > 0) { - params.set('group', group); - } else { - params.delete('group'); - navigateToLibrary(router, `${params.toString()}`); + setQueryTerm(query || null); + setViewMode(view); + setSortBy(sort); + setSortOrder(order); + setCoverFit(cover); + }, [searchParams, settings]); + + useEffect(() => { + const params = new URLSearchParams(searchParams?.toString()); + let hasChanges = false; + + if (queryTerm) { + if (params.get('q') !== queryTerm) { + params.set('q', queryTerm); + hasChanges = true; } } else { - params.delete('group'); - navigateToLibrary(router, `${params.toString()}`); + if (params.has('q')) { + params.delete('q'); + hasChanges = true; + } } - }, [router, settings, searchParams, currentBookshelfItems, showGroupingModal]); + + if (sortBy !== 'updated' && params.get('sort') !== sortBy) { + params.set('sort', sortBy); + hasChanges = true; + } else if (sortBy === 'updated' && sortOrder === 'desc' && viewMode === 'grid') { + if (params.has('sort')) { + params.delete('sort'); + hasChanges = true; + } + } + + if (groupId) { + if (currentBookshelfItems.length === 0) { + params.delete('group'); + hasChanges = true; + } else if (params.get('group') !== groupId) { + params.set('group', groupId); + hasChanges = true; + } + } else if (params.has('group')) { + params.delete('group'); + hasChanges = true; + } + + if (hasChanges) { + navigateToLibrary(router, params.toString()); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [queryTerm, sortBy, sortOrder, viewMode, coverFit, groupId, currentBookshelfItems.length]); const toggleSelection = useCallback( (id: string) => { diff --git a/apps/readest-app/src/app/reader/components/sidebar/BookCard.tsx b/apps/readest-app/src/app/reader/components/sidebar/BookCard.tsx index b0aa3039..cb5cdb7d 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/BookCard.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/BookCard.tsx @@ -1,6 +1,5 @@ import clsx from 'clsx'; -import React from 'react'; -import Image from 'next/image'; +import React, { useRef } from 'react'; import { MdInfoOutline } from 'react-icons/md'; import { Book } from '@/types/book'; import { useThemeStore } from '@/store/themeStore'; @@ -8,12 +7,14 @@ import { useTranslation } from '@/hooks/useTranslation'; import { eventDispatcher } from '@/utils/event'; import { useResponsiveSize } from '@/hooks/useResponsiveSize'; import { formatAuthors, formatTitle } from '@/utils/book'; +import BookCover from '@/components/BookCover'; const BookCard = ({ book }: { book: Book }) => { - const { coverImageUrl, title, author, metadata } = book; + const { title, author } = book; const _ = useTranslation(); const { isDarkMode } = useThemeStore(); const iconSize18 = useResponsiveSize(18); + const bookCoverRef = useRef(null); const showBookDetails = () => { eventDispatcher.dispatchSync('show-book-details', book); @@ -21,19 +22,21 @@ const BookCard = ({ book }: { book: Book }) => { return (
- {_('Book { - (e.target as HTMLImageElement).style.display = 'none'; - }} - /> + > + (bookCoverRef.current!.style.display = 'none')} + /> +

{formatTitle(title)}

{formatAuthors(author)}

diff --git a/apps/readest-app/src/components/BookCover.tsx b/apps/readest-app/src/components/BookCover.tsx index 1339c4e5..e3628ce1 100644 --- a/apps/readest-app/src/components/BookCover.tsx +++ b/apps/readest-app/src/components/BookCover.tsx @@ -13,6 +13,7 @@ interface BookCoverProps { imageClassName?: string; showSpine?: boolean; isPreview?: boolean; + onImageError?: () => void; } const BookCover: React.FC = memo( @@ -24,6 +25,7 @@ const BookCover: React.FC = memo( className, imageClassName, isPreview, + onImageError, }) => { const coverRef = useRef(null); const [imageLoaded, setImageLoaded] = useState(false); @@ -54,6 +56,7 @@ const BookCover: React.FC = memo( setImageLoaded(false); setImageError(true); toggleImageVisibility(false); + onImageError?.(); }; useEffect(() => {