Skip to content

refactor(frontend): custom font files caching#376

Draft
NamLe029 wants to merge 163 commits intogrimmory-tools:developfrom
NamLe029:custom-font-caching
Draft

refactor(frontend): custom font files caching#376
NamLe029 wants to merge 163 commits intogrimmory-tools:developfrom
NamLe029:custom-font-caching

Conversation

@NamLe029
Copy link
Copy Markdown
Contributor

@NamLe029 NamLe029 commented Apr 4, 2026

Description

Utilize persistent CacheStorage to cache font files on client-side, which may slightly improve load time.

Changes

  • Use CacheStorage to cache font files.
  • Include Last-Modified header in server response.
  • Pass authentication token by Authorizatrion header (used to be passed as a URL parameter).

Notes

Summary by CodeRabbit

  • New Features

    • Optional custom-font caching: when enabled, fonts load from local cache for faster startup; when disabled, fonts are fetched on demand.
  • Improvements

    • Font downloads now set clearer HTTP cache and content headers (including Last-Modified) to improve client handling.
  • Frontend

    • Font loading uses blob-derived object URLs for consistent rendering across cache/no-cache modes.
  • Tests

    • Updated tests to cover the new cache-dependent font-loading behavior.

github-actions Bot and others added 30 commits March 21, 2026 02:18
## [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>
…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
@balazs-szucs balazs-szucs marked this pull request as draft April 4, 2026 22:59
astappiev and others added 14 commits April 5, 2026 14:04
…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
@NamLe029 NamLe029 force-pushed the custom-font-caching branch from 6246cca to 0d28ee0 Compare April 7, 2026 12:07
@NamLe029 NamLe029 marked this pull request as ready for review April 7, 2026 12:09
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
frontend/src/app/shared/service/custom-font.service.ts (1)

110-118: ⚠️ Potential issue | 🟡 Minor

Revoke temporary blob URLs to avoid leaking browser memory.

URL.createObjectURL(...) is used, but the created URL is never released. Please revoke it in a finally block after FontFace.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.ts

Also 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6246cca and 0d28ee0.

📒 Files selected for processing (5)
  • booklore-api/src/main/java/org/booklore/controller/CustomFontController.java
  • booklore-api/src/main/java/org/booklore/service/customfont/CustomFontService.java
  • booklore-api/src/test/java/org/booklore/service/customfont/CustomFontServiceTest.java
  • frontend/src/app/features/readers/ebook-reader/features/fonts/custom-font.service.ts
  • frontend/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 @Autowired field injection
Use MapStruct for entity/DTO mapping

Files:

  • booklore-api/src/main/java/org/booklore/controller/CustomFontController.java
  • booklore-api/src/main/java/org/booklore/service/customfont/CustomFontService.java
  • booklore-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 @SpringBootTest only 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}: Prefer inject() over constructor injection
Follow frontend/eslint.config.js: component selectors use app-*, directive selectors use app*, and any is 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 cacheStorageEnabled branch and inject()-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 File while 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 File path directly, which aligns with the refactored getFontFile return type.

@coderabbitai coderabbitai Bot requested review from imajes and zachyale April 7, 2026 13:09
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0d28ee0 and baf9bfd.

📒 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}: Prefer inject() over constructor injection
Follow frontend/eslint.config.js: component selectors use app-*, directive selectors use app*, and any is 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 LocalSettingsService to the test module keeps this spec aligned with the new service dependency.

Also applies to: 61-61

Comment on lines +47 to +50
localSettingsService = {
get: vi.fn().mockReturnValue({cacheStorageEnabled: false}),
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Copy link
Copy Markdown
Member

@zachyale zachyale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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 👍

@NamLe029 NamLe029 marked this pull request as draft April 9, 2026 00:50
@zachyale zachyale force-pushed the develop branch 2 times, most recently from 0bf513c to 62434c5 Compare April 22, 2026 21:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.