Skip to content

fix: stop failing the consent gate open on slow first-layer loads - #82

Merged
0x7f merged 5 commits into
mainfrom
fix-layer-page-load-timeout
Oct 22, 2026
Merged

0x7f merged 5 commits into
mainfrom
fix-layer-page-load-timeout

Conversation

@0x7f

@0x7f 0x7f commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Why

Since 0.9.0 the gate fails open if the layer page hasn't loaded within 8s. On fresh Android installs that often fires before the layer shows:

  • The timer starts at mount, so it includes creating the WebView (first WebView in the process starts the system WebView engine).
  • onLoadEnd fires on onPageFinished, after every subresource (~27 requests), all uncached (cacheEnabled={false}).
  • First launch is when the layer shows (no consent yet) and when the device is busiest.

Fail-open never calls onVisibilityChange(false), so hosts that hide content while the layer is visible stay blank.

Changes

  • layerPageLoadTimeoutMs defaults to 30s, like every other gate timeout. Unreachable pages still surface fast via onError. Drops the E2E 30s override from Make the iOS E2E job tolerate staging and simulator flakiness #81.
  • Fail-open reports onVisibilityChange(false); the visibility policy stops after failing open.
  • Fail-opens are reported to Sentry, tagged uiVersion. The SDK ERROR state is skipped (already reported). HTTP errors on the page become an Error with the status.
  • Core: sdk.reportError(error, { msg, tags }).
  • README documents timeouts.
  • E2E login submits with Return, not a tap on "Log in".

Behaviour changes

  • onVisibilityChange fires only on an actual change. No repeated true after the second layer or login.
  • UI peer range for the core is >=0.10.0. yarn install warns (YN0060) until the release PR bumps the core.

Testing

  • Lint, typecheck, prettier, build, tests (core 70, UI 57) pass.
  • New gate and layer tests. Restoring the 8s default or removing the fail-open visibility report fails them.
  • Not run on a device.

0x7f added 3 commits October 20, 2026 10:02
The UI package fails the consent gate open on CMP, SDK and layer failures,
but only logs them to the console: it has no Sentry client of its own, so
none of those failures reach Contentpass. Expose the SDK's reportError on
its instance, and let a report carry tags that apply to that event only,
so the UI can say which of its versions failed without changing the scope
every other report goes through.
The page-load stage gave the layer 8s on the assumption that it waits for
a small static HTML shell. It waits for much more: the timer starts when
the layer mounts, so it covers creating the WebView, and onLoadEnd fires
on Android's onPageFinished, once the page and every script, font and
image it loads have arrived. A fresh Android install is the slowest case
for all of it - the first WebView in the process, nothing cached, the CMP
SDK's first download running alongside - and it is also when the layer
shows, since there is no consent yet. Those installs failed the gate open
before the layer ever appeared; 0.8.1, which had no deadline, kept showing
the spinner until the page arrived.

Default the stage to UI_OPERATION_TIMEOUT_MS like every other gate
timeout. A page that cannot load at all still shows up fast: DNS, TLS and
connection failures arrive as onError, which already shows the retry
screen. The E2E job's 30s override only existed to work around the 8s
default, so it goes.

An HTTP error on the page now fails with an Error that names the status,
rather than the raw native event.
Failing open renders the app in place of the consent layer, but the host
app was never told: onVisibilityChange had reported true when the layer
appeared and nothing afterwards. An app that hides its content while the
layer is visible stayed blank for the rest of the session.

Track what the host app was last told and report false when the gate
fails open. Once failed open, the visibility policy stops running, so a
later consent change cannot report the layer as visible again. Reporting
against the last reported value rather than the gate's internal state
also stops the repeated true the host got after every second layer or
login, during which the gate hides the layer without telling it.

Report every fail-open through the SDK's reportError, tagged with the UI
package version, except the SDK's own ERROR state, which the SDK reported
when it entered it. That needs the SDK's new reportError, so the peer
range now starts at 0.10.0. Document the timeouts prop, which the README
did not mention.
@0x7f
0x7f force-pushed the fix-layer-page-load-timeout branch from 12761be to 6d230b2 Compare October 20, 2026 10:28
0x7f added 2 commits October 22, 2026 13:39
The happy-path flow tapped the staging login page's "Log in" button. The
button sits in a footer pinned to the bottom of the page, and once the
password field has focus the keyboard's toolbar covers the bottom of the
viewport. Where the page ends up after the keyboard opens varies by a few
points between runs: when the button's centre fell inside the toolbar,
the toolbar took the tap, the form was never submitted, and the flow
timed out waiting for the app to show the logged-in state.

Pressing return in the password field submits the form the same way,
without depending on where the button is.
@contentpass/react-native-contentpass 0.9.1 -> 0.10.0
@contentpass/react-native-contentpass-ui 0.10.1 -> 0.11.0
@0x7f
0x7f merged commit fc7f467 into main Oct 22, 2026
6 checks passed
@0x7f
0x7f deleted the fix-layer-page-load-timeout branch October 22, 2026 15:00
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