Skip to content

feat(RevenueCatUI): arbitrate web_view drag gestures with the paywall scroll#3823

Merged
AlvaroBrey merged 4 commits into
mainfrom
webview/scroll-touch-arbitration
Jul 24, 2026
Merged

feat(RevenueCatUI): arbitrate web_view drag gestures with the paywall scroll#3823
AlvaroBrey merged 4 commits into
mainfrom
webview/scroll-touch-arbitration

Conversation

@AlvaroBrey

@AlvaroBrey AlvaroBrey commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-ios and hybrids

Motivation

A web_view embedded in the paywall double-scrolls: dragging interactive or scrollable content (a map, an inner list) also scrolls the whole paywall.

Description

PaywallWebView decides, once per drag, whether the web_view or the paywall scroll owns the gesture, via requestDisallowInterceptTouchEvent (claim-once, since Compose won't hand a gesture back). The web_view claims only when its content consumes the drag:

  • a per-gesture JS probe reports inner overflow scrollers and touch-action maps — the signals native code can't see — over the existing secure message channel;
  • native canScrollVertically/Horizontally covers root/page scroll;
  • otherwise the paywall scrolls.

No-ops to native scrollability on old WebViews without the probe features. Verified on device (inner list, carousel, map) plus unit tests for the decision logic.


Note

Medium Risk
Touch handling and injected JS affect all paywall web_views; behavior degrades to native scroll-only on older WebViews, with edge cases where JS-only gesture blocking isn't detected.

Overview
Fixes double-scroll when dragging inside paywall web_view content (maps, inner lists, carousels) by deciding once per drag whether the WebView or the outer paywall scroll owns the gesture.

Introduces PaywallWebView, which calls requestDisallowInterceptTouchEvent only when shouldWebViewOwnGesture says the WebView should own the drag (respecting touch slop and dominant axis). A WebViewGestureOwnershipProbe injects document-start JS and uses WebMessageListener to report per-touch whether inner scrollers or non-default touch-action need the gesture; that verdict overrides native checks when it says "own," otherwise root canScrollVertically/Horizontally decides.

WebViewComponentView swaps plain WebView for PaywallWebView, wires install/teardown of the probe on release, and disableTapHighlight no longer falls back to * when origin is unknown—it skips injection instead (tests updated). Unit tests cover arbitration rules and probe install behavior.

Reviewed by Cursor Bugbot for commit ab663d1. Bugbot is set up for automated code reviews on this repo. Configure here.

@AlvaroBrey AlvaroBrey self-assigned this Jul 23, 2026
@AlvaroBrey
AlvaroBrey force-pushed the webview/scroll-touch-arbitration branch from ac5ad0d to 4f4b622 Compare July 23, 2026 15:15
@AlvaroBrey

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4f4b622. Configure here.

@AlvaroBrey
AlvaroBrey force-pushed the webview/scroll-touch-arbitration branch 2 times, most recently from 5bf36f2 to 4d2f4ab Compare July 23, 2026 15:55
@AlvaroBrey
AlvaroBrey marked this pull request as ready for review July 23, 2026 16:10
@AlvaroBrey
AlvaroBrey requested a review from a team as a code owner July 23, 2026 16:10
@AlvaroBrey AlvaroBrey added pr:fix A bug fix and removed pr:feat A new feature labels Jul 23, 2026
@AlvaroBrey
AlvaroBrey force-pushed the webview/scroll-touch-arbitration branch from 4d2f4ab to e8ba929 Compare July 24, 2026 06:55
@AlvaroBrey
AlvaroBrey force-pushed the webview/scroll-touch-arbitration branch from e8ba929 to 0672256 Compare July 24, 2026 07:13
@AlvaroBrey

Copy link
Copy Markdown
Contributor Author

@cursor review

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d3a11d4. Configure here.

Copy link
Copy Markdown
Contributor Author

@ajpallares ajpallares left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one! Two small comments, non-blocking at all

… scroll

PaywallWebView decides, once per gesture, whether the web_view or the paywall scroll owns a drag,
via requestDisallowInterceptTouchEvent (the only lever Compose honors). It claims only when the
web_view should own the drag (Compose won't hand a gesture back, so no pre-claim/edge-chaining).
The content verdict (supplied next) covers inner scrollers/maps; native canScrollVertically/Horizontally
covers root scroll and is the sole signal on old WebViews.
…content

Injects a web-standard document-start probe that, on each touchstart, reports whether the touched
element consumes the drag: an inner overflow scroller, or an element declaring touch-action (a map)
— the signals canScrollVertically can't see. Per-element, so a page-global listener can't make the
whole view greedy. Reported over the same secure addWebMessageListener channel the bridge uses, and
removed on release so a late verdict can't fire during teardown. No-ops on old WebViews.
AlvaroBrey and others added 2 commits July 24, 2026 13:37
…in, name JS node-type constant

Fail closed instead of falling back to a wildcard origin when the bundle
origin can't be resolved, matching shouldBlockWebViewNavigation's existing
posture for that case. Also names the JS nodeType magic number.

Co-authored-by: Antonio Pallares <ajpallares@users.noreply.github.com>
@AlvaroBrey
AlvaroBrey force-pushed the webview/scroll-touch-arbitration branch from d8b1a48 to ab663d1 Compare July 24, 2026 11:38
@AlvaroBrey
AlvaroBrey added this pull request to the merge queue Jul 24, 2026
Merged via the queue into main with commit 98047cc Jul 24, 2026
35 checks passed
@AlvaroBrey
AlvaroBrey deleted the webview/scroll-touch-arbitration branch July 24, 2026 12:11
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