Skip to content

feat(web-reader): HTTP Caching & Cache Storage for content responses#75

Merged
balazs-szucs merged 8 commits into
grimmory-tools:developfrom
NamLe029:web-reader-caching
Apr 3, 2026
Merged

feat(web-reader): HTTP Caching & Cache Storage for content responses#75
balazs-szucs merged 8 commits into
grimmory-tools:developfrom
NamLe029:web-reader-caching

Conversation

@NamLe029

@NamLe029 NamLe029 commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Description

This PR introduces:

  • HTTP Caching via Last-Modified at EPUB, PDF, audiobook content endpoints
  • Cache Storage for persistent content response caching on client side (only PDF, EPUB)

Improvements:
This feature helps reducing traffic to the server, especially for large book files.

How does Cache Storage work?
When enabled, all EPUB, PDF content responses will be cached. Subsequent requests will reuse that cached response as long as the response is deemed valid (via a HEAD request with the If-Modified-Since header to the server).

Additional changes:

  • LocalSettings for device-scoped settings (to control Cache Storage behaviors)
  • UI for LocalSettings

Testing

  • ./gradlew test passes
  • ng test passes
  • Cache Storage behaves as expected
  • Settings UI for Cache Storage behaves as expected

Screenshots

Local Settings UI
image

Summary by CodeRabbit

  • New Features

    • Browser Cache Storage: cache/revalidate fetched book and audiobook content, view usage, and clear cache.
    • "Local" settings tab and UI to toggle caching, refresh usage, and clear cache.
    • PDF viewer and file-fetching now use cached content when enabled.
  • Bug Fixes & Improvements

    • Server accepts HEAD and If-Modified-Since; responses expose Last-Modified.
    • Improved cache-control and dynamic content-type handling.

@coderabbitai

coderabbitai Bot commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Backend streaming endpoints changed to return ResponseEntity with dynamic Content-Type and optional Last-Modified; CORS updated to allow HEAD and cache headers. Added FileUtils.getFileLastModified. Frontend: introduced CacheStorageService and LocalSettingsService, a Local Settings UI, and integrated cache-aware loading into BookFileService and PDF reader (blob/object-URL handling).

Changes

Cohort / File(s) Summary
Security / CORS
booklore-api/src/main/java/org/booklore/config/security/SecurityConfig.java
Added HEAD to allowed methods, added If-Modified-Since to allowed request headers, and exposed Last-Modified.
Controllers & Service: streaming → ResponseEntity
booklore-api/src/main/java/org/booklore/controller/AudiobookReaderController.java, booklore-api/src/main/java/org/booklore/controller/BookController.java, booklore-api/src/main/java/org/booklore/service/book/BookService.java
Replaced servlet-style void streaming methods with endpoints returning ResponseEntity<? extends Resource>/ResponseEntity<Resource>; removed servlet request/response params and range-based streaming delegation; introduced helper response construction with dynamic Content-Type, Cache-Control, and optional Last-Modified.
File utilities
booklore-api/src/main/java/org/booklore/util/FileUtils.java
Added public Long getFileLastModified(Path) — returns millis or null on failure and logs errors.
Frontend: Cache API & Local settings services
booklore-ui/src/app/shared/service/cache-storage.service.ts, booklore-ui/src/app/shared/service/local-settings.service.ts
Added CacheStorageService (wraps browser Cache Storage, supports validation via HEAD/If-Modified-Since, put/match/delete/clear/getCacheSizeInBytes) and LocalSettingsService (persist cacheStorageEnabled, defaults, load/commit helpers).
Frontend: Content loading & PDF reader
booklore-ui/src/app/features/book/service/book-file.service.ts, booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts
BookFileService.getFileContent now uses CacheStorageService when caching enabled (returns Blob/object-URL path via from(...)); PDF reader disables streaming (disableStream = true), resolves content via cache-aware helper, and revokes created blob URLs on destroy.
Frontend: Local Settings UI & wiring
booklore-ui/src/app/features/settings/local-settings/local-settings.component.ts, .../local-settings.component.html, .../local-settings.component.scss, booklore-ui/src/app/features/settings/settings.component.ts, booklore-ui/src/app/features/settings/settings.component.html
Added LocalSettingsComponent with template and styles to toggle cache, display/refresh usage, clear cache; added Local tab and registered component in settings imports.

Sequence Diagram

sequenceDiagram
    actor Browser as Client/Browser
    participant App as Frontend App
    participant Cache as CacheStorage (Browser)
    participant Http as HttpClient/Network
    participant API as Backend API

    Browser->>App: request book/audio URI
    App->>Cache: getCache(uri)
    alt cache hit
        Cache-->>App: cached Response
        alt validate freshness (noValidate = false)
            App->>Http: HEAD uri (If-Modified-Since: cached.last-modified)
            Http-->>App: 304 Not Modified
            App-->>Browser: serve cached blob / object-URL
            else 200 OK
            Http-->>App: 200 + body + headers
            App->>Cache: put(uri, Response.clone())
            App-->>Browser: serve fetched blob / object-URL
        end
    else cache miss
        App->>Http: GET uri
        Http->>API: GET request
        API-->>Http: Response (+ Last-Modified?)
        Http-->>App: Response
        App->>Cache: put(uri, Response.clone())
        App-->>Browser: serve fetched blob / object-URL
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰
I tuck bytes in a secret lair,
HEAD taps check the cached-air,
Last-Modified hums the tune,
Blobs become a silver lune,
Hop — the reader wakes at noon.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically summarizes the main changes: introducing HTTP caching via Last-Modified headers and Cache Storage for persistent client-side caching of content responses.
Description check ✅ Passed The PR description covers the main objectives, improvements, how Cache Storage works, additional changes, and testing results. While somewhat informal in style, it provides sufficient detail about the feature's purpose and functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 9

