Skip to content

Forward system back presses into the paywall as back_button_input - #442

Open
ianrumac wants to merge 8 commits into
developfrom
claude/reroute-back-button-input
Open

Forward system back presses into the paywall as back_button_input#442
ianrumac wants to merge 8 commits into
developfrom
claude/reroute-back-button-input

Conversation

@ianrumac

@ianrumac ianrumac commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Changes in this pull request

  • Breaking change (team-approved): system back presses are now forwarded into the paywall as the paywall-js back_button_input message instead of dismissing it directly. The paywall navigates its flow back one page when possible; at the root (or on a single-page paywall) it posts the existing close message, which dismisses through the same path as a manual close (PaywallResult.Declined / PaywallCloseReason.ManualClose) — so single-page paywalls dismiss the same as before from the app's perspective, with surveys, delegate callbacks, and analytics unchanged.
  • reroute_back_button now only controls the app callback. When ENABLED and PaywallOptions.onBackPressed is set, the callback gets first refusal; returning true consumes the press, returning false (or no callback) forwards it. When DISABLED/unset the press is forwarded directly. Extracted into pure isBackPressConsumedByApp().
  • Routed through the standard message pipeline. PaywallView.backButtonPressed() calls webView.messageHandler.handle(PaywallMessage.BackButtonPressed), which sends back_button_input into the webview via the same pass()/accept64 machinery as paywall_open/paywall_close — reusing main-thread dispatch, error logging, and encoding rather than a hand-rolled evaluate.
  • No capability probe. Paywalls built on runtimes that predate the back_button_input consumer will ignore the press — noted in the CHANGELOG; paywalls should be re-published on a current runtime. The paywall-side consumer (navigate back / close at root) ships with superwall/paywall-next#3652, which must deploy before this releases.
  • Native dismissal remains only for the edge where no PaywallView is attached to the activity.

Checklist

  • All unit tests pass.
  • All UI tests pass.
  • Demo project builds and runs.
  • I added/updated tests or detailed why my change isn't tested. (BackPressBehaviorTest covers the app-callback matrix — disabled/unset never consult the callback, enabled+consumed, enabled+declined; PaywallViewTest verifies the press reaches the message handler; PaywallMessageHandlerTest verifies BackButtonPressed passes back_button_input into the webview via accept64. The OnBackPressedCallback wiring is thin glue over the tested functions and there is no existing activity test harness.)
  • I added an entry to the CHANGELOG.md for any breaking changes, enhancements, or bug fixes.
  • I have run ktlint in the main directory and fixed any issues. (Not runnable in this environment — please rely on CI; code follows surrounding style.)
  • I have updated the SDK documentation as well as the online docs.
  • I have reviewed the contributing guide

🤖 Generated with Claude Code

https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m

@ianrumac ianrumac changed the title Forward re-routed back presses into the paywall as back_button_input Forward system back presses into the paywall as back_button_input Jul 27, 2026
@ianrumac ianrumac changed the title Forward system back presses into the paywall as back_button_input Forward re-routed back presses into the paywall as back_button_input Jul 27, 2026
@ianrumac
ianrumac marked this pull request as draft July 27, 2026 12:55
@ianrumac ianrumac changed the title Forward re-routed back presses into the paywall as back_button_input Forward system back presses into the paywall as back_button_input Jul 28, 2026
@ianrumac
ianrumac marked this pull request as ready for review July 28, 2026 11:39
claude added 6 commits July 28, 2026 14:44
…input

When reroute_back_button is enabled in Paywall settings, a system back
press previously did nothing unless the host app set the
PaywallOptions.onBackPressed callback — otherwise the paywall dismissed
as if the setting were off.

Back presses the app callback doesn't consume are now injected into the
webview as the paywall-js back_button_input message. The paywall either
navigates its flow back one page or posts the existing close message,
which dismisses through the standard manual-close path (Declined /
ManualClose), so back-at-root behaves exactly like a native dismissal.

The press/consume decision is extracted into backPressBehavior() and
unit tested; the JS bridge payload is covered in PaywallViewTest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m
…lity probe

Multi-page flow paywalls were closing entirely on system back instead of
navigating back a page. Forwarding is now the default for every paywall,
not just those with reroute_back_button enabled — that setting now only
controls whether the PaywallOptions.onBackPressed app callback gets
first refusal.

Because published paywalls pin their built runtime, blindly forwarding
would strand users on paywalls whose runtime lacks the back_button_input
consumer. The SDK therefore probes the webview for
window.paywall.supportsBackButtonInput first; when absent (older
runtime, still loading, crashed webview) it falls back to the native
dismiss, preserving today's behavior exactly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m
…is ENABLED

Forwarding by default changes implied behavior for existing multi-page
paywalls, so gate it behind the existing per-paywall dashboard setting
while the default story is decided (possibly a new setting). DISABLED
and unset paywalls dismiss on back exactly as before. The capability
probe stays: even ENABLED paywalls on older runtimes keep the native
dismiss.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m
Team decision: this ships as an accepted breaking change. Every system
back press the app callback doesn't consume is forwarded into the
paywall as back_button_input — the paywall navigates its flow back one
page or posts close (Declined/ManualClose) at the root. The
reroute_back_button setting now only controls whether the
PaywallOptions.onBackPressed callback is consulted first. Native
dismissal remains only when no PaywallView is attached to the activity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m
The message's arrival is the signal — only presses are ever sent, so
the boolean carried no information. Matches the simplified paywall-js
schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m
The Unreleased section this entry was written under became 2.7.23 on
develop; the change ships with 2.8.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m
@ianrumac
ianrumac force-pushed the claude/reroute-back-button-input branch from 9e8adf9 to 8cc6d5f Compare July 28, 2026 14:46
claude and others added 2 commits July 28, 2026 16:51
Adds PaywallMessage.BackButtonPressed and handles it via the standard
pass()/accept64 pipeline instead of a hand-rolled evaluate in
PaywallView, reusing the established main-thread dispatch, error
logging, and encoding. Removes BackButtonInputEvent and reverts the
gameControllerJson rename it motivated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m
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.

2 participants