diff --git a/apps/readest-app/src/__tests__/components/BookCover.test.tsx b/apps/readest-app/src/__tests__/components/BookCover.test.tsx index 572c3a9b..108b727a 100644 --- a/apps/readest-app/src/__tests__/components/BookCover.test.tsx +++ b/apps/readest-app/src/__tests__/components/BookCover.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; -import { render, cleanup } from '@testing-library/react'; +import { render, cleanup, fireEvent } from '@testing-library/react'; import BookCover from '@/components/BookCover'; import { Book } from '@/types/book'; @@ -39,6 +39,21 @@ describe('BookCover', () => { expect(img?.getAttribute('loading')).toBe('lazy'); }); + it('reports natural aspect ratio via onAspectRatioChange when fit-mode image loads', () => { + const onAspectRatioChange = vi.fn(); + const { container } = render( + , + ); + const img = container.querySelector('img.cover-image') as HTMLImageElement; + expect(img).toBeTruthy(); + + Object.defineProperty(img, 'naturalWidth', { value: 600, configurable: true }); + Object.defineProperty(img, 'naturalHeight', { value: 900, configurable: true }); + fireEvent.load(img); + + expect(onAspectRatioChange).toHaveBeenCalledWith(600 / 900); + }); + it('falls back to metadata.author on the fallback cover when book.author is empty', () => { const book = makeBook({ author: '', diff --git a/apps/readest-app/src/app/library/components/BookItem.tsx b/apps/readest-app/src/app/library/components/BookItem.tsx index a3d6221d..6159b180 100644 --- a/apps/readest-app/src/app/library/components/BookItem.tsx +++ b/apps/readest-app/src/app/library/components/BookItem.tsx @@ -1,4 +1,5 @@ import clsx from 'clsx'; +import { useEffect, useState } from 'react'; import { MdCheckCircle, MdCheckCircleOutline } from 'react-icons/md'; import { LiaCloudUploadAltSolid, @@ -49,6 +50,21 @@ const BookItem: React.FC = ({ const { settings } = useSettingsStore(); const iconSize15 = useResponsiveSize(15); + const [coverAspect, setCoverAspect] = useState(null); + useEffect(() => { + setCoverAspect(null); + }, [book.hash, book.metadata?.coverImageUrl, book.coverImageUrl]); + + const CELL_ASPECT_RATIO = 28 / 41; + const fitCoverInGrid = mode === 'grid' && coverFit === 'fit' && coverAspect !== null; + const shouldShrinkWidth = fitCoverInGrid && coverAspect! < CELL_ASPECT_RATIO; + const bookitemMainStyle = fitCoverInGrid + ? { + aspectRatio: coverAspect!, + ...(shouldShrinkWidth ? { width: `${(coverAspect! / CELL_ASPECT_RATIO) * 100}%` } : {}), + } + : undefined; + return (
= ({ >
= ({ coverFit={coverFit} showSpine={false} imageClassName='rounded shadow-md' + onAspectRatioChange={setCoverAspect} /> {bookSelected && (
diff --git a/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx b/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx index 11a0b247..56174c5e 100644 --- a/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx +++ b/apps/readest-app/src/app/reader/components/tts/TTSControl.tsx @@ -175,7 +175,7 @@ const TTSControl: React.FC = ({ bookKey, gridInsets }) => {