fix(book-card): prefer audiobook covers for square mixed-format cards#467
Conversation
📝 WalkthroughWalkthroughThe changes add logic to prioritize audiobook thumbnail covers when displaying books in square aspect ratio contexts, such as the "Continue Listening" scroller. A new computed flag detects if a book has an audiobook format (either primary or alternative), and the cover selection now considers both audiobook availability and square cover display settings. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@frontend/src/app/features/book/components/book-browser/book-card/book-card.component.ts`:
- Around line 120-123: The computed coverImageUrl currently uses
urlHelper.getAudiobookThumbnailUrl(...) for audiobooks/square mixed formats but
that call can return null and cause a missing thumbnail; change the logic in the
coverImageUrl computed (the computed itself referencing isAudiobook(),
useSquareCovers(), hasAudiobookFormat()) to call
urlHelper.getAudiobookThumbnailUrl(this.book().id,
this.book().metadata?.audiobookCoverUpdatedOn) into a temporary value and if it
is null/undefined fall back to urlHelper.getThumbnailUrl(this.book().id,
this.book().metadata?.coverUpdatedOn) (e.g. using a nullish coalescing or
conditional) so a valid ebook thumbnail is used when audiobook cover metadata is
absent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c7290622-df90-40a3-bf8c-e5f89d66ac31
📒 Files selected for processing (2)
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.tsfrontend/src/app/features/book/components/book-browser/book-card/book-card.component.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Test Suite / Backend Tests
- GitHub Check: Test Suite / Frontend Tests
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (4)
frontend/src/**/*.{ts,tsx,html,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Use 2-space indentation in TypeScript, HTML, and SCSS files
Files:
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.tsfrontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.ts
frontend/src/app/**/*.component.ts
📄 CodeRabbit inference engine (AGENTS.md)
Keep Angular code on standalone components. Do not add NgModules
Files:
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.ts
frontend/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
frontend/src/app/**/*.{ts,tsx}: Preferinject()over constructor injection
Followfrontend/eslint.config.js: component selectors useapp-*, directive selectors useapp*, andanyis disallowed
Files:
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.tsfrontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.ts
frontend/src/**/*.{test,spec}.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use Vitest for tests in the frontend
Files:
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: pedronave
Repo: grimmory-tools/grimmory PR: 173
File: booklore-api/src/main/java/org/booklore/service/hardcover/HardcoverSyncService.java:188-199
Timestamp: 2026-03-24T18:46:47.249Z
Learning: In grimmory-tools/grimmory (HardcoverSyncService.java), when a `hardcoverBookId` is stored in book metadata but is malformed (non-numeric), the preferred behavior is to return null and skip the sync rather than falling back to ISBN-based lookup. The reasoning is that an explicitly set bookId represents deliberate user intent, and silently resolving via ISBN could sync progress to a different book than intended.
📚 Learning: 2026-04-04T14:03:28.295Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 372
File: frontend/src/app/features/book/service/app-books-api.service.ts:150-178
Timestamp: 2026-04-04T14:03:28.295Z
Learning: In `frontend/src/app/features/book/service/app-books-api.service.ts`, the `summaryToBook` function intentionally uses `as unknown as Book` to cast a partial object to the `Book` type. The returned object omits some required `Book` fields (e.g. `metadata.bookId`, `pdfProgress.page`) and includes an extra `bookFiles: []` property not in the `Book` interface. This is by design — consumers of `AppBooksApiService.books()` (e.g. `BookCardComponent`) only access the subset of properties that are populated from `AppBookSummary`, so no runtime issues occur. Do not flag this as a type safety error.
Applied to files:
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.tsfrontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.ts
📚 Learning: 2026-04-05T21:16:01.715Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 385
File: frontend/src/app/app.component.ts:55-56
Timestamp: 2026-04-05T21:16:01.715Z
Learning: When reviewing code in the Grimmory frontend (Angular), prefer modern Angular patterns. Specifically: (1) Prefer `DestroyRef` with `takeUntilDestroyed(destroyRef)` for teardown in Angular v16+ instead of manually tracking `Subscription` arrays and calling `unsubscribe()` in `ngOnDestroy()`. (2) Prefer `inject()` for dependency injection over constructor injection where appropriate. (3) Prefer Angular signals (e.g., `signal`, `computed`) over `BehaviorSubject`/`Observable` for state where signals/computed values fit the use case. Flag older patterns when they can be replaced with these modern equivalents without changing behavior.
Applied to files:
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.tsfrontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.ts
📚 Learning: 2026-04-07T09:28:09.587Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 393
File: frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts:255-263
Timestamp: 2026-04-07T09:28:09.587Z
Learning: In this Angular frontend (under frontend/src/app/), flag manual resource management/cleanup patterns when there is an Angular v16+ automatic alternative. Examples to prefer: (1) Instead of manually pairing document/window event listeners with stored cleanup functions (e.g., add/removeEventListener with mouseMoveCleanup/documentClickCleanup/keydownCleanup/touchCleanup fields), register teardown via DestroyRef.onDestroy(cleanupFn) (or equivalent Angular v16+ teardown mechanism). (2) Instead of storing Subscriptions in fields and explicitly unsubscribing in ngOnDestroy (e.g., annotationSaveSubscription/annotationCacheSubscription), use takeUntilDestroyed(destroyRef) (piped into the observable) or other Angular v16+ primitives. (3) If teardown is lifecycle-coupled and can be automated via DestroyRef/takeUntilDestroyed/signals (or other Angular v16+ mechanisms), prefer the automated approach over manual ngOnDestroy cleanup. Raise a review finding for the manual pattern and recommend the aut...
Applied to files:
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.tsfrontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.ts
🔇 Additional comments (2)
frontend/src/app/features/book/components/book-browser/book-card/book-card.component.ts (1)
114-118: Good mixed-format audiobook detection.This correctly extends audiobook detection beyond
primaryFileand matches the square-cover objective.frontend/src/app/features/book/components/book-browser/book-card/book-card.component.spec.ts (1)
302-331: Targeted test coverage looks solid.This test cleanly validates the square-cover audiobook-priority behavior for mixed-format books and the non-square fallback path.
Description
Linked Issue: Fixes #465
Changes
Summary by CodeRabbit
Bug Fixes
Tests