🧹 Nitpick comments (2)
booklore-api/src/main/java/org/booklore/service/book/BookService.java (1)

31-31: Consider using explicit imports instead of wildcard.

Wildcard imports can obscure dependencies and potentially cause naming collisions. The explicit imports would be: CacheControl, MediaType, MediaTypeFactory, ResponseEntity.

Suggested explicit imports
-import org.springframework.http.*;
+import org.springframework.http.CacheControl;
+import org.springframework.http.MediaType;
+import org.springframework.http.MediaTypeFactory;
+import org.springframework.http.ResponseEntity;
🤖 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/service/book/BookService.java` at
line 31, Replace the wildcard import "import org.springframework.http.*;" in
BookService.java with explicit imports for the symbols used so imports are clear
and avoid collisions; specifically add explicit imports for CacheControl,
MediaType, MediaTypeFactory, and ResponseEntity (remove the wildcard and
organize/import only the classes referenced by methods in BookService).
booklore-ui/src/app/shared/service/cache-storage.service.ts (1)

43-43: Handle cache write failures from put() to avoid unhandled async errors.

this.put(uri, res.clone()) is fire-and-forget; if caches.open()/cache.put() fails, the rejection is unobserved.

Proposed fix
-    this.put(uri, res.clone());
+    await this.put(uri, res.clone()).catch((error) => {
+      console.error("Error writing cache:", error);
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-ui/src/app/shared/service/cache-storage.service.ts` at line 43, The
call this.put(uri, res.clone()) is currently fire-and-forget and can produce
unobserved promise rejections if caches.open()/cache.put() fails; update the
caller (or put method) to await the promise or attach a .catch handler to it and
log or handle errors to prevent unhandled async errors. Specifically, locate the
invocation this.put(uri, res.clone()) in CacheStorageService and change it to
either await this.put(uri, res.clone()) inside an async function or append
.catch(err => {/* log via your logger or console.error with context */}) so
failures from put() (and internals like caches.open()/cache.put()) are observed
and handled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@booklore-ui/src/app/features/book/service/book-file.service.ts`:
- Around line 34-36: The cache path in BookFileService (when
localSettingsService.get().cacheStorageEnabled is true) must handle rejections
and falsy responses from cacheStorageService.getCache(url) and fall back to
this.http.get; update the logic around cacheStorageService.getCache(url) (the
Promise wrapped with from(...)) to catch Promise rejections and check for a
falsy response/blob, and on error or missing data return the HTTP Observable
(this.http.get<Blob>(url, { responseType: 'blob' as 'json' })); implement this
using RxJS operators (e.g., from(...).pipe(catchError(...)) or
switchMap/mergeMap to choose the HTTP fallback) so cache failures do not
propagate to callers.

In `@booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts`:
- Around line 209-211: The constructed URI in pdf-reader.component.ts uses
direct concatenation of fileType into the query string (const uri = ... ?
`${API_CONFIG.BASE_URL}/api/v1/books/${bookId}/content?bookType=${fileType}` :
...), which can break for reserved characters; fix it by encoding the bookType
value with encodeURIComponent(fileType) when building the query parameter (keep
bookId and base URL unchanged) so the resulting uri is safe for HTTP requests.
- Around line 205-217: getBookData currently creates blob URLs via
URL.createObjectURL(blob) and assigns them to this.bookData without revoking
previous ones, and ngOnDestroy doesn't revoke the active blob URL; to fix,
introduce a property (e.g., currentBlobUrl) to track the active object URL,
ensure before assigning a new URL in getBookData you revoke the existing one via
URL.revokeObjectURL(currentBlobUrl) (and set currentBlobUrl to the new URL after
assignment), and revoke currentBlobUrl inside ngOnDestroy to release memory;
update any places that read/write this.bookData to use the tracked URL
accordingly (functions: getBookData, ngOnDestroy, and where this.bookData is
set/used).

In
`@booklore-ui/src/app/features/settings/local-settings/local-settings.component.html`:
- Around line 4-57: The template in local-settings.component.html is using
*transloco="let t; prefix: 'settingsNaming'" but still contains hardcoded
strings; replace each literal (header title/description, section
title/description, labels/descriptions for "Enable Cache Storage", "Cache
Storage Usage (MB)", "Clear Cache", and button labels "Refresh"/"Clear Cache")
with the appropriate transloco lookup using the t function (e.g. {{
t('localSettings.title') }}, {{ t('localSettings.description') }}, {{
t('cacheStorage.title') }}, etc.) while keeping bindings and event handlers
intact (settings.cacheStorageEnabled, cacheStorageUsageMb,
isLoadingCacheStorageUsage, isClearingCacheStorage, loadCacheStorageUsage(),
clearCacheStorage()); ensure keys use the existing prefix 'settingsNaming' so
strings localize consistently with other settings components.

In
`@booklore-ui/src/app/features/settings/local-settings/local-settings.component.scss`:
- Around line 9-45: Stylelint is flagging declaration-empty-line-before
violations in the SCSS blocks; open the .scss and for each selector shown in the
diff (.settings-title, .settings-description, .settings-card, .section-header,
.section-title, .section-description, .settings-content, .setting-item) adjust
blank lines so they follow the project's declaration-empty-line-before rule
(remove unexpected empty lines before declaration lists or add the single
required blank line where configured), ensure there are no extra blank lines
between declarations inside each block, and re-run stylelint (or apply stylelint
--fix) to verify the violations are resolved.

In
`@booklore-ui/src/app/features/settings/local-settings/local-settings.component.ts`:
- Around line 59-69: Wrap the body of the accept async handler in a
try/catch/finally: call await this.cacheStorageService.clear() and await
this.loadCacheStorageUsage() inside try, catch errors to call
this.messageService.add with a severity "error" and useful detail, and ensure
this.isClearingCacheStorage is always reset to false in finally; keep the
existing success message in the try path and reference the accept method,
isClearingCacheStorage flag, cacheStorageService.clear, loadCacheStorageUsage,
and messageService.add when making the changes.

In `@booklore-ui/src/app/features/settings/settings.component.html`:
- Around line 13-15: The "Local" tab label is hardcoded; update the p-tab
content that uses SettingsTab.LocalSettings to use the translation helper (e.g.,
{{ t('settings.tabs.local') }}) instead of the literal "Local", and add the
corresponding "settings.tabs.local" key with value "Local" to the translation
files (e.g., en.json) so the tab is internationalized; ensure you reference the
same key pattern used by other tabs like {{ t('reader') }}.

In `@booklore-ui/src/app/shared/service/cache-storage.service.ts`:
- Around line 97-101: The clear() method currently maps cache.keys() to an array
of this.delete(...) promises but doesn't wait for them, so fix clear() to await
all deletions before resolving: after obtaining keys from cache() and mapping to
this.delete(key.url), return or await Promise.all(...) of those delete promises
(or convert clear() to async and await the deletions). Ensure you still resolve
to void after all this.delete(...) calls complete, keeping references to the
cache() and delete(...) methods.
- Line 70: The catch clause in CacheStorageService uses an invalid union
annotation `catch (error: HttpErrorResponse | any)` which violates TS catch
typing rules; change the catch parameter to `catch (error: unknown)` in the
method where this appears (look for the catch in cache-storage.service.ts, e.g.,
inside methods of class CacheStorageService), then inside the catch block narrow
the error with runtime checks (for example use `if (error instanceof
HttpErrorResponse)` or type guard helpers) before accessing
HttpErrorResponse-specific properties, otherwise treat it as a generic error.

---

Nitpick comments:
In `@booklore-api/src/main/java/org/booklore/service/book/BookService.java`:
- Line 31: Replace the wildcard import "import org.springframework.http.*;" in
BookService.java with explicit imports for the symbols used so imports are clear
and avoid collisions; specifically add explicit imports for CacheControl,
MediaType, MediaTypeFactory, and ResponseEntity (remove the wildcard and
organize/import only the classes referenced by methods in BookService).

In `@booklore-ui/src/app/shared/service/cache-storage.service.ts`:
- Line 43: The call this.put(uri, res.clone()) is currently fire-and-forget and
can produce unobserved promise rejections if caches.open()/cache.put() fails;
update the caller (or put method) to await the promise or attach a .catch
handler to it and log or handle errors to prevent unhandled async errors.
Specifically, locate the invocation this.put(uri, res.clone()) in
CacheStorageService and change it to either await this.put(uri, res.clone())
inside an async function or append .catch(err => {/* log via your logger or
console.error with context */}) so failures from put() (and internals like
caches.open()/cache.put()) are observed and handled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 129b7314-91e8-4f5b-8d52-9964d60c56da

📥 Commits

Reviewing files that changed from the base of the PR and between 6ef4448 and a4e1a80.

📒 Files selected for processing (14)
  • booklore-api/src/main/java/org/booklore/config/security/SecurityConfig.java
  • booklore-api/src/main/java/org/booklore/controller/AudiobookReaderController.java
  • booklore-api/src/main/java/org/booklore/controller/BookController.java
  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
  • booklore-ui/src/app/features/book/service/book-file.service.ts
  • booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.html
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.scss
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.ts
  • booklore-ui/src/app/features/settings/settings.component.html
  • booklore-ui/src/app/features/settings/settings.component.ts
  • booklore-ui/src/app/shared/service/cache-storage.service.ts
  • booklore-ui/src/app/shared/service/local-settings.service.ts

Comment thread booklore-ui/src/app/features/book/service/book-file.service.ts
Comment thread booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts Outdated
Comment thread booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts Outdated
Comment thread booklore-ui/src/app/features/settings/settings.component.html Outdated
Comment thread booklore-ui/src/app/shared/service/cache-storage.service.ts Outdated
Comment thread booklore-ui/src/app/shared/service/cache-storage.service.ts Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@booklore-ui/src/app/shared/service/cache-storage.service.ts`:
- Around line 100-106: There is an extra closing brace that prematurely ends the
CacheStorageService class (after the clear() method) causing
getCacheSizeInBytes() and cache() to be parsed as top-level statements; remove
the spurious '}' so the class remains open and those methods
(getCacheSizeInBytes, cache, clear, etc.) are defined as members of
CacheStorageService.
- Around line 38-45: The call to this.put(uri, res.clone()) is not awaited so
getCache() can return before the entry is persisted and write failures are
ignored; update the code to await the cache write (await this.put(uri,
res.clone())) and wrap it in a try/catch so write errors are logged/handled (or
rethrown) rather than silently dropped; ensure put(uri, Response) returns a
Promise if it doesn't already and that callers of getCache()/put() can properly
observe failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d01da3b-d2e2-48ea-af14-560ed6801367

