refactor(frontend): custom font files caching#376
refactor(frontend): custom font files caching#376NamLe029 wants to merge 163 commits intogrimmory-tools:developfrom
Conversation
## [2.3.0](grimmory-tools/grimmory@v2.2.6...v2.3.0) (2026-03-21) ### Features * **release:** document develop-based stable release previews ([930e526](grimmory-tools@930e526)) ### Bug Fixes * **api:** fix potential memory leaks in file processing ([031e8ae](grimmory-tools@031e8ae)) * **ci:** correct artifact download action pin ([37ca101](grimmory-tools@37ca101)) * **ci:** publish PR test results from workflow_run ([11a76bf](grimmory-tools@11a76bf)) * **ci:** repair release preview and test result publishing ([afa5b81](grimmory-tools@afa5b81)) * drop telemetry from app ([grimmory-tools#52](grimmory-tools#52)) ([4d82cb7](grimmory-tools@4d82cb7)) * **ui:** repair frontend compile after rebrand ([fea1ec6](grimmory-tools@fea1ec6)) ### Refactors * **build:** rename frontend dist output to grimmory ([ecf388f](grimmory-tools@ecf388f)) * **i18n:** rename booklore translation keys to grimmory ([eb94afa](grimmory-tools@eb94afa)) * **metadata:** move default parser from Amazon to Goodreads ([e252122](grimmory-tools@e252122)) * pull kepubify & ffprobe during build ([grimmory-tools#50](grimmory-tools#50)) ([1c15629](grimmory-tools@1c15629)) * **ui:** rebrand frontend surfaces to grimmory ([d786dd8](grimmory-tools@d786dd8)) ### Chores * **api:** remove the custom startup banner ([98c9b1a](grimmory-tools@98c9b1a)) * **deps:** bump flatted from 3.4.1 to 3.4.2 in /booklore-ui ([grimmory-tools#73](grimmory-tools#73)) ([c4bd0c7](grimmory-tools@c4bd0c7)) * **funding:** point support links at opencollective ([55c0ac0](grimmory-tools@55c0ac0)) * **release:** 2.2.7 [skip ci] ([0b5e24c](grimmory-tools@0b5e24c)) * remove old verbose PR template, replace with temporary more low-key one. ([grimmory-tools#84](grimmory-tools#84)) ([b868526](grimmory-tools@b868526)) * **ui:** drop financial support dialog ([grimmory-tools#21](grimmory-tools#21)) ([62be6b1](grimmory-tools@62be6b1)) ### Documentation * updated supported file formats in README.md ([grimmory-tools#68](grimmory-tools#68)) ([f912e80](grimmory-tools@f912e80)) ### Style * **i18n:** normalize translation json formatting ([grimmory-tools#89](grimmory-tools#89)) ([857290d](grimmory-tools@857290d)) * **ui:** simplify the topbar logo branding ([0416d48](grimmory-tools@0416d48)) (cherry picked from commit c335c7b)
…ooklore PR Port) (grimmory-tools#16) * fix(api): Correct format for fixed-layout epub when using Kobo Sync * chore: Cleanup duplicate code * feat: Cache fixed-layout property in db * fix: Missing nullcheck in after retrieving BookFileEntity * fix: Remove memory leak in EpubReaderService.java * chore: Avoid opening the epub twice for fixed-layout extraction * chore: Fix DB migration from rebase --------- Co-authored-by: brios <127139797+balazs-szucs@users.noreply.github.com>
grimmory-tools#79) * chore(deps): bump actions/setup-node from 6.2.0 to 6.3.0 (grimmory-tools#1) Dependabot couldn't find the original pull request head commit, ea510f4. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/setup-buildx-action from 3.12.0 to 4.0.0 (grimmory-tools#2) Dependabot couldn't find the original pull request head commit, faed6bf. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/build-push-action from 6.19.2 to 7.0.0 (grimmory-tools#3) Dependabot couldn't find the original pull request head commit, f110823. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/login-action from 3.7.0 to 4.0.0 (grimmory-tools#6) Dependabot couldn't find the original pull request head commit, 9a8d7a1. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(archive): infer archive type via Magic Numbers instead of filename * fix(archive): improve archive type detection and improve logging for cover image retrieval --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…unt (grimmory-tools#87) * fix: fixed halves missing from series number in Hardcover metadata * fix: use series primary books count
…ry-tools#117) * refactor(frontend): migrate state to TanStack Query and signals * fix(frontend): keep book cache in sync after patches
…hints to supported versions (grimmory-tools#76) * chore(deps): bump actions/setup-node from 6.2.0 to 6.3.0 (grimmory-tools#1) Dependabot couldn't find the original pull request head commit, ea510f4. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/setup-buildx-action from 3.12.0 to 4.0.0 (grimmory-tools#2) Dependabot couldn't find the original pull request head commit, faed6bf. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/build-push-action from 6.19.2 to 7.0.0 (grimmory-tools#3) Dependabot couldn't find the original pull request head commit, f110823. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/login-action from 3.7.0 to 4.0.0 (grimmory-tools#6) Dependabot couldn't find the original pull request head commit, 9a8d7a1. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs(security): update security policy with reporting guidelines and hints to supported versions --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…or improved configuration (grimmory-tools#139)
…oring services (grimmory-tools#137) * refactor(concurrency): improve thread management and locking in monitoring services * chore: remove unnecessary comments
the kepubify / ffprobe binaries may be on disk but they're in the "data" directory under `tools` rather than under the current directory
… in the app (grimmory-tools#158) Co-authored-by: Zack Yancey <yanceyz@proton.me>
…rimmory-tools#100) * chore(build): migrate Gradle build scripts from Groovy to Kotlin DSL * chore(docker): update Gradle build files to Kotlin DSL in Dockerfile
…tead of better fields like ISBN in Goodreads/Bookdrop (grimmory-tools#85) * chore(deps): bump actions/setup-node from 6.2.0 to 6.3.0 (grimmory-tools#1) Dependabot couldn't find the original pull request head commit, ea510f4. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/setup-buildx-action from 3.12.0 to 4.0.0 (grimmory-tools#2) Dependabot couldn't find the original pull request head commit, faed6bf. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/build-push-action from 6.19.2 to 7.0.0 (grimmory-tools#3) Dependabot couldn't find the original pull request head commit, f110823. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/login-action from 3.7.0 to 4.0.0 (grimmory-tools#6) Dependabot couldn't find the original pull request head commit, 9a8d7a1. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(metadata): fix metadata fetching relying for filename "first" instead of better fields like ISBN in Goodreads/Bookdrop * perf(ci): speed up image builds and centralize cache writes - Image builds: - pin frontend and backend Docker build stages to the build platform so multi-arch packaging can reuse architecture-independent work - add an Angular build cache mount and move dynamic version metadata to the end of the runtime stage so static layers stay reusable across tags - reduce image workflow checkout depth and keep preview builds on `linux/amd64` only to avoid unnecessary QEMU and history overhead - Cache policy: - make CI packaging smoke builds consume the shared image cache without writing new BuildKit cache state - make normal preview builds consume shared GHA and registry caches without mutating the canonical cache - keep nightly and stable release builds as the workflows that refresh the shared image cache in both GHA and registry backends - Preview override: - add a `refresh_shared_cache` input to the preview workflow for an explicit no-cache rebuild that repopulates the shared cache when maintainers need to bust and refresh it - keep the default preview behavior optimized for fast disposable builds rather than cache churn - Validation: - keep workflow YAML parsing clean after the cache-policy changes - keep `git diff --check` clean for the touched Docker and workflow files * docs: Add a note about how to make a release * chore: remove unncesary comments --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: James Cox <james@imaj.es>
…nt of frontend lint issues (grimmory-tools#163) * feat(lint): add Angular Lint Threshold workflow for CI integration * Update workflow name for clarity in CI integration * fix(lint): update LINT_THRESHOLD to 725 in CI checks * Also run workflow on main branch Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(lint): simplify lint problem count calculation using jq * feat(lint): enhance quality threshold checks and reporting in CI workflow * refactor(lint): rename lint step for clarity in CI workflow * fix(lint): swap lint warning and error thresholds for correct configuration * fix(lint): update build warning threshold and correct log processing for accurate warning count * refactor(lint): remove unnecessary working-directory specification for quality thresholds step --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…ols#56) * refactor(icon): fix SVG icon loading straight to memory * refactor(icon): loading with improved error handling * refactor(icon): add flag for SVG icons loading state * chore(release): 2.3.0 [skip ci] ## [2.3.0](grimmory-tools/grimmory@v2.2.6...v2.3.0) (2026-03-21) ### Features * **release:** document develop-based stable release previews ([930e526](grimmory-tools@930e526)) ### Bug Fixes * **api:** fix potential memory leaks in file processing ([031e8ae](grimmory-tools@031e8ae)) * **ci:** correct artifact download action pin ([37ca101](grimmory-tools@37ca101)) * **ci:** publish PR test results from workflow_run ([11a76bf](grimmory-tools@11a76bf)) * **ci:** repair release preview and test result publishing ([afa5b81](grimmory-tools@afa5b81)) * drop telemetry from app ([grimmory-tools#52](grimmory-tools#52)) ([4d82cb7](grimmory-tools@4d82cb7)) * **ui:** repair frontend compile after rebrand ([fea1ec6](grimmory-tools@fea1ec6)) ### Refactors * **build:** rename frontend dist output to grimmory ([ecf388f](grimmory-tools@ecf388f)) * **i18n:** rename booklore translation keys to grimmory ([eb94afa](grimmory-tools@eb94afa)) * **metadata:** move default parser from Amazon to Goodreads ([e252122](grimmory-tools@e252122)) * pull kepubify & ffprobe during build ([grimmory-tools#50](grimmory-tools#50)) ([1c15629](grimmory-tools@1c15629)) * **ui:** rebrand frontend surfaces to grimmory ([d786dd8](grimmory-tools@d786dd8)) ### Chores * **api:** remove the custom startup banner ([98c9b1a](grimmory-tools@98c9b1a)) * **deps:** bump flatted from 3.4.1 to 3.4.2 in /booklore-ui ([grimmory-tools#73](grimmory-tools#73)) ([c4bd0c7](grimmory-tools@c4bd0c7)) * **funding:** point support links at opencollective ([55c0ac0](grimmory-tools@55c0ac0)) * **release:** 2.2.7 [skip ci] ([0b5e24c](grimmory-tools@0b5e24c)) * remove old verbose PR template, replace with temporary more low-key one. ([grimmory-tools#84](grimmory-tools#84)) ([b868526](grimmory-tools@b868526)) * **ui:** drop financial support dialog ([grimmory-tools#21](grimmory-tools#21)) ([62be6b1](grimmory-tools@62be6b1)) ### Documentation * updated supported file formats in README.md ([grimmory-tools#68](grimmory-tools#68)) ([f912e80](grimmory-tools@f912e80)) ### Style * **i18n:** normalize translation json formatting ([grimmory-tools#89](grimmory-tools#89)) ([857290d](grimmory-tools@857290d)) * **ui:** simplify the topbar logo branding ([0416d48](grimmory-tools@0416d48)) * fix(book-browser): prevent memory leaks by unsubscribing from observables (grimmory-tools#80) * test(icon): remove redundant initialization in IconServiceTest --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Alex Fair <20632147+afairgiant@users.noreply.github.com>
- Command surface: - add a root `Justfile` with namespaced `api`, `ui`, and `release` modules so humans and agents can use the same entrypoints from the repo root - add project-local Justfiles for `booklore-api`, `booklore-ui`, and `tools/release` so each area remains self-contained and can still be driven from inside its own directory - standardize local developer workflows around `just check`, `just test`, `just api run`, `just ui dev`, `just db-up`, and `just image-build` - Workflow integration: - update the reusable test suite workflow to install `just` and run backend and frontend CI steps through the Justfiles instead of hardcoded shell commands - update the semantic-release workflows to install `just` and invoke release tooling via `just release install`, `just release preview`, and `just release run` - keep the Docker image publication workflows on the native Docker actions while moving the project-command logic into the canonical command surface - Documentation: - split backend and frontend implementation guidance into `booklore-api/README.md`, `booklore-api/CONTRIBUTING.md`, `booklore-ui/README.md`, and `booklore-ui/CONTRIBUTING.md` - simplify the top-level `README.md` and `CONTRIBUTING.md` so they stay focused on overview, policy, and navigation while pointing to component-specific guides for deeper detail - align contributor-facing examples on the `just` interface instead of mixing ad hoc Gradle, npm, Docker, and Compose commands - Developer environment: - add `mise.toml` to pin the expected Java and Node toolchain versions for local work - switch the development Compose defaults from the old Booklore database values to Grimmory-oriented defaults - keep the command layout ready for a future API/UI split without forcing that packaging change yet
- Issue intake: - rebrand the bug and feature request forms from Booklore to Grimmory - remove the old `[Bug]` and `[Feature]` title prefixes so issue titles can stay natural - default issue intake to `needs-triage` while relying on GitHub Issue Types for the primary classification - New templates: - add a dedicated enhancement template for smaller quality-of-life and UX improvements - add a documentation template for doc fixes, missing guides, and confusing setup flows - add a performance template for runtime, UI, build, and operational performance problems - Type alignment: - map the templates onto the repo's issue-type taxonomy with `Bug`, `Feature`, `Enhancement`, `Documentation`, and `Performance` - keep the larger feature template distinct from the smaller enhancement template so incoming requests are easier to triage
- Community links: - update the issue-template support link to use the canonical Grimmory Discord invite from `README.md` - update the contributing guide to point at the same Discord server - Consistency: - remove the stale invite URL so issue intake, contributor docs, and the repository landing page all reference the same community link
- Community links: - replace the old Discord invite code with the new `9YJ7HB4n8T` invite in the repository README - update the contributor guide to point at the same server - align the issue-template support link with the same canonical invite - Consistency: - keep the public docs and issue intake flow on a single Discord destination so users and contributors land in the right community space
- Agent workflow: - add a root `AGENTS.md` focused on agent execution rather than general contributor onboarding - document the expected command surface, branch target, and staged verification order - Repo boundaries: - map the backend and frontend layout with the key source, resource, test, i18n, and asset paths - call out ownership boundaries for deploy, packaging, tools, docs, and shared assets - Project rules: - capture backend and frontend conventions that agents should follow before editing or validating work - include repo-specific PR expectations such as linked issues, test output, and UI evidence
- GitHub Actions caching: - replace implicit setup-java cache writes with explicit Gradle cache restore/save steps - only persist the shared Gradle cache from long-lived develop and release workflows - remove the unnecessary QEMU setup from the single-arch smoke build - disable QEMU image caching in multi-arch publish workflows to stop repeated binfmt cache entries - CodeQL workflow: - replace the generated advanced template with a pinned repo-owned CodeQL workflow - add per-ref concurrency to cancel superseded CodeQL runs before they create more caches - restrict dependency caching to Java, using restore-only on pull requests and full caching on long-lived runs - disable trap caching and turn off PR overlay database caching to avoid repetitive low-value CodeQL caches - Validation: - validated the edited workflow YAML with yq - did not execute the workflows locally
- Planning: - add the staged frontend migration plan under docs/plans - record the intended rename, Yarn 4 adoption, lint rollout, and PR replay sequence - Cleanup ledger: - capture the remaining frontend Booklore-era aliases that should survive the cutover temporarily - document explicit triggers for removing each compatibility shim after the dust settles
…ding of wasm PDFium workers (grimmory-tools#333) * refactor(pdf-viewer): improve worker and blob handling for better loading of wasm PDFium workers * feat(doc-viewer): add dismissible info banner for saved changes
…mory-tools#331) Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.69.10 to 2.71.0. - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](taiki-e/install-action@7627fb4...a1df912) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.71.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…mory-tools#344) Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash-es dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…rimmory-tools#342) Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash-es dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…immory-tools#339) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.23...4.18.1) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.18.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore(api): fix typing for kobo sync service * feat(api): add more paths for grimmory/kobo API * fix(api): disable kobo features if kobo is unavailable * feat(api): add proxy flag to kobo setting * feat(api): adhere to kobo proxy enabled flag * feat(ui): add forward requests toggle * fix(ui): load setting * style: use assertNotNull * fix: reduce advertised routes this cuts down the advertised routes - in particular to reduce the number of auth-related requests we will be taking on. we may eventually want to do this but I want to reduce the amount of change in this PR * fix: undo disable of kobo features This reverts commit 1b242fa.
… authenticated (grimmory-tools#385) * fix(frontend): library health pings even not authenticated * test * authInitialized guard * subscription
* fix(api): use CBX Extractor in the CBX processor instead of implementing a separate cover detection algorithm in CBX processor, this uses the CBXMetadataExtractor to fetch the cover image. this means we now will take into account expected file names, `ComicInfo.xml` definitions, and fall back to correctly sorted files * style(api): drop unused imports * fix(api): drop placeholder for cbx this brings the CBX metadata extractor in line with the other metadata extractors that return null when no cover can be found * fix: handle no cover
…outes (grimmory-tools#361) * see pr message * fix: + use PathPattern for request matching + include common public route to whitelist + logging for failed queryjwt * forgot to remove debug code
…ycle (grimmory-tools#322) * refactor(threads): replace manual thread management with Spring lifecycle - Convert LibraryWatchService, LibraryFileEventProcessor, BookdropMonitoringService, BookdropEventHandlerService to SmartLifecycle for proper startup/shutdown ordering - Replace Executors.newCachedThreadPool() in FilenamePatternExtractor with Spring-managed ExecutorService bean - Delete SecurityContextVirtualThread utility class - Add DelegatingSecurityContextRunnable task decorator to TaskExecutorConfig for automatic security context propagation - Replace SecurityContextVirtualThread.runWithSecurityContext() calls in TaskService, BookCoverService, SendEmailV2Service, LibraryService with injected Executor - Fix CbxReaderService pre-existing compile error (transferEntryTo) * refactor(threads): improve thread management with ThreadPoolTaskExecutor and improve shutdown handling * fix: fix broken test
…ding position across devices (grimmory-tools#358) * fix(reader): add fetchFreshBookDetail method to ensure up-to-date reading position across devices * test(audiobook): enhance tests to verify queryClient interactions for progress updates * fix(audiobook): include audiobookProgress and fileProgress in progress payload
6246cca to
0d28ee0
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
frontend/src/app/shared/service/custom-font.service.ts (1)
110-118:⚠️ Potential issue | 🟡 MinorRevoke temporary blob URLs to avoid leaking browser memory.
URL.createObjectURL(...)is used, but the created URL is never released. Please revoke it in afinallyblock afterFontFace.load()/add flow.Proposed fix
async loadFontFace(font: CustomFont): Promise<void> { if (this.loadedFonts.has(font.fontName)) { return; } + let objectUrl: string | null = null; try { - let fontUrl = this.getFontUrl(font.id); + const fontUrl = this.getFontUrl(font.id); if (this.localSettingsService.get().cacheStorageEnabled) { const cachedBlob = await (await this.cacheStorageService.getCache(fontUrl)).blob(); - fontUrl = URL.createObjectURL(cachedBlob); + objectUrl = URL.createObjectURL(cachedBlob); } else { const freshBlob = await firstValueFrom(this.http.get(fontUrl, {responseType: 'blob'})); - fontUrl = URL.createObjectURL(freshBlob); + objectUrl = URL.createObjectURL(freshBlob); } const fontFace = new FontFace( font.fontName, - `url(${fontUrl})`, + `url(${objectUrl})`, { weight: 'normal', style: 'normal' } ); await fontFace.load(); document.fonts.add(fontFace); this.loadedFonts.add(font.fontName); } catch (error) { console.error(`Failed to load font ${font.fontName}:`, error); throw error; + } finally { + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + } } }#!/bin/bash # Verify object URL creation/revocation pairing in this service. rg -n -C2 'createObjectURL|revokeObjectURL' frontend/src/app/shared/service/custom-font.service.tsAlso applies to: 122-131
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/app/shared/service/custom-font.service.ts` around lines 110 - 118, The code creates temporary blob URLs via URL.createObjectURL in custom-font.service.ts (see getFontUrl usage and branches that call cacheStorageService.getCache(...) and http.get(...)), but never revokes them; wrap the FontFace.load()/font addition flow in a try/finally and store the created object URL in a local variable (e.g., tempFontUrl) so that in finally you call URL.revokeObjectURL(tempFontUrl) if it was created; ensure you only revoke URLs you created (skip revoking the original remote URL string) and handle both branches (cachedBlob and freshBlob) so every createObjectURL call has a matching revoke.
🧹 Nitpick comments (1)
booklore-api/src/main/java/org/booklore/controller/CustomFontController.java (1)
78-80: Avoid double lookup for the same font in one request.Line 78 and Line 79 currently trigger separate service/repository reads for identical
(fontId, userId). Consolidating into one service call reduces DB load and removes a small inconsistency window.♻️ Suggested direction
- File fontFile = customFontService.getFontFile(fontId, user.getId()); - FontFormat format = customFontService.getFontFormat(fontId, user.getId()); + var fontResource = customFontService.getFontResource(fontId, user.getId()); + File fontFile = fontResource.file(); + FontFormat format = fontResource.format();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@booklore-api/src/main/java/org/booklore/controller/CustomFontController.java` around lines 78 - 80, CustomFontController currently calls customFontService.getFontFile(fontId, user.getId()) and customFontService.getFontFormat(fontId, user.getId()) separately causing duplicate lookups; change the service to return both file and format in a single call (e.g., add a DTO or pair) and replace the two calls with one call like customFontService.getFontResource(fontId, user.getId()) that returns {file, format}, then use the returned file for FileUtils.getFileLastModified(fontFile.toPath()) and the returned format (FontFormat) where needed to eliminate the double lookup and any inconsistency window.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@frontend/src/app/shared/service/custom-font.service.ts`:
- Around line 110-118: The code creates temporary blob URLs via
URL.createObjectURL in custom-font.service.ts (see getFontUrl usage and branches
that call cacheStorageService.getCache(...) and http.get(...)), but never
revokes them; wrap the FontFace.load()/font addition flow in a try/finally and
store the created object URL in a local variable (e.g., tempFontUrl) so that in
finally you call URL.revokeObjectURL(tempFontUrl) if it was created; ensure you
only revoke URLs you created (skip revoking the original remote URL string) and
handle both branches (cachedBlob and freshBlob) so every createObjectURL call
has a matching revoke.
---
Nitpick comments:
In
`@booklore-api/src/main/java/org/booklore/controller/CustomFontController.java`:
- Around line 78-80: CustomFontController currently calls
customFontService.getFontFile(fontId, user.getId()) and
customFontService.getFontFormat(fontId, user.getId()) separately causing
duplicate lookups; change the service to return both file and format in a single
call (e.g., add a DTO or pair) and replace the two calls with one call like
customFontService.getFontResource(fontId, user.getId()) that returns {file,
format}, then use the returned file for
FileUtils.getFileLastModified(fontFile.toPath()) and the returned format
(FontFormat) where needed to eliminate the double lookup and any inconsistency
window.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 94e48418-e4dc-4981-a483-7c494bb88d5e
📒 Files selected for processing (5)
booklore-api/src/main/java/org/booklore/controller/CustomFontController.javabooklore-api/src/main/java/org/booklore/service/customfont/CustomFontService.javabooklore-api/src/test/java/org/booklore/service/customfont/CustomFontServiceTest.javafrontend/src/app/features/readers/ebook-reader/features/fonts/custom-font.service.tsfrontend/src/app/shared/service/custom-font.service.ts
✅ Files skipped from review due to trivial changes (1)
- frontend/src/app/features/readers/ebook-reader/features/fonts/custom-font.service.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). (1)
- GitHub Check: Test Suite / Backend Tests
🧰 Additional context used
📓 Path-based instructions (4)
booklore-api/src/**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
booklore-api/src/**/*.java: Use 4-space indentation in Java files and match surrounding Java style
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce@Autowiredfield injection
Use MapStruct for entity/DTO mapping
Files:
booklore-api/src/main/java/org/booklore/controller/CustomFontController.javabooklore-api/src/main/java/org/booklore/service/customfont/CustomFontService.javabooklore-api/src/test/java/org/booklore/service/customfont/CustomFontServiceTest.java
booklore-api/src/test/**/*.java
📄 CodeRabbit inference engine (AGENTS.md)
Prefer focused unit tests; use
@SpringBootTestonly when the Spring context is required
Files:
booklore-api/src/test/java/org/booklore/service/customfont/CustomFontServiceTest.java
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/shared/service/custom-font.service.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/shared/service/custom-font.service.ts
🧠 Learnings (2)
📚 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/shared/service/custom-font.service.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/shared/service/custom-font.service.ts
🔇 Additional comments (3)
frontend/src/app/shared/service/custom-font.service.ts (1)
3-3: Nice split between cache-backed and network-backed blob loading.The
cacheStorageEnabledbranch andinject()-based dependency wiring are clean and consistent with the existing service design.Also applies to: 8-9, 22-23, 112-118
booklore-api/src/main/java/org/booklore/service/customfont/CustomFontService.java (1)
157-177: Return-type refactor is consistent and safe here.Line 157 and Line 177 correctly switch this method to return
Filewhile preserving authorization, path validation, and on-disk existence checks.booklore-api/src/test/java/org/booklore/service/customfont/CustomFontServiceTest.java (1)
305-310: Test update correctly follows the new service contract.The assertion now validates the returned
Filepath directly, which aligns with the refactoredgetFontFilereturn type.
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/readers/ebook-reader/features/fonts/custom-font.service.spec.ts`:
- Around line 47-50: The tests currently stub localSettingsService.get() to
always return {cacheStorageEnabled: false}, so add at least one test case that
stubs localSettingsService.get() to return {cacheStorageEnabled: true} and
asserts the cache-enabled behavior of CustomFontService (or the test subject in
custom-font.service.spec.ts); specifically, update the localSettingsService mock
used by the suite to return true within this new test, invoke the relevant
method(s) that should take the cache path (e.g., the method that reads/writes
font data), and assert that the caching functions are called/used (or that the
cache-specific side effects occur) rather than the non-cache path.
🪄 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: 35f8ee99-8b86-4602-a2ce-40514bd8fae1
📒 Files selected for processing (1)
frontend/src/app/features/readers/ebook-reader/features/fonts/custom-font.service.spec.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 (3)
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/readers/ebook-reader/features/fonts/custom-font.service.spec.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/readers/ebook-reader/features/fonts/custom-font.service.spec.ts
frontend/src/**/*.{test,spec}.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use Vitest for tests in the frontend
Files:
frontend/src/app/features/readers/ebook-reader/features/fonts/custom-font.service.spec.ts
🧠 Learnings (2)
📚 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/readers/ebook-reader/features/fonts/custom-font.service.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/readers/ebook-reader/features/fonts/custom-font.service.spec.ts
🔇 Additional comments (1)
frontend/src/app/features/readers/ebook-reader/features/fonts/custom-font.service.spec.ts (1)
9-9: Good dependency alignment in TestBed setup.Adding
LocalSettingsServiceto the test module keeps this spec aligned with the new service dependency.Also applies to: 61-61
| localSettingsService = { | ||
| get: vi.fn().mockReturnValue({cacheStorageEnabled: false}), | ||
| } | ||
|
|
There was a problem hiding this comment.
Add coverage for cacheStorageEnabled: true path.
get() is hardcoded to { cacheStorageEnabled: false }, so this suite only validates the non-cache flow. The new cache-enabled branch should have at least one dedicated test that sets cacheStorageEnabled: true and asserts the cache path behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@frontend/src/app/features/readers/ebook-reader/features/fonts/custom-font.service.spec.ts`
around lines 47 - 50, The tests currently stub localSettingsService.get() to
always return {cacheStorageEnabled: false}, so add at least one test case that
stubs localSettingsService.get() to return {cacheStorageEnabled: true} and
asserts the cache-enabled behavior of CustomFontService (or the test subject in
custom-font.service.spec.ts); specifically, update the localSettingsService mock
used by the suite to return true within this new test, invoke the relevant
method(s) that should take the cache path (e.g., the method that reads/writes
font data), and assert that the caching functions are called/used (or that the
cache-specific side effects occur) rather than the non-cache path.
zachyale
left a comment
There was a problem hiding this comment.
@NamLe029 The functional changes in the PR look good, but specs are failing and need to be updated for closer compliance with your new implementation of EpubCustomFontService and the HttpClient + header/cache approach.
Let me know when specs have been addressed and we can get this in 👍
0bf513c to
62434c5
Compare
Description
Utilize persistent CacheStorage to cache font files on client-side, which may slightly improve load time.
Changes
Last-Modifiedheader in server response.Authorizatrionheader (used to be passed as a URL parameter).Notes
Authorizatrionheader authentication for custom font is only supported until thenSummary by CodeRabbit
New Features
Improvements
Frontend
Tests