Skip to content

fix(phone/mobile): enable pan mode and disable swipe-to-navigate and tap-to-turn-page zones on mobile devices#742

Merged
zachyale merged 3 commits into
grimmory-tools:developfrom
balazs-szucs:morepdfstuff
Apr 22, 2026
Merged

fix(phone/mobile): enable pan mode and disable swipe-to-navigate and tap-to-turn-page zones on mobile devices#742
zachyale merged 3 commits into
grimmory-tools:developfrom
balazs-szucs:morepdfstuff

Conversation

@balazs-szucs

@balazs-szucs balazs-szucs commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Description

Linked Issue: Fixes #

Changes

This pull request improves the mobile usability of the PdfReaderComponent by adjusting gesture handling and navigation to prevent accidental page turns and interactions on phones. The main changes ensure that swipe and tap gestures behave differently on phones, prioritizing document selection and navigation over page turning.

Mobile gesture and navigation improvements:

  • In ngOnInit, the component now detects if the device is a phone (screen width < 768px) and automatically enables pan mode for a better touch experience.
  • During PDF viewer initialization, pan mode is activated if the device is detected as a phone, ensuring smoother navigation and preventing accidental page turns.

Gesture handling changes:

  • Swipe-to-change-page gestures are now disabled on phones and when pan mode is active, reducing accidental page turns during document selection or navigation.
  • Tap-to-change-page zones (left/right screen tap for previous/next page) are disabled on phones; taps only toggle the UI chrome, further preventing unintended navigation.

Summary by CodeRabbit

  • New Features

    • Automatic pan mode on narrow screens (phones) for smoother PDF navigation; remembers user pan preference when resizing.
  • Bug Fixes

    • Reduced accidental page turns by disabling swipe and tap-to-navigate zones when pan mode is active or on phones; taps on phones now primarily toggle viewer chrome.

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Added an isPhone signal (based on window.innerWidth < 768) with a resize listener and cleanup; centralized pan-mode side effects via applyPanMode(active), synced pan state with the embedded viewer during initialization, and adjusted touch navigation to suppress swipes and disable left/right tap zones on phones.

Changes

Cohort / File(s) Summary
Mobile PDF Reader & embed sync
frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
Added isPhone signal and resize listener; introduced applyPanMode(active) to set isPanActive, clear annotation/embed tools when enabling pan, and sync EmbedPDF pan after viewer init; updated toggleAnnotationTool/togglePanMode to use applyPanMode while tracking user preference; during initialization, enable EmbedPDF pan if isPanActive(); touch navigation now suppresses swipe when isPanActive() or isPhone() and disables left/right tap-zone page changes on phones.

Sequence Diagram(s)

sequenceDiagram
  participant User as "User (touch/resize)"
  participant Window as "Window (resize)"
  participant PDFComp as "PDFReaderComponent"
  participant Embed as "EmbedPDF Viewer"

  User->>PDFComp: touch (tap/swipe)
  alt isPhone() or isPanActive()
    PDFComp-->>User: handle tap -> toggle chrome (no page turn)
    PDFComp--x Embed: suppress swipe/page-change
  else
    PDFComp->>Embed: forward swipe -> page turn
  end

  Window->>PDFComp: resize event
  PDFComp->>PDFComp: update isPhone signal
  alt transitioned to phone
    PDFComp->>PDFComp: applyPanMode(true)
    PDFComp->>Embed: set pan mode = true (after init)
  else transitioned to non-phone
    PDFComp->>PDFComp: applyPanMode(userPreference)
    PDFComp->>Embed: set pan mode accordingly
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hop through code with careful paws,

Phones get pan and gentler laws,
Taps stay quiet, swipes kept tame,
Embed and viewer learn my name,
A soft-scroll world—hurray, applause! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title follows conventional commit format with 'fix' type, relevant scope '(phone/mobile)', and clear description of changes.
Description check ✅ Passed The PR description includes both required sections (Description and Changes) with detailed explanations of mobile usability improvements and specific gesture handling changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts (1)

1202-1217: ⚠️ Potential issue | 🟡 Minor

Add a movement threshold before toggling chrome on phone taps.

