Skip to content

WEBDEV-8991: Keep browse loan alive during read aloud - #1598

Open
nsharma123 wants to merge 1 commit into
masterfrom
WEBDEV-8991-readaloud-keeps-loan-active
Open

nsharma123 wants to merge 1 commit into
masterfrom
WEBDEV-8991-readaloud-keeps-loan-active

Conversation

@nsharma123

@nsharma123 nsharma123 commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A patron reported their book being auto-returned "due to inactivity" after an hour while actively listening to read aloud — WEBDEV-8991.

ia-book-actions decides a browse loan is still in use from the BookReader:userAction event. That event is triggered in exactly two places today:

  • BookReader.prototype.updateFirstIndex() — the displayed index changed
  • Navbar.js — a navbar control was clicked

Read aloud produces neither reliably. It only turns a page incidentally, via maybeFlipToIndex(), and in 2up the engine reads both visible pages before it flips — so a single spread can easily outlast the 15 minute window the renewal allows (pageChangedInLast). The renewal check at minute 49 then sees no recorded activity and the loan lapses mid-playback.

This is worse than a late renewal. While the loan is still held, Lending::do_renew takes the $true_renewal branch and skips the availability/policy gate. Once it lapses, the next attempt has to pass that gate — so the book can be lost to another patron outright.

Fix

Drive the activity signal off playback progress rather than the incidental page flip:

  • Per chunk — beforeChunkPlay() emits userAction, so every paragraph spoken counts.
  • On resume — resuming picks up inside the current chunk, so it would otherwise wait for the next chunk boundary to register.
  • Throttled to 60s — chunks are paragraphs; short ones would otherwise signal every few seconds, and each event costs an IndexedDB read+write on the lending side. throttle(fn, threshold, false) fires leading-edge, so the first chunk signals immediately.

No change is needed in ia-book-actions — it already maps userAction onto its page-changed path for renewal purposes.

Testing

  • Syntax verified; lint rules reviewed by hand (no max-len; indent/semi/no-unused all satisfied).
  • Verified the class-field ordering is safe: BookReaderPlugin's constructor does not call setup() — BookReader.js constructs the plugin, then calls setup() as a separate statement — so signalUserIsActive is initialized before the resume handler is registered.
  • ttsEngine.events is a jQuery emitter and AbstractTTSEngine already registers a class-field arrow the same way, so the idiom matches existing code.

I was not able to run the jest suite locally (no node_modules in my checkout); relying on CI here. There is currently no plugin.tts.test.js — happy to add one covering the emit + throttle if reviewers want it in this PR.

Out of scope

The "Are you still there?" warning modal is acknowledge-only — dismissWarningModal() closes it without renewing, and sets a flag that suppresses every later warning. That lives in ia-book-actions and likely deserves its own ticket.

🤖 Generated with Claude Code

A patron reported their book being auto-returned "due to inactivity"
after an hour while actively listening to read aloud.

ia-book-actions only learns a patron is still there from `userAction`,
which is otherwise triggered solely by an index change (see
BookReader.prototype.updateFirstIndex) or a navbar click. Read aloud
produces neither reliably: in 2up the engine reads both visible pages
before it flips, so a spread can easily outlast the 15 minute window the
loan renewal allows, and the loan lapses mid-playback. Once it lapses,
renewal has to pass an availability check it would have skipped while the
loan was still held, so the book can be lost to another patron entirely.

Drive the signal off playback progress instead:

- emit `userAction` per chunk in beforeChunkPlay, throttled to once a
  minute since chunks are paragraphs and short ones would otherwise
  signal every few seconds for no benefit
- emit on resume as well, which picks up inside the current chunk and
  would otherwise wait for the next chunk boundary to register

No change needed on the ia-book-actions side, which already treats
`userAction` as a page change for renewal purposes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.81%. Comparing base (882adbc) to head (559fb68).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1598   +/-   ##
=======================================
  Coverage   63.81%   63.81%           
=======================================
  Files          69       69           
  Lines        6256     6256           
  Branches     1391     1391           
=======================================
  Hits         3992     3992           
  Misses       2225     2225           
  Partials       39       39           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant