fix(metadata): hide sidecar tab when sidecar json is disabled#814
fix(metadata): hide sidecar tab when sidecar json is disabled#814
Conversation
📝 WalkthroughWalkthroughThe changes introduce a computed Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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.
🧹 Nitpick comments (2)
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.ts (1)
89-94: Consider acomputedsignal for reactivity.
canShowSidecarTabis a plain getter that reads a signal (appSettings()) plus plain fields (admin,canEditMetadata,isLocalStorage). It works today because the template re-evaluates on change detection, but expressing it as acomputed()would make it properly reactive to settings changes and align with the team's signal-first pattern—especially ifadmin/canEditMetadata/isLocalStorageare ever migrated to signals (noteisLocalStorageis currently only assigned once inngOnInit, so it won't react to later diskType changes).Based on learnings: prefer Angular signals (
signal/computed) over imperative patterns where they fit.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.ts` around lines 89 - 94, Replace the plain getter canShowSidecarTab with an Angular computed signal so its value updates reactively when appSettings() or any future signals (admin, canEditMetadata, isLocalStorage) change: create a computed that reads this.appSettingsService.appSettings() and the component fields (admin, canEditMetadata, isPhysical, isLocalStorage) and returns the same boolean expression currently in the getter; update template bindings to use the computed value and remove the getter; if isLocalStorage is set only in ngOnInit consider converting it to a signal or updating it through a signal source so the computed reacts to diskType changes.frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts (1)
37-50: Test coverage is narrow — consider covering permission/physical/localStorage gates.The suite only toggles
sidecarEnabled. SincecanShowSidecarTabalso depends on(admin || canEditMetadata),!isPhysical, andisLocalStorage, a regression in any of those conditions would not be caught. Also note thatngOnInitis never invoked here, soisLocalStoragestays at its field default (true) anddiskTypeinbuildSettingsis effectively unexercised — a future refactor that movesisLocalStoragederivation out ofngOnInitcould silently change behavior without failing this spec.Consider adding cases for: user without admin/canEditMetadata, a physical book, and non-LOCAL disk type.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts` around lines 37 - 50, The test only toggles sidecarEnabled and never exercises the other gates used by BookMetadataCenterComponent.canShowSidecarTab; update the spec to call ngOnInit and add cases that toggle user permissions (admin and canEditMetadata), physical/disk type variations (isPhysical/diskType in buildSettings) and isLocalStorage to cover all branches: create instances via TestBed.runInInjectionContext(() => new BookMetadataCenterComponent()), call component.ngOnInit(), then set appSettingsSignal with buildSettings variations (sidecarEnabled true/false, diskType set to physical/non-LOCAL) and adjust the user/permission signal or input to simulate admin=false and canEditMetadata=false to assert canShowSidecarTab false, and assert true only when sidecarEnabled + (admin||canEditMetadata) + !isPhysical + isLocalStorage are satisfied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts`:
- Around line 37-50: The test only toggles sidecarEnabled and never exercises
the other gates used by BookMetadataCenterComponent.canShowSidecarTab; update
the spec to call ngOnInit and add cases that toggle user permissions (admin and
canEditMetadata), physical/disk type variations (isPhysical/diskType in
buildSettings) and isLocalStorage to cover all branches: create instances via
TestBed.runInInjectionContext(() => new BookMetadataCenterComponent()), call
component.ngOnInit(), then set appSettingsSignal with buildSettings variations
(sidecarEnabled true/false, diskType set to physical/non-LOCAL) and adjust the
user/permission signal or input to simulate admin=false and
canEditMetadata=false to assert canShowSidecarTab false, and assert true only
when sidecarEnabled + (admin||canEditMetadata) + !isPhysical + isLocalStorage
are satisfied.
In
`@frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.ts`:
- Around line 89-94: Replace the plain getter canShowSidecarTab with an Angular
computed signal so its value updates reactively when appSettings() or any future
signals (admin, canEditMetadata, isLocalStorage) change: create a computed that
reads this.appSettingsService.appSettings() and the component fields (admin,
canEditMetadata, isPhysical, isLocalStorage) and returns the same boolean
expression currently in the getter; update template bindings to use the computed
value and remove the getter; if isLocalStorage is set only in ngOnInit consider
converting it to a signal or updating it through a signal source so the computed
reacts to diskType changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3364c57e-b149-421f-9d73-b42e71caaab9
📒 Files selected for processing (3)
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.htmlfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.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). (5)
- GitHub Check: Test Suite / Frontend Tests
- GitHub Check: Test Suite / Backend Tests
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (java-kotlin)
- GitHub Check: Frontend Lint Threshold Check
🧰 Additional context used
📓 Path-based instructions (5)
frontend/src/**/*.{ts,tsx,html,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Use 2-space indentation in TypeScript, HTML, and SCSS in frontend code
Files:
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.htmlfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts
frontend/src/**/*.component.ts
📄 CodeRabbit inference engine (AGENTS.md)
Keep Angular code on standalone components. Do not add NgModules in frontend code
Files:
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.ts
frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer inject() over constructor injection in frontend code
Files:
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts
frontend/src/**/*.{ts,tsx,html}
📄 CodeRabbit inference engine (AGENTS.md)
frontend/src/**/*.{ts,tsx,html}: Follow frontend/eslint.config.js: component selectors use app-, directive selectors use app, and any is disallowed in frontend code
Put user-facing strings in Transloco files under frontend/src/i18n/
Files:
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.htmlfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts
frontend/src/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Vitest for tests in frontend code
Files:
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts
🧠 Learnings (5)
📚 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/metadata/component/book-metadata-center/book-metadata-center.component.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.htmlfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.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/metadata/component/book-metadata-center/book-metadata-center.component.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.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/metadata/component/book-metadata-center/book-metadata-center.component.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts
📚 Learning: 2026-04-11T03:55:57.229Z
Learnt from: zachyale
Repo: grimmory-tools/grimmory PR: 439
File: frontend/src/app/features/series-browser/components/series-browser/series-browser.component.ts:178-196
Timestamp: 2026-04-11T03:55:57.229Z
Learning: In this Angular frontend (frontend/src/app/), prefer the team’s reactive i18n “signal/computed” pattern: (1) For individual reactive translated strings, use `translateSignal()` from `jsverse/transloco`. (2) For option/label arrays that must update on language switch, create a single `activeLang` signal with `toSignal(t.langChanges$, { initialValue: t.getActiveLang() })`, then derive the arrays as `computed()` signals that read `activeLang()`. This should avoid manual `langChanges$` subscriptions and any `ngOnDestroy` subscription cleanup; prefer this over subscribing in `ngOnInit` when implementing reactive localization.
Applied to files:
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.tsfrontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts
📚 Learning: 2026-04-22T01:56:39.495Z
Learnt from: CR
Repo: grimmory-tools/grimmory PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-22T01:56:39.495Z
Learning: Applies to frontend/src/**/*.{test,spec}.{ts,tsx} : Use Vitest for tests in frontend code
Applied to files:
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.spec.ts
🔀 Multi-repo context grimmory-tools/grimmory-docs
[::grimmory-tools/grimmory-docs::] docs/metadata/metadata-settings.md — multiple sections describe "Sidecar JSON Files" and the Metadata Persistence settings page where "Enable Sidecar JSON" is documented (UI text, images, and behavior). Relevant lines: mentions enabling/disabling writing sidecar JSON alongside books and bulk export/import controls.
[::grimmory-tools/grimmory-docs::] docs/metadata/metadata-center.md — documents the Metadata Center UI including a Sidecar section/tab (images and descriptions of Export/Import, sync status, and that tabs shown depend on the book). Relevant lines: describes Sidecar tab content and that tabs depend on book type.
[::grimmory-tools/grimmory-docs::] docs/metadata/sidecar-files.md — details sidecar file purpose, export/import actions, write-on-update/write-on-scan options, and UI elements (Export/Import, sync badge).
[::grimmory-tools/grimmory-docs::] docs/metadata/metadata-fetch-configuration.md — references sidecar export/import buttons at library level and sidecar-related bulk operations.
Summary / impact:
- The docs confirm a user-facing "Enable Sidecar JSON" setting and numerous UI references to a Sidecar tab/feature. Hiding the Sidecar tab when sidecar JSON is disabled aligns with documented settings and UI behavior; no other repos were available to search. This documentation should be updated if the UI behavior (tab visibility) changes in a way that needs mention.
🔇 Additional comments (1)
frontend/src/app/features/metadata/component/book-metadata-center/book-metadata-center.component.html (1)
40-45: LGTM — single source of truth for sidecar visibility.Both the tab header and panel gate on
canShowSidecarTab, so they cannot diverge.Also applies to: 66-70
Description
Currently, Sidecar tab is gated by user permissions, is physical and is local, but not if Sidecar is enabled or not. Hide the Sidecar tab in the book details metadata center when
Enable Sidecar JSONis disabled.Linked Issue: Fixes #810
Changes
[...].sidecarSettings.enabledNote: In testing this, I found that setting the URL param for the view to a view a user doesn't have permissions for just shows an empty screen. Created a follow up outside of this PR on #813
Summary by CodeRabbit