The comment says “minimal movement,” but the code only checks duration and touchMoveCount. A quick pan/selection drag with fewer than 3 move events can now toggle chrome anywhere on phones. Gate the tap path on actual deltaX/deltaY distance too.

Suggested fix
     // Tap detection: short duration, minimal movement
-    if (elapsed < this.TAP_MAX_DURATION && this.touchMoveCount < 3) {
+    const tapMaxMovement = 10;
+    const isTap =
+      elapsed < this.TAP_MAX_DURATION &&
+      this.touchMoveCount < 3 &&
+      Math.abs(deltaX) < tapMaxMovement &&
+      Math.abs(deltaY) < tapMaxMovement;
+
+    if (isTap) {
       const screenWidth = window.innerWidth;
       const tapX = this.touchStartX;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts` around
lines 1202 - 1217, The tap detection currently only checks elapsed time
(TAP_MAX_DURATION) and touchMoveCount but not actual movement, so on phones a
short pan with few move events can still trigger the chrome toggle; add a
movement threshold constant (e.g., TAP_MOVE_THRESHOLD) and compute deltaX/deltaY
from touchStartX/touchStartY and the current touch end position, then require
Math.abs(deltaX) and Math.abs(deltaY) to be below that threshold before taking
the tap path. Update the existing tap branch (the block using TAP_MAX_DURATION,
touchMoveCount, touchStartX and the phone/zone checks) to include this movement
check so that the center/phone toggle chrome action only runs when movement is
within the threshold.
🤖 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/pdf-reader/pdf-reader.component.ts`:
- Around line 193-195: The component computes isPhone once and sets pan state
via this.isPanActive.set, but viewport changes can desync it from later
touch-time checks (and this.isMobile); add a shared reactive flag (e.g., a
component property like isPhoneSignal or getIsPhone()) that is updated on window
'resize' and 'orientationchange', call setPanMode() whenever that computed
phone-state changes to keep EmbedPDF pan mode and touch-time logic in sync, and
register the listeners with DestroyRef.onDestroy(...) for cleanup instead of
manual teardown fields; update all references that read the one-time isPhone or
this.isMobile to use the shared signal/helper so both isPanActive.set and touch
handlers see the same current value.

---

Outside diff comments:
In `@frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts`:
- Around line 1202-1217: The tap detection currently only checks elapsed time
(TAP_MAX_DURATION) and touchMoveCount but not actual movement, so on phones a
short pan with few move events can still trigger the chrome toggle; add a
movement threshold constant (e.g., TAP_MOVE_THRESHOLD) and compute deltaX/deltaY
from touchStartX/touchStartY and the current touch end position, then require
Math.abs(deltaX) and Math.abs(deltaY) to be below that threshold before taking
the tap path. Update the existing tap branch (the block using TAP_MAX_DURATION,
touchMoveCount, touchStartX and the phone/zone checks) to include this movement
check so that the center/phone toggle chrome action only runs when movement is
within the threshold.
🪄 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: 1c946552-447e-4211-9701-8af8028d18ef

📥 Commits

Reviewing files that changed from the base of the PR and between f8cdcd5 and d1cb01f.

📒 Files selected for processing (1)
  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Test Suite / Frontend Tests
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Frontend Lint Threshold Check
🧰 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/pdf-reader/pdf-reader.component.ts
frontend/src/app/**/*.component.ts

📄 CodeRabbit inference engine (AGENTS.md)

Keep Angular code on standalone components. Do not add NgModules

Files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.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/pdf-reader/pdf-reader.component.ts
🧠 Learnings (3)
📚 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/pdf-reader/pdf-reader.component.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/pdf-reader/pdf-reader.component.ts
📚 Learning: 2026-04-11T03:55:57.229Z
Learnt from: zachyale
Repo: grimmory-tools/grimmory PR: 439
File: frontend/src/app/features/series-browser/components/series-browser/series-browser.component.ts:178-196
Timestamp: 2026-04-11T03:55:57.229Z
Learning: In this Angular frontend (frontend/src/app/), prefer the team’s reactive i18n “signal/computed” pattern: (1) For individual reactive translated strings, use `translateSignal()` from `jsverse/transloco`. (2) For option/label arrays that must update on language switch, create a single `activeLang` signal with `toSignal(t.langChanges$, { initialValue: t.getActiveLang() })`, then derive the arrays as `computed()` signals that read `activeLang()`. This should avoid manual `langChanges$` subscriptions and any `ngOnDestroy` subscription cleanup; prefer this over subscribing in `ngOnInit` when implementing reactive localization.

Applied to files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
🔇 Additional comments (1)
frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts (1)

440-442: Good placement for applying the pan-mode default.

This runs after the embedded book viewer is initialized, so the pan plugin is available before setPanMode(true) is called.

Comment thread frontend/src/app/features/readers/pdf-reader/pdf-reader.component.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

🤖 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/pdf-reader/pdf-reader.component.ts`:
- Around line 195-205: syncPhoneMode currently overwrites user pan state on
every resize; change it to only act on breakpoint transitions and route all pan
changes through the existing togglePanMode/shared helper so manual tool state
isn't clobbered. Specifically: replace the direct writes to isPanActive inside
syncPhoneMode with logic that detects a change in isPhone (compare previous
value) and, on transitions, apply embedPdfBook.setPanMode only via the shared
togglePanMode (or a new helper) so active-tool clearing is preserved; introduce
a separate userPanActive vs phoneForcedPan flag so entering phone mode sets
phoneForcedPan=true and enables pan, and leaving phone mode clears
phoneForcedPan and restores userPanActive instead of overriding it; keep the
same event listener and destroyRef.onDestroy remove call.
🪄 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: a6fcfaed-4965-4ae6-b091-cb2acfcc218a

📥 Commits

Reviewing files that changed from the base of the PR and between d1cb01f and 16a69c6.

📒 Files selected for processing (1)
  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Test Suite / Frontend Tests
  • 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/pdf-reader/pdf-reader.component.ts
frontend/src/app/**/*.component.ts

📄 CodeRabbit inference engine (AGENTS.md)

Keep Angular code on standalone components. Do not add NgModules

Files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.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/pdf-reader/pdf-reader.component.ts
🧠 Learnings (3)
📚 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/pdf-reader/pdf-reader.component.ts
📚 Learning: 2026-04-05T21:16:01.715Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 385
File: frontend/src/app/app.component.ts:55-56
Timestamp: 2026-04-05T21:16:01.715Z
Learning: When reviewing code in the Grimmory frontend (Angular), prefer modern Angular patterns. Specifically: (1) Prefer `DestroyRef` with `takeUntilDestroyed(destroyRef)` for teardown in Angular v16+ instead of manually tracking `Subscription` arrays and calling `unsubscribe()` in `ngOnDestroy()`. (2) Prefer `inject()` for dependency injection over constructor injection where appropriate. (3) Prefer Angular signals (e.g., `signal`, `computed`) over `BehaviorSubject`/`Observable` for state where signals/computed values fit the use case. Flag older patterns when they can be replaced with these modern equivalents without changing behavior.

Applied to files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
📚 Learning: 2026-04-11T03:55:57.229Z
Learnt from: zachyale
Repo: grimmory-tools/grimmory PR: 439
File: frontend/src/app/features/series-browser/components/series-browser/series-browser.component.ts:178-196
Timestamp: 2026-04-11T03:55:57.229Z
Learning: In this Angular frontend (frontend/src/app/), prefer the team’s reactive i18n “signal/computed” pattern: (1) For individual reactive translated strings, use `translateSignal()` from `jsverse/transloco`. (2) For option/label arrays that must update on language switch, create a single `activeLang` signal with `toSignal(t.langChanges$, { initialValue: t.getActiveLang() })`, then derive the arrays as `computed()` signals that read `activeLang()`. This should avoid manual `langChanges$` subscriptions and any `ngOnDestroy` subscription cleanup; prefer this over subscribing in `ngOnInit` when implementing reactive localization.

Applied to files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
🔇 Additional comments (4)
frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts (4)

73-73: LGTM — phone mode is modelled reactively.

This fits the component’s existing signal-based state and keeps template/touch logic using one source of truth.


452-454: LGTM — pan mode is applied after viewer initialization.

This covers the case where phone mode set isPanActive before EmbedPDF’s pan capability was ready.


1198-1202: LGTM — swipe navigation is suppressed in the right modes.

Using the shared isPhone()/isPanActive() state here keeps gesture behavior aligned with pan/mobile mode.


1219-1228: LGTM — phone taps no longer trigger page-zone navigation.

The fallback to chrome toggling preserves the expected tap behavior without accidental page turns.

@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)
frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts (2)

461-464: Consider routing the deferred pan through applyPanMode for consistency.

Not a functional issue — isPanActive and activeAnnotationTool were already reconciled by syncPhoneMode() during ngOnInit, so only setPanMode needs to flush here. However, calling applyPanMode(this.isPanActive()) would keep all pan side-effect logic in one place and defensively clear any annotation tool that may have been selected between ngOnInit and initBookViewer (e.g. via a toolbar click before the viewer finished initializing).

♻️ Suggested change
-      if (this.isPanActive()) {
-        this.embedPdfBook.setPanMode(true);
-      }
+      if (this.isPanActive()) {
+        this.applyPanMode(true);
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts` around
lines 461 - 464, Replace the direct call to embedPdfBook.setPanMode(true) in
initBookViewer with a call to applyPanMode(this.isPanActive()) so all
pan-related side effects (including clearing activeAnnotationTool) are
centralized; locate the block using isPanActive(), embedPdfBook.setPanMode, and
initBookViewer and change it to call applyPanMode instead, ensuring
syncPhoneMode/ngOnInit reconciliation is preserved and any annotation tool
selected after ngOnInit gets cleared by applyPanMode.

1238-1256: Optional: hoist the phone check above the tap-zone branches.

Functionally correct, but this.isPhone() is evaluated twice and the "center zone (or any zone on phone)" comment is doing heavy lifting. A short-circuit keeps intent obvious and avoids re-reading the signal.

♻️ Suggested change
-      this.ngZone.run(() => {
-        // On phones, disable tap-to-change-page zones to prevent accidental navigation.
-        // Taps will only toggle the chrome.
-        if (!this.isPhone() && tapX < screenWidth * this.TAP_ZONE_RATIO) {
-          // Left zone: previous page
-          this.goToPreviousPage();
-        } else if (!this.isPhone() && tapX > screenWidth * (1 - this.TAP_ZONE_RATIO)) {
-          // Right zone: next page
-          this.goToNextPage();
-        } else {
-          // Center zone (or any zone on phone): toggle chrome
-          if (this.headerVisible() || this.footerVisible()) {
+      this.ngZone.run(() => {
+        const toggleChrome = () => {
+          if (this.headerVisible() || this.footerVisible()) {
             this.hideChrome();
           } else {
             this.showChrome();
             this.startChromeAutoHide();
           }
+        };
+
+        // On phones, tap zones are disabled — taps only toggle chrome.
+        if (this.isPhone()) {
+          toggleChrome();
+        } else if (tapX < screenWidth * this.TAP_ZONE_RATIO) {
+          this.goToPreviousPage();
+        } else if (tapX > screenWidth * (1 - this.TAP_ZONE_RATIO)) {
+          this.goToNextPage();
+        } else {
+          toggleChrome();
         }
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts` around
lines 1238 - 1256, The tap handler currently calls this.isPhone() twice and
mixes phone logic into the tap-zone branches; refactor the ngZone.run block to
check this.isPhone() once at the top and short-circuit to the "phone" behavior
(always toggle chrome) if true, otherwise evaluate tapX against screenWidth *
this.TAP_ZONE_RATIO to call goToPreviousPage() or goToNextPage() or
toggle/show/hide chrome and call startChromeAutoHide() as appropriate; update
the block containing ngZone.run, isPhone(), TAP_ZONE_RATIO, goToPreviousPage,
goToNextPage, headerVisible, footerVisible, hideChrome, showChrome, and
startChromeAutoHide so the intent is explicit and this.isPhone() is not
re-evaluated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts`:
- Around line 461-464: Replace the direct call to embedPdfBook.setPanMode(true)
in initBookViewer with a call to applyPanMode(this.isPanActive()) so all
pan-related side effects (including clearing activeAnnotationTool) are
centralized; locate the block using isPanActive(), embedPdfBook.setPanMode, and
initBookViewer and change it to call applyPanMode instead, ensuring
syncPhoneMode/ngOnInit reconciliation is preserved and any annotation tool
selected after ngOnInit gets cleared by applyPanMode.
- Around line 1238-1256: The tap handler currently calls this.isPhone() twice
and mixes phone logic into the tap-zone branches; refactor the ngZone.run block
to check this.isPhone() once at the top and short-circuit to the "phone"
behavior (always toggle chrome) if true, otherwise evaluate tapX against
screenWidth * this.TAP_ZONE_RATIO to call goToPreviousPage() or goToNextPage()
or toggle/show/hide chrome and call startChromeAutoHide() as appropriate; update
the block containing ngZone.run, isPhone(), TAP_ZONE_RATIO, goToPreviousPage,
goToNextPage, headerVisible, footerVisible, hideChrome, showChrome, and
startChromeAutoHide so the intent is explicit and this.isPhone() is not
re-evaluated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbf3e924-c936-4bf6-9f56-f01be4ef4bfe

📥 Commits

Reviewing files that changed from the base of the PR and between 16a69c6 and 4b94f8e.

📒 Files selected for processing (1)
  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Test Suite / Frontend Tests
  • 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/pdf-reader/pdf-reader.component.ts
frontend/src/app/**/*.component.ts

📄 CodeRabbit inference engine (AGENTS.md)

Keep Angular code on standalone components. Do not add NgModules

Files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.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/pdf-reader/pdf-reader.component.ts
🧠 Learnings (3)
📚 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/pdf-reader/pdf-reader.component.ts
📚 Learning: 2026-04-05T21:16:01.715Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 385
File: frontend/src/app/app.component.ts:55-56
Timestamp: 2026-04-05T21:16:01.715Z
Learning: When reviewing code in the Grimmory frontend (Angular), prefer modern Angular patterns. Specifically: (1) Prefer `DestroyRef` with `takeUntilDestroyed(destroyRef)` for teardown in Angular v16+ instead of manually tracking `Subscription` arrays and calling `unsubscribe()` in `ngOnDestroy()`. (2) Prefer `inject()` for dependency injection over constructor injection where appropriate. (3) Prefer Angular signals (e.g., `signal`, `computed`) over `BehaviorSubject`/`Observable` for state where signals/computed values fit the use case. Flag older patterns when they can be replaced with these modern equivalents without changing behavior.

Applied to files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
📚 Learning: 2026-04-11T03:55:57.229Z
Learnt from: zachyale
Repo: grimmory-tools/grimmory PR: 439
File: frontend/src/app/features/series-browser/components/series-browser/series-browser.component.ts:178-196
Timestamp: 2026-04-11T03:55:57.229Z
Learning: In this Angular frontend (frontend/src/app/), prefer the team’s reactive i18n “signal/computed” pattern: (1) For individual reactive translated strings, use `translateSignal()` from `jsverse/transloco`. (2) For option/label arrays that must update on language switch, create a single `activeLang` signal with `toSignal(t.langChanges$, { initialValue: t.getActiveLang() })`, then derive the arrays as `computed()` signals that read `activeLang()`. This should avoid manual `langChanges$` subscriptions and any `ngOnDestroy` subscription cleanup; prefer this over subscribing in `ngOnInit` when implementing reactive localization.

Applied to files:

  • frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts
🔇 Additional comments (2)
frontend/src/app/features/readers/pdf-reader/pdf-reader.component.ts (2)

196-214: LGTM — resize-driven phone sync is clean.

The lastIsPhone transition guard prevents clobbering user pan state on incidental resizes, syncPhoneMode() runs once on init to set initial state, and teardown is wired via destroyRef.onDestroy rather than a manual cleanup field. This correctly addresses the prior review feedback.


717-745: LGTM — applyPanMode centralizes pan side effects well.

applyPanMode consolidates signal updates, annotation-tool clearing, and EmbedPDF sync in one place, and both toggleAnnotationTool and togglePanMode now correctly track userPanPreferred so leaving phone width restores the user's explicit choice rather than deriving it from isPhone.

@zachyale zachyale merged commit 45eef2f into grimmory-tools:develop Apr 22, 2026
16 checks passed
zachyale pushed a commit that referenced this pull request Apr 22, 2026
dsmouse pushed a commit to dsmouse/grimmory that referenced this pull request May 11, 2026
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.

2 participants