📥 Commits

Reviewing files that changed from the base of the PR and between a4e1a80 and d9708a6.

📒 Files selected for processing (2)
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.ts
  • booklore-ui/src/app/shared/service/cache-storage.service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.ts

Comment thread booklore-ui/src/app/shared/service/cache-storage.service.ts
Comment thread booklore-ui/src/app/shared/service/cache-storage.service.ts Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts (1)

101-117: ⚠️ Potential issue | 🟠 Major

Revoke prior blob URL before replacing bookData during in-component navigation.

Line 196 cleanup only runs on destroy; Line 117 can overwrite an existing blob: URL with a new one while the component is still alive (route param changes), leaving the old URL unreleased.

Proposed fix
         next: ({book, bookSetting, myself, bookData}) => {
+          if (this.bookData?.startsWith('blob:') && this.bookData !== bookData) {
+            URL.revokeObjectURL(this.bookData);
+          }
           const pdfMeta = book;
           const pdfPrefs = bookSetting;
@@
           this.page = pdfMeta.pdfProgress?.page || 1;
           this.bookData = bookData;

Also applies to: 196-198, 217-220

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts`
around lines 101 - 117, Before overwriting this.bookData with a new blob URL in
the next handler, revoke the previous blob URL to avoid leaking object URLs: if
this.bookData is a non-empty string that starts with "blob:" call
URL.revokeObjectURL(this.bookData) (guarding for null/undefined), then assign
the new bookData; apply the same revoke-before-assign pattern wherever
this.bookData is set (including the component's ngOnDestroy cleanup and any
other assignments) so previous blob URLs are always released.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@booklore-ui/src/app/shared/service/cache-storage.service.ts`:
- Around line 70-76: In the catch block inside CacheStorageService
(cache-storage.service.ts) remove the duplicated check for HttpErrorResponse
with status === 304 so the branch appears only once; locate the catch handling
(the function where the try/catch checks "error instanceof HttpErrorResponse &&
error.status === 304") and delete the redundant repeated if statement so the
not-modified return true path is present a single time.

---

Duplicate comments:
In `@booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts`:
- Around line 101-117: Before overwriting this.bookData with a new blob URL in
the next handler, revoke the previous blob URL to avoid leaking object URLs: if
this.bookData is a non-empty string that starts with "blob:" call
URL.revokeObjectURL(this.bookData) (guarding for null/undefined), then assign
the new bookData; apply the same revoke-before-assign pattern wherever
this.bookData is set (including the component's ngOnDestroy cleanup and any
other assignments) so previous blob URLs are always released.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd4a1d51-c9f8-403d-825c-589f85a0c968

📥 Commits

Reviewing files that changed from the base of the PR and between d9708a6 and 454279c.

📒 Files selected for processing (3)
  • booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.scss
  • booklore-ui/src/app/shared/service/cache-storage.service.ts
✅ Files skipped from review due to trivial changes (1)
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.scss

Comment thread booklore-ui/src/app/shared/service/cache-storage.service.ts Outdated

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (2)
booklore-ui/src/app/shared/service/cache-storage.service.ts (2)

104-111: Cache size calculation may be inaccurate when content-length header is missing.

The method relies on the content-length header which may not always be present (e.g., chunked responses, server omission). For responses without this header, size defaults to 0, under-reporting actual cache usage. Since this is displayed to users in the UI, consider using blob.size for accurate measurement.

♻️ Proposed fix using blob size
   getCacheSizeInBytes(): Promise<number> {
     return this.cache()
       .then((cache) => cache.keys())
       .then((keys) => keys.map((key) => this.match(key.url)))
       .then((promises) => Promise.all(promises))
-      .then((responses) => responses.map(response => parseInt(response?.headers.get("content-length") || "0")))
+      .then((responses) => Promise.all(
+        responses.map(response => response?.blob().then(blob => blob.size) ?? Promise.resolve(0))
+      ))
       .then((sizes) => sizes.reduce((total, size) => total + size, 0));
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-ui/src/app/shared/service/cache-storage.service.ts` around lines 104
- 111, getCacheSizeInBytes currently sums Content-Length headers which can be
missing; update it to fall back to the actual response body size by calling
response.blob() and using blob.size when headers lack content-length (and handle
null/undefined responses and non-OK responses). In the promise chain starting
from cache() -> cache.keys() -> this.match(key.url) (and inside the responses
array), for each response check response?.headers.get("content-length") first,
parse it if present, otherwise await response.blob() and use blob.size (treat
missing response as size 0), then sum those sizes and return the total; keep
Promise.all for the blob async calls so behavior remains asynchronous.

26-28: Replace @ts-ignore with a safer type narrowing approach.

The @ts-ignore directive is too broad and suppresses all TypeScript errors on the line. The logic guarantees entry is defined here, but TypeScript cannot infer this. Use a more targeted approach.

♻️ Proposed fix
-    if (!stale)
-      //@ts-ignore
-      return entry;
+    if (!stale && entry) {
+      return entry;
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-ui/src/app/shared/service/cache-storage.service.ts` around lines 26
- 28, Remove the broad `@ts-ignore` and narrow the type explicitly: instead of
suppressing TypeScript, check that entry is not null/undefined (e.g. if (!stale
&& entry !== undefined) return entry;) or use a non-null assertion only on the
return (return entry!) if you are certain entry exists; update the code around
the stale and entry variables so TypeScript can infer the defined type without
`@ts-ignore`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@booklore-ui/src/app/shared/service/cache-storage.service.ts`:
- Around line 104-111: getCacheSizeInBytes currently sums Content-Length headers
which can be missing; update it to fall back to the actual response body size by
calling response.blob() and using blob.size when headers lack content-length
(and handle null/undefined responses and non-OK responses). In the promise chain
starting from cache() -> cache.keys() -> this.match(key.url) (and inside the
responses array), for each response check
response?.headers.get("content-length") first, parse it if present, otherwise
await response.blob() and use blob.size (treat missing response as size 0), then
sum those sizes and return the total; keep Promise.all for the blob async calls
so behavior remains asynchronous.
- Around line 26-28: Remove the broad `@ts-ignore` and narrow the type explicitly:
instead of suppressing TypeScript, check that entry is not null/undefined (e.g.
if (!stale && entry !== undefined) return entry;) or use a non-null assertion
only on the return (return entry!) if you are certain entry exists; update the
code around the stale and entry variables so TypeScript can infer the defined
type without `@ts-ignore`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7831c9f7-4bd3-4c59-b3c4-defbdd6ed117

📥 Commits

Reviewing files that changed from the base of the PR and between 454279c and 8fac12f.

📒 Files selected for processing (1)
  • booklore-ui/src/app/shared/service/cache-storage.service.ts

@imajes

imajes commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

@NamLe029 thanks for this -- can you rebase, and also give me a bit of an idea for what this improvement helps with - and what the risks are? usually when adding caching, we need to be thoughtful of areas that might require the latest / non-cached version... so is that afforded throughout?

@NamLe029

Copy link
Copy Markdown
Contributor Author

Oops, apologies for lack of details.

Improvements:
This feature helps reducing traffic to the server, especially for large book files, by caching responses and reusing them later.
Risks:

  • There is a chance that retrieving from cache storage fails. It's already addressed by fallback to fetching the server for response.
  • For possibility of outdated response/cache, every time the cache is requested, its validated by sending a HEAD request to server, checking if its been modified. So, we can be sure that the cache is up-to-date every request.

@NamLe029 NamLe029 force-pushed the web-reader-caching branch from 8fac12f to b551207 Compare March 21, 2026 03:58

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

♻️ Duplicate comments (1)
booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts (1)

101-118: ⚠️ Potential issue | 🟠 Major

Revoke the previous blob URL before overwriting bookData.

When route params change in the same component instance, Lines 217-219 create a new object URL and Line 117 replaces this.bookData. Lines 196-198 only revoke the last URL on destroy, so earlier blob URLs stay pinned for the rest of the session.

Suggested change
         next: ({book, bookSetting, myself, bookData}) => {
+          if (this.bookData?.startsWith('blob:')) {
+            URL.revokeObjectURL(this.bookData);
+          }
+
           const pdfMeta = book;
           const pdfPrefs = bookSetting;
 
           this.pageTitle.setBookPageTitle(pdfMeta);
@@
           this.canPrint = myself.permissions.canDownload || myself.permissions.admin;
           this.page = pdfMeta.pdfProgress?.page || 1;
           this.bookData = bookData;

Also applies to: 196-198, 217-219

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts`
around lines 101 - 118, When assigning a new blob URL to this.bookData (the code
path that calls URL.createObjectURL around lines where createObjectURL is used),
revoke any previously-created URL first to avoid leaking pinned blobs; e.g., if
this.bookData?.blobUrl (or whatever property holds the URL) exists call
URL.revokeObjectURL(prevUrl) before overwriting this.bookData, and ensure
ngOnDestroy still revokes the final URL (the existing revoke logic in
ngOnDestroy should remain but confirm it uses the same property). Update the
assignment sites (the createObjectURL block and wherever this.bookData is
replaced) to perform the revoke and then set the new blob URL.
🧹 Nitpick comments (1)
booklore-api/src/main/java/org/booklore/util/FileUtils.java (1)

86-91: Treat missing files as a warn-and-null path.

This helper logs every missing-path case as an error, while getFileSizeInKb(Path) in the same class already treats that case as a non-fatal miss. Reusing that pattern here will keep transient cache-validation misses from looking like server faults.

♻️ Proposed fix
     public Long getFileLastModified(Path filePath) {
         try {
+            if (!Files.exists(filePath)) {
+                log.warn(FILE_NOT_FOUND_MESSAGE + "{}", filePath.toAbsolutePath());
+                return null;
+            }
             return Files.getLastModifiedTime(filePath).toMillis();
         } catch (IOException e) {
             log.error("Failed to get last modified for path [{}]: {}", filePath, e.getMessage(), e);
             return null;
         }
     }
🤖 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/util/FileUtils.java` around lines 86
- 91, The getFileLastModified(Path) method currently logs all IOExceptions as
errors; change it to mirror getFileSizeInKb(Path) by detecting a missing-file
condition (e.g., NoSuchFileException or Files.notExists(filePath)) and logging
that case at WARN level while returning null, and only log other IOExceptions as
errors; update the log call in getFileLastModified to use log.warn(...) for
missing-paths and keep log.error(...) for unexpected IOExceptions so transient
cache misses aren't treated as server faults.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@booklore-api/src/main/java/org/booklore/service/book/BookService.java`:
- Around line 349-350: The response currently sets CacheControl via
CacheControl.noCache().cachePrivate() in BookService (the
builder.cacheControl(...) call) which allows HTTP caching alongside the new
Cache Storage layer; update the backend to make Cache Storage the only persisted
cache by replacing the CacheControl invocation to use
CacheControl.noStore().cachePrivate() (i.e., change CacheControl.noCache() →
CacheControl.noStore() in the builder.cacheControl(...) call), so the server
sends no-store and avoids creating an unmanaged HTTP cache copy.

In
`@booklore-ui/src/app/features/settings/local-settings/local-settings.component.html`:
- Around line 28-33: The label for the "Enable Cache Storage" control is not
associated with the p-checkbox, so clicking the label won't toggle the checkbox
and screen readers lose the relationship; update the label element to include
for="cache-storage-enabled" and set the p-checkbox prop
inputId="cache-storage-enabled" (the p-checkbox bound to
settings.cacheStorageEnabled and using (onChange)="onSettingChange()") so the
visible label is correctly tied to the checkbox input for accessibility.

In `@booklore-ui/src/app/shared/service/local-settings.service.ts`:
- Around line 15-17: The default LocalSettings currently enables persistent
cache by setting defaultSettings.cacheStorageEnabled to true; change this
default to false so cacheStorageEnabled is opt-in via the Local Settings UI
(update the defaultSettings: LocalSettings object and any code that relies on
its initial value such as consumers of LocalSettings to handle false as
default). Ensure references to defaultSettings and the cacheStorageEnabled
property are updated so the app treats persistence as disabled until the user
explicitly enables it.

---

Duplicate comments:
In `@booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts`:
- Around line 101-118: When assigning a new blob URL to this.bookData (the code
path that calls URL.createObjectURL around lines where createObjectURL is used),
revoke any previously-created URL first to avoid leaking pinned blobs; e.g., if
this.bookData?.blobUrl (or whatever property holds the URL) exists call
URL.revokeObjectURL(prevUrl) before overwriting this.bookData, and ensure
ngOnDestroy still revokes the final URL (the existing revoke logic in
ngOnDestroy should remain but confirm it uses the same property). Update the
assignment sites (the createObjectURL block and wherever this.bookData is
replaced) to perform the revoke and then set the new blob URL.

---

Nitpick comments:
In `@booklore-api/src/main/java/org/booklore/util/FileUtils.java`:
- Around line 86-91: The getFileLastModified(Path) method currently logs all
IOExceptions as errors; change it to mirror getFileSizeInKb(Path) by detecting a
missing-file condition (e.g., NoSuchFileException or Files.notExists(filePath))
and logging that case at WARN level while returning null, and only log other
IOExceptions as errors; update the log call in getFileLastModified to use
log.warn(...) for missing-paths and keep log.error(...) for unexpected
IOExceptions so transient cache misses aren't treated as server faults.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9450181f-547e-4d95-a1c5-da725e1ab3ca

📥 Commits

Reviewing files that changed from the base of the PR and between 8fac12f and b551207.

📒 Files selected for processing (14)
  • booklore-api/src/main/java/org/booklore/config/security/SecurityConfig.java
  • booklore-api/src/main/java/org/booklore/controller/AudiobookReaderController.java
  • booklore-api/src/main/java/org/booklore/controller/BookController.java
  • booklore-api/src/main/java/org/booklore/service/book/BookService.java
  • booklore-api/src/main/java/org/booklore/util/FileUtils.java
  • booklore-ui/src/app/features/book/service/book-file.service.ts
  • booklore-ui/src/app/features/readers/pdf-reader/pdf-reader.component.ts
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.html
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.scss
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.ts
  • booklore-ui/src/app/features/settings/settings.component.html
  • booklore-ui/src/app/features/settings/settings.component.ts
  • booklore-ui/src/app/shared/service/cache-storage.service.ts
  • booklore-ui/src/app/shared/service/local-settings.service.ts
✅ Files skipped from review due to trivial changes (2)
  • booklore-ui/src/app/features/settings/local-settings/local-settings.component.scss
  • booklore-ui/src/app/shared/service/cache-storage.service.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • booklore-api/src/main/java/org/booklore/config/security/SecurityConfig.java
  • booklore-ui/src/app/features/settings/settings.component.html
  • booklore-ui/src/app/features/settings/settings.component.ts
  • booklore-api/src/main/java/org/booklore/controller/BookController.java
  • booklore-ui/src/app/features/book/service/book-file.service.ts
  • booklore-api/src/main/java/org/booklore/controller/AudiobookReaderController.java

Comment thread frontend/src/app/shared/service/local-settings.service.ts
@balazs-szucs

balazs-szucs commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Hi,

apologies for taking this long can you resolve the git conflicts, thanks!

@balazs-szucs

Copy link
Copy Markdown
Contributor

But this look good to me.

@balazs-szucs

balazs-szucs commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Or the that frontend changes necessarily needed? I wonder about that too, most caches don't have to emptied out manually, is there a good reason to expose that option to the user?

@NamLe029 NamLe029 force-pushed the web-reader-caching branch from aa8ed6b to d232c4d Compare April 1, 2026 11:33
@NamLe029 NamLe029 marked this pull request as draft April 1, 2026 11:36
@NamLe029 NamLe029 force-pushed the web-reader-caching branch from bde1913 to e799d02 Compare April 1, 2026 11:47
commit e799d02
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:47:08 2026 +0700

    should be done

commit d232c4d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:49 2026 +0700

    ok done

commit 911d69c
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:15 2026 +0700

    dont break pls

commit 498e996
Author: Nam Le <namle888029@gmail.com>
Date:   Sat Mar 21 11:35:32 2026 +0700

    changes:
    + avoid double caching
    + detach get & validate logic
    + error handling for cache insert

commit a215d11
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 22:01:37 2026 +0700

    fix dup

commit 539b70f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 21:46:43 2026 +0700

    fix
    + leaking Blob resource
    + scss lint
    + extra `}`

commit d1c8cee
Author: NamLe029 <146613205+NamLe029@users.noreply.github.com>
Date:   Fri Mar 20 21:34:14 2026 +0700

    Apply suggestions from code review

    Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

commit ca14572
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 20:25:46 2026 +0700

    wording

commit 558b0f6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:51:28 2026 +0700

    remove dexie

commit 4b07508
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:22:43 2026 +0700

    use observable for pdf bookdata

commit 1c88aff
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:07:14 2026 +0700

    switch to cache storage

commit c766428
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 15:13:42 2026 +0700

    error handling

commit bdbaa8f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 14:16:49 2026 +0700

    cleanup unecessary validation

commit 56e264e
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:50:45 2026 +0700

    apply simpleCacheService

commit 45ef6ae
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:02:44 2026 +0700

    interface for local settings

commit 421305d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:01:20 2026 +0700

    simple cache service for caching request response

commit 1aa81b4
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:47:54 2026 +0700

    local settings service for device-scoped settings

commit 647b4f5
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:35 2026 +0700

    add dexie dependency

commit d8bfed6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:00 2026 +0700

    enable HTTP Caching (Last-Modified) for PDF, EPUB, audiobook content response
@NamLe029 NamLe029 force-pushed the web-reader-caching branch from e799d02 to 8d8ac77 Compare April 1, 2026 11:55
@NamLe029

NamLe029 commented Apr 1, 2026

Copy link
Copy Markdown
Contributor Author

The reason I put a toggle to this feature is that when it's on, features like partial fetching of pdf reader is unavailable, which increases initial load time.

Another thing is that it gonna take up much space if the books are large, and when will the cache be cleaned is unknown, up to the browser. So it's best to let the user decide whether to use this feature or not, or give them the choice to clean up the cache.

Also, creating a whole new tab seems overkill (originally there was more control settings because I used IndexedDB) so I gonna move it to the 'Reader' tab

@balazs-szucs

Copy link
Copy Markdown
Contributor

Sounds reasonable.

@NamLe029 NamLe029 marked this pull request as ready for review April 2, 2026 11:19

@balazs-szucs balazs-szucs left a comment

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.

Thank you for your patience!

@balazs-szucs

Copy link
Copy Markdown
Contributor

Ah apologies I only pulled down the latest, could fix the compilation errors? Afterwards we are good to go.

@NamLe029

NamLe029 commented Apr 3, 2026

Copy link
Copy Markdown
Contributor Author

i forgot to remove the test case for a deleted method. should be good rn

@NamLe029

NamLe029 commented Apr 3, 2026

Copy link
Copy Markdown
Contributor Author

final

@balazs-szucs balazs-szucs merged commit c5c88b4 into grimmory-tools:develop Apr 3, 2026
12 checks passed
zachyale pushed a commit to zachyale/grimmory that referenced this pull request Apr 17, 2026
…rimmory-tools#75)

* Squashed commit of the following:

commit e799d02
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:47:08 2026 +0700

    should be done

commit d232c4d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:49 2026 +0700

    ok done

commit 911d69c
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:15 2026 +0700

    dont break pls

commit 498e996
Author: Nam Le <namle888029@gmail.com>
Date:   Sat Mar 21 11:35:32 2026 +0700

    changes:
    + avoid double caching
    + detach get & validate logic
    + error handling for cache insert

commit a215d11
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 22:01:37 2026 +0700

    fix dup

commit 539b70f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 21:46:43 2026 +0700

    fix
    + leaking Blob resource
    + scss lint
    + extra `}`

commit d1c8cee
Author: NamLe029 <146613205+NamLe029@users.noreply.github.com>
Date:   Fri Mar 20 21:34:14 2026 +0700

    Apply suggestions from code review

    Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

commit ca14572
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 20:25:46 2026 +0700

    wording

commit 558b0f6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:51:28 2026 +0700

    remove dexie

commit 4b07508
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:22:43 2026 +0700

    use observable for pdf bookdata

commit 1c88aff
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:07:14 2026 +0700

    switch to cache storage

commit c766428
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 15:13:42 2026 +0700

    error handling

commit bdbaa8f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 14:16:49 2026 +0700

    cleanup unecessary validation

commit 56e264e
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:50:45 2026 +0700

    apply simpleCacheService

commit 45ef6ae
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:02:44 2026 +0700

    interface for local settings

commit 421305d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:01:20 2026 +0700

    simple cache service for caching request response

commit 1aa81b4
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:47:54 2026 +0700

    local settings service for device-scoped settings

commit 647b4f5
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:35 2026 +0700

    add dexie dependency

commit d8bfed6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:00 2026 +0700

    enable HTTP Caching (Last-Modified) for PDF, EPUB, audiobook content response

* delete unused

* final touch

* move local settings

* test

* remove test case for deleted  method

* lint

---------

Co-authored-by: brios <127139797+balazs-szucs@users.noreply.github.com>
zachyale pushed a commit to zachyale/grimmory that referenced this pull request Apr 17, 2026
…rimmory-tools#75)

* Squashed commit of the following:

commit e799d02
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:47:08 2026 +0700

    should be done

commit d232c4d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:49 2026 +0700

    ok done

commit 911d69c
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:15 2026 +0700

    dont break pls

commit 498e996
Author: Nam Le <namle888029@gmail.com>
Date:   Sat Mar 21 11:35:32 2026 +0700

    changes:
    + avoid double caching
    + detach get & validate logic
    + error handling for cache insert

commit a215d11
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 22:01:37 2026 +0700

    fix dup

commit 539b70f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 21:46:43 2026 +0700

    fix
    + leaking Blob resource
    + scss lint
    + extra `}`

commit d1c8cee
Author: NamLe029 <146613205+NamLe029@users.noreply.github.com>
Date:   Fri Mar 20 21:34:14 2026 +0700

    Apply suggestions from code review

    Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

commit ca14572
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 20:25:46 2026 +0700

    wording

commit 558b0f6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:51:28 2026 +0700

    remove dexie

commit 4b07508
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:22:43 2026 +0700

    use observable for pdf bookdata

commit 1c88aff
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:07:14 2026 +0700

    switch to cache storage

commit c766428
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 15:13:42 2026 +0700

    error handling

commit bdbaa8f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 14:16:49 2026 +0700

    cleanup unecessary validation

commit 56e264e
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:50:45 2026 +0700

    apply simpleCacheService

commit 45ef6ae
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:02:44 2026 +0700

    interface for local settings

commit 421305d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:01:20 2026 +0700

    simple cache service for caching request response

commit 1aa81b4
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:47:54 2026 +0700

    local settings service for device-scoped settings

commit 647b4f5
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:35 2026 +0700

    add dexie dependency

commit d8bfed6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:00 2026 +0700

    enable HTTP Caching (Last-Modified) for PDF, EPUB, audiobook content response

* delete unused

* final touch

* move local settings

* test

* remove test case for deleted  method

* lint

---------

Co-authored-by: brios <127139797+balazs-szucs@users.noreply.github.com>
zachyale pushed a commit that referenced this pull request Apr 17, 2026
…75)

* Squashed commit of the following:

commit e799d02
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:47:08 2026 +0700

    should be done

commit d232c4d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:49 2026 +0700

    ok done

commit 911d69c
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:15 2026 +0700

    dont break pls

commit 498e996
Author: Nam Le <namle888029@gmail.com>
Date:   Sat Mar 21 11:35:32 2026 +0700

    changes:
    + avoid double caching
    + detach get & validate logic
    + error handling for cache insert

commit a215d11
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 22:01:37 2026 +0700

    fix dup

commit 539b70f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 21:46:43 2026 +0700

    fix
    + leaking Blob resource
    + scss lint
    + extra `}`

commit d1c8cee
Author: NamLe029 <146613205+NamLe029@users.noreply.github.com>
Date:   Fri Mar 20 21:34:14 2026 +0700

    Apply suggestions from code review

    Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

commit ca14572
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 20:25:46 2026 +0700

    wording

commit 558b0f6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:51:28 2026 +0700

    remove dexie

commit 4b07508
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:22:43 2026 +0700

    use observable for pdf bookdata

commit 1c88aff
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:07:14 2026 +0700

    switch to cache storage

commit c766428
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 15:13:42 2026 +0700

    error handling

commit bdbaa8f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 14:16:49 2026 +0700

    cleanup unecessary validation

commit 56e264e
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:50:45 2026 +0700

    apply simpleCacheService

commit 45ef6ae
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:02:44 2026 +0700

    interface for local settings

commit 421305d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:01:20 2026 +0700

    simple cache service for caching request response

commit 1aa81b4
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:47:54 2026 +0700

    local settings service for device-scoped settings

commit 647b4f5
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:35 2026 +0700

    add dexie dependency

commit d8bfed6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:00 2026 +0700

    enable HTTP Caching (Last-Modified) for PDF, EPUB, audiobook content response

* delete unused

* final touch

* move local settings

* test

* remove test case for deleted  method

* lint

---------

Co-authored-by: brios <127139797+balazs-szucs@users.noreply.github.com>
zachyale pushed a commit that referenced this pull request Apr 22, 2026
…75)

* Squashed commit of the following:

commit e799d02
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:47:08 2026 +0700

    should be done

commit d232c4d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:49 2026 +0700

    ok done

commit 911d69c
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:15 2026 +0700

    dont break pls

commit 498e996
Author: Nam Le <namle888029@gmail.com>
Date:   Sat Mar 21 11:35:32 2026 +0700

    changes:
    + avoid double caching
    + detach get & validate logic
    + error handling for cache insert

commit a215d11
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 22:01:37 2026 +0700

    fix dup

commit 539b70f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 21:46:43 2026 +0700

    fix
    + leaking Blob resource
    + scss lint
    + extra `}`

commit d1c8cee
Author: NamLe029 <146613205+NamLe029@users.noreply.github.com>
Date:   Fri Mar 20 21:34:14 2026 +0700

    Apply suggestions from code review

    Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

commit ca14572
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 20:25:46 2026 +0700

    wording

commit 558b0f6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:51:28 2026 +0700

    remove dexie

commit 4b07508
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:22:43 2026 +0700

    use observable for pdf bookdata

commit 1c88aff
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:07:14 2026 +0700

    switch to cache storage

commit c766428
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 15:13:42 2026 +0700

    error handling

commit bdbaa8f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 14:16:49 2026 +0700

    cleanup unecessary validation

commit 56e264e
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:50:45 2026 +0700

    apply simpleCacheService

commit 45ef6ae
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:02:44 2026 +0700

    interface for local settings

commit 421305d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:01:20 2026 +0700

    simple cache service for caching request response

commit 1aa81b4
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:47:54 2026 +0700

    local settings service for device-scoped settings

commit 647b4f5
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:35 2026 +0700

    add dexie dependency

commit d8bfed6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:00 2026 +0700

    enable HTTP Caching (Last-Modified) for PDF, EPUB, audiobook content response

* delete unused

* final touch

* move local settings

* test

* remove test case for deleted  method

* lint

---------

Co-authored-by: brios <127139797+balazs-szucs@users.noreply.github.com>
dsmouse pushed a commit to dsmouse/grimmory that referenced this pull request May 11, 2026
…rimmory-tools#75)

* Squashed commit of the following:

commit e799d02
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:47:08 2026 +0700

    should be done

commit d232c4d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:49 2026 +0700

    ok done

commit 911d69c
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:32:15 2026 +0700

    dont break pls

commit 498e996
Author: Nam Le <namle888029@gmail.com>
Date:   Sat Mar 21 11:35:32 2026 +0700

    changes:
    + avoid double caching
    + detach get & validate logic
    + error handling for cache insert

commit a215d11
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 22:01:37 2026 +0700

    fix dup

commit 539b70f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 21:46:43 2026 +0700

    fix
    + leaking Blob resource
    + scss lint
    + extra `}`

commit d1c8cee
Author: NamLe029 <146613205+NamLe029@users.noreply.github.com>
Date:   Fri Mar 20 21:34:14 2026 +0700

    Apply suggestions from code review

    Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

commit ca14572
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 20:25:46 2026 +0700

    wording

commit 558b0f6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:51:28 2026 +0700

    remove dexie

commit 4b07508
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 19:22:43 2026 +0700

    use observable for pdf bookdata

commit 1c88aff
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:07:14 2026 +0700

    switch to cache storage

commit c766428
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 15:13:42 2026 +0700

    error handling

commit bdbaa8f
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 14:16:49 2026 +0700

    cleanup unecessary validation

commit 56e264e
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:50:45 2026 +0700

    apply simpleCacheService

commit 45ef6ae
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:02:44 2026 +0700

    interface for local settings

commit 421305d
Author: Nam Le <namle888029@gmail.com>
Date:   Wed Apr 1 18:01:20 2026 +0700

    simple cache service for caching request response

commit 1aa81b4
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:47:54 2026 +0700

    local settings service for device-scoped settings

commit 647b4f5
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:35 2026 +0700

    add dexie dependency

commit d8bfed6
Author: Nam Le <namle888029@gmail.com>
Date:   Fri Mar 20 12:46:00 2026 +0700

    enable HTTP Caching (Last-Modified) for PDF, EPUB, audiobook content response

* delete unused

* final touch

* move local settings

* test

* remove test case for deleted  method

* lint

---------

Co-authored-by: brios <127139797+balazs-szucs@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants