Skip to content

fix(web): replay an audioEnable that arrives before the publication is open - #231

Merged
ddfreiling merged 2 commits into
mainfrom
fix/web-defer-audio-enable
Sep 16, 2026
Merged

ddfreiling merged 2 commits into
mainfrom
fix/web-defer-audio-enable

Conversation

@ddfreiling

Copy link
Copy Markdown
Member

Problem

On web, a text publication is only opened once ReadiumWebView mounts and supplies the #container element — FlutterReadiumWeb.openPublication calls the JS side only for pure audiobooks, which need no DOM container.

So an audioEnable issued straight after openPublication arrives before there is anything to enable. It fell through every branch, logged a warning, and returned. Nothing retried it, so the book stayed at TimebasedState.none forever.

From a LYT4 Web E2E run:

21:31:40.475  audioEnable
21:31:40.476  WARN audioEnable: no audiobook or Media Overlay content detected
21:31:41.101  Guided Navigation detected

626 ms too early.

Fix

Remember that call and replay it at the end of openPublication.

Why it still resolves immediately

This is required, not a convenience. The host app awaits audioEnable inside its open flow, and only routes to the page hosting ReadiumWebView once that flow succeeds. Blocking here would leave the open waiting on a reader that is waiting on the open — a deadlock.

Consequences handled

Concern Handling
A deferred failure can no longer be thrown to its caller The replay is awaited inside openPublication, so failures travel the existing open-failure path
A stale request running against the next publication Cleared in closePublication
Running twice on a reader remount or hot restart Consumed before replaying
Intercepting the pure-audiobook path The guard requires both no publication and no audio navigator

No play() is recorded alongside it, because audioEnable resumes playback itself via _seekAudioAndResume(..., true).

Follow-up (not in this PR)

This is a symptom of a deeper asymmetry: on iOS and Android the plugin owns the publication lifecycle, while on web the widget does. That is why the web platform API cannot honour its own contract, and why play/pause/stop are fire-and-forget voids there. Worth tracking separately.

Verification

  • bin/typecheck clean
  • bin/format clean
  • bin/unit_tests: 18 suites, 283 tests pass (plus iOS unit tests), including 3 new ones covering defer, replay, and clear-on-close
  • bin/build_js succeeds

Not verified: a web end-to-end run against this branch. That happens when LYT4 picks the release up.

🤖 Generated with Claude Code

ddfreiling and others added 2 commits September 17, 2026 00:27
…s open

On web a text publication is only opened once `ReadiumWebView` mounts and
supplies the `#container` element — `FlutterReadiumWeb.openPublication` calls
the JS side only for pure audiobooks. An `audioEnable` issued straight after
`openPublication` therefore found no publication, logged "no audiobook or Media
Overlay content detected" and returned, and nothing ever retried it:

  21:31:40.475  audioEnable
  21:31:40.476  WARN audioEnable: no audiobook or Media Overlay content detected
  21:31:41.101  Guided Navigation detected

Remember such a call and replay it at the end of `openPublication`.

It still resolves immediately rather than waiting for the publication. That is
required, not merely convenient: the host app awaits `audioEnable` before it
routes to the page hosting the reader view, so blocking would leave the open
waiting on a reader that is waiting on the open.

The replay is awaited inside `openPublication`, so a failure travels the
existing open-failure path instead of being lost — the deferred caller can no
longer be thrown to. The entry is consumed before replaying (a reader remount or
hot restart cannot run it twice) and cleared by `closePublication`.

The guard requires both no publication and no audio navigator, so it cannot
intercept the pure-audiobook path, where the navigator exists on its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ddfreiling

Copy link
Copy Markdown
Member Author

Necessary fix to get out, I take full responsibility and merge this. Deeper root-cause refactor of how web opens publications will be initiated later.

@ddfreiling
ddfreiling merged commit f03c915 into main Sep 16, 2026
14 checks passed
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