Skip to content

feat(react-native): fix lagging masks and imageQuality option - #734

Merged
abelonogov-ld merged 4 commits into
mainfrom
andrey/rn-bump-native-obs-sr
Aug 20, 2026
Merged

feat(react-native): fix lagging masks and imageQuality option#734
abelonogov-ld merged 4 commits into
mainfrom
andrey/rn-bump-native-obs-sr

Conversation

@abelonogov-ld

@abelonogov-ld abelonogov-ld commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Session replay masks drifted off the content they are meant to cover whenever the
screen was moving — while scrolling, while overscrolling, and across view
transitions. The fixes landed natively in observability-android and
swift-launchdarkly-observability; this PR pulls them into the React Native
bridge and forwards the new imageQuality capture option that shipped alongside
them.

Lagging masks

Androidlaunchdarkly-observability-android 0.65.0 → 0.66.1

  • Frame alignment. Mask geometry was sampled in a Choreographer callback,
    which runs in the animation phase before that frame's measure/layout/draw,
    so it described the previous traversal — and PixelCopy returned whatever the
    surface held, usually older still. Nothing guaranteed the captured pixels fell
    between the before and after mask passes, so masks trailed the content they
    covered. Geometry is now read from inside the draw pass that produces the frame
    (ViewTreeObserver.OnDrawListener) and the capture waits for that specific
    frame to finish rendering (Window.addOnFrameMetricsAvailableListener) before
    copying pixels.
  • Stretch overscroll. Android 12+ rubber-band overscroll is a RenderEffect
    applied by the RenderThread: no view moves, no layout changes, and Compose's
    boundsInWindow is equally unaware. Mask geometry faithfully described a
    layout that was never rendered, so content slid out from under its mask at the
    edges. Masks in a stretched container's subtree now inflate using the
    framework's own damping curve. RecyclerView, ViewPager2, NestedScrollView
    and Compose containers are covered; framework ScrollView / ListView keep
    their edge effects behind blocked non-SDK fields and are not inflated.

iOSLaunchDarklySessionReplay > 0.47.0 → **> 0.53.1**

  • Masks dropped behind fading covers (0.53.1). A screen fading in absorbed the
    masks of everything behind it, so masked content was captured in the clear for
    the length of the transition. UIView.animate writes the animation's final
    value to the model layer immediately, so an incoming cover read as fully opaque
    while the screen behind it was still on display. Opacity is now accumulated
    down the layer tree, and a layer with a live opacity animation counts as
    transparent for the redundant-mask pass.
  • Double-scaled masks (0.52.0) — mask rects were scaled twice against the
    captured frame, so they landed off their content.
  • Masks built from presentation layers again (0.48.1) — masks track in-flight
    animated positions rather than final model-layer values.

New capture option

imageQuality (0.01.0, default 0.3) sets the JPEG encoding quality of
exported frames, carried over the JS → Android/iOS bridges into native
ReplayOptions / SessionReplayOptions. This matches Flutter's capture option
set and lets apps trade replay fidelity against payload size.

Also picked up by the bumps

  • Flag-evaluation telemetry is deduplicated through the SDK's DedupingHook on
    both platforms (android 0.66.0, iOS 0.53.0).
  • iOS crash reports carry every thread plus a structured Apple crash payload for
    symbolication (0.48.0, 0.49.0); session replay stops on unrecoverable
    initialization errors (0.49.0); dSYM upload is a one-line build phase (0.50.0);
    lite OTel core (0.51.0); iOS Client SDK 11.4.0 (0.52.1).

Version changes

Dependency Before After
com.launchdarkly:launchdarkly-observability-android 0.65.0 0.66.1
LaunchDarklySessionReplay (CocoaPods) ~> 0.47.0 ~> 0.53.1
com.launchdarkly:launchdarkly-android-client-sdk 5.14.0 5.14.0 (unchanged, latest)

Test plan

  • JS: yarn test --watchman=false --testPathPattern='src/__tests__/index.test' in sdk/@launchdarkly/react-native-ld-session-replay (18 passed)
  • Android: ./gradlew :launchdarkly_session-replay-react-native:testDebugUnitTest --tests 'com.sessionreplayreactnative.SessionReplayClientAdapterTest' in example/android
  • iOS: pod install in the example app resolves LaunchDarklySessionReplay 0.53.1 and builds
  • CI: React Native session replay workflows
  • Manual (Android): scroll and rubber-band overscroll a masked list, confirm masks stay on their content
  • Manual (iOS): present the number pad with a cross-dissolve over masked content, confirm nothing is exposed mid-fade
  • Manual: low vs high imageQuality visibly changes replay frame size

…e SDKs

Match Flutter's SessionReplayOptions capture set by forwarding imageQuality
through the JS/Android/iOS bridges, and bump observability-android to 0.66.0
and LaunchDarklySessionReplay to ~> 0.53.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abelonogov-ld
abelonogov-ld requested a review from a team as a code owner August 15, 2026 01:28
* main:
  fix(android): anchor session replay masks to the rendered frame and cover stretch overscroll (Rubber animation) (#739)
  fix: remediate dependency vulnerabilities across npm, go, pip, and rubygems (#732)
  chore: release main (#735)
  fix(go): Honor unsampled parent in span-kind sampler (#722)
@abelonogov-ld abelonogov-ld changed the title feat(react-native): expose session replay imageQuality and bump native SDKs feat(react-native): fix lagging masks and imageQuality option Aug 19, 2026
@abelonogov-ld
abelonogov-ld enabled auto-merge (squash) August 19, 2026 23:58
@abelonogov-ld
abelonogov-ld merged commit d0e2f99 into main Aug 20, 2026
24 checks passed
@abelonogov-ld
abelonogov-ld deleted the andrey/rn-bump-native-obs-sr branch August 20, 2026 00:15
abelonogov-ld pushed a commit that referenced this pull request Aug 20, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>session-replay-react-native: 0.23.0</summary>

##
[0.23.0](session-replay-react-native-0.22.2...session-replay-react-native-0.23.0)
(2026-08-20)


### Features

* **react-native:** fix lagging masks and imageQuality option
([#734](#734))
([d0e2f99](d0e2f99))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/bugbot) is generating a
summary for commit b4c86a6. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
abelonogov-ld added a commit that referenced this pull request Aug 20, 2026
* main:
  chore: release main (#741)
  feat(react-native): fix lagging masks and imageQuality option (#734)
  chore: release main (#736)
  fix(android): anchor session replay masks to the rendered frame and cover stretch overscroll (Rubber animation) (#739)
  fix: remediate dependency vulnerabilities across npm, go, pip, and rubygems (#732)
  chore: release main (#735)
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