Forward system back presses into the paywall as back_button_input - #442
Open
ianrumac wants to merge 8 commits into
Open
Forward system back presses into the paywall as back_button_input#442ianrumac wants to merge 8 commits into
back_button_input#442ianrumac wants to merge 8 commits into
Conversation
back_button_inputback_button_input
back_button_inputback_button_input
ianrumac
marked this pull request as draft
July 27, 2026 12:55
back_button_inputback_button_input
ianrumac
marked this pull request as ready for review
July 28, 2026 11:39
…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
force-pushed
the
claude/reroute-back-button-input
branch
from
July 28, 2026 14:46
9e8adf9 to
8cc6d5f
Compare
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes in this pull request
back_button_inputmessage 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 existingclosemessage, 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_buttonnow only controls the app callback. When ENABLED andPaywallOptions.onBackPressedis set, the callback gets first refusal; returningtrueconsumes the press, returningfalse(or no callback) forwards it. When DISABLED/unset the press is forwarded directly. Extracted into pureisBackPressConsumedByApp().PaywallView.backButtonPressed()callswebView.messageHandler.handle(PaywallMessage.BackButtonPressed), which sendsback_button_inputinto the webview via the samepass()/accept64machinery aspaywall_open/paywall_close— reusing main-thread dispatch, error logging, and encoding rather than a hand-rolledevaluate.back_button_inputconsumer 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.PaywallViewis attached to the activity.Checklist
BackPressBehaviorTestcovers the app-callback matrix — disabled/unset never consult the callback, enabled+consumed, enabled+declined;PaywallViewTestverifies the press reaches the message handler;PaywallMessageHandlerTestverifiesBackButtonPressedpassesback_button_inputinto the webview viaaccept64. TheOnBackPressedCallbackwiring is thin glue over the tested functions and there is no existing activity test harness.)CHANGELOG.mdfor any breaking changes, enhancements, or bug fixes.ktlintin the main directory and fixed any issues. (Not runnable in this environment — please rely on CI; code follows surrounding style.)🤖 Generated with Claude Code
https://claude.ai/code/session_017vi3SWn6yXT5kTBj7NEb3m