Skip to content

fix(session-replay-react-native): SRMaskView pointerEvents compiles across RN 0.73-0.88 (SDKRN-41)#1874

Merged
aliaksandr-kazarez merged 3 commits into
mainfrom
fix/sdkrn-41-pointerevents-cross-version
Jul 9, 2026
Merged

fix(session-replay-react-native): SRMaskView pointerEvents compiles across RN 0.73-0.88 (SDKRN-41)#1874
aliaksandr-kazarez merged 3 commits into
mainfrom
fix/sdkrn-41-pointerevents-cross-version

Conversation

@aliaksandr-kazarez

@aliaksandr-kazarez aliaksandr-kazarez commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a publish blocker for @amplitude/session-replay-react-native (SDKRN-41): the Fabric SRMaskView's touch-transparency code (pointerEvents = BOX_NONE, shipped in #1866) does not compile on React Native ≥ 0.78 — New-Architecture consumers on newer RN cannot build the package at all.

Found by a cross-version compile/behavior sweep (RN 0.73 / 0.76 / 0.77.2 / 0.79.5 / 0.86.0 / 0.88-nightly, package installed via npm pack tarball into fresh apps).

The problem: three incompatible ABI shapes

pointerEvents changed source-level shape twice as Meta Kotlinized the RN Android codebase, and no single Kotlin syntax compiles against all three eras:

RN era Shape Kotlin property syntax Kotlin setPointerEvents(...) call override fun getPointerEvents()
≤ ~0.77 Java interface + Java ReactViewGroup get/set ✅ (synthetic)
~0.78–0.80 Kotlin ReactPointerEventsView declares val pointerEvents; ReactViewGroup still Java 'val' cannot be reassigned ❌ overrides nothing
0.81+ Kotlin ReactViewGroup with var pointerEvents ❌ unresolved reference ❌ (fun vs var)

The first two fix attempts on this branch each cover two of the three eras (kept as commits for the archaeology; each was disproven empirically by the sweep).

The fix: a bytecode-level Java shim

The bytecode has setPointerEvents(PointerEvents) in every era — the Java method through 0.80, the Kotlin-var-generated setter from 0.81 (verified via javap of the react-android 0.86 AAR). Java source resolves against bytecode, immune to Kotlin's property semantics. So:

  • SRMaskViewPointerEvents.java — a ~10-line package-private shim: view.setPointerEvents(PointerEvents.BOX_NONE)
  • SRMaskView.kt calls it from init and re-asserts in onLayout (covers recycling resets version-agnostically; replaces the unshippable accessor override)

Verification

Stock tarball (no local patches), Android compile per version — with explicit verification that compileDebugJavaWithJavac executes and the shim .class lands in the library output on every new-arch lane:

RN Result
0.73.11 old-arch assembleDebug (newarch sources excluded; packaging sane)
0.77.2 new-arch ✅ compile (forced non-incremental) + instrumented suite 16/16 on device (incl. the BOX_NONE test)
0.79.5 new-arch ✅ compile + assembleDebug; separately: 5-min debug crash-stress + 12/12 taps-through-mask + masking verified in captured replay (iOS 0.79 also device-verified: ~9 min, ~8,700 stress commits, zero asserts)
0.86.0 new-arch ✅ compile + assembleDebug (device behavior verified with semantically identical code during the sweep)
0.88.0-nightly ✅ compile + assembleDebug

iOS is untouched by this change (its analogue is the version-stable hitTest override).

Do NOT "simplify" the shim back into Kotlin — the constraint is documented in both files, and any Kotlin call/override/assignment shape breaks at least one supported RN era.

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?: No

🤖 Generated with Claude Code


Note

Low Risk
Narrow Android Fabric touch-targeting fix with unchanged intended behavior; no auth, data, or API surface changes.

Overview
Fixes a publish blocker where Fabric SRMaskView no longer compiled on React Native ≥ 0.78 because Kotlin cannot express pointerEvents = BOX_NONE consistently across RN’s three incompatible Android API shapes.

SRMaskViewPointerEvents.java is added as a small Java shim that calls setPointerEvents(PointerEvents.BOX_NONE) against bytecode that exists on all supported RN versions. SRMaskView.kt drops direct Kotlin setPointerEvents usage and the getPointerEvents() override, and instead calls SRMaskViewPointerEvents.forceBoxNone(this) in init and again in onLayout so touch transparency survives view recycling without version-specific overrides.

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

aliaksandr-kazarez and others added 3 commits July 9, 2026 14:03
…N-41)

RN 0.79 converted ReactPointerEventsView to a Kotlin interface with a val
pointerEvents property, so the 0.77-era override fun getPointerEvents()
belt-and-braces accessor 'overrides nothing' and hard-fails compilation on
RN 0.78+ (found by the RN-version matrix sweep, lane 0.79 — a publish
blocker for New-Architecture consumers on newer RN).

No accessor override can compile against both shapes, so drop it: keep the
init-time setPointerEvents(BOX_NONE) and re-assert it in onLayout (a
version-stable hook that runs before any capture/touch can observe a
recycled instance whose field was reset). Instrumented suite 16/16 on RN
0.77.2; the 0.79 lane verified the identical semantics compile and behave
(5-min crash-stress, 78/78 taps through masks, Switch responsive under a
widened frame) with this shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d ReactViewGroup (SDKRN-41)

The stable/nightly matrix lane showed the previous commit is still broken on
RN >= 0.81: ReactViewGroup.java became ReactViewGroup.kt in RN 0.81, so
setPointerEvents(...) is an unresolved reference from Kotlin source there.
Property-assignment syntax (pointerEvents = BOX_NONE) is the one shape that
compiles everywhere: Kotlin synthesizes the property from the Java accessor
pair on <= 0.80 and binds the real property on 0.81+. Verified equivalent by
the lane's marked node_modules experiments on 0.86.0 and
0.88.0-nightly-20260709 (build + device smoke green with property syntax).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ABI shapes (SDKRN-41)

The cross-version sweep disproved both prior shapes: RN ~0.78-0.80 has a
Kotlin ReactPointerEventsView declaring val pointerEvents over a still-Java
ReactViewGroup, so property assignment fails there ('val' cannot be
reassigned) while the explicit setter call fails on 0.81+ (Kotlinized
ReactViewGroup, unresolved reference). No single Kotlin syntax compiles
against all three eras.

The bytecode always has setPointerEvents(PointerEvents) — the Java method
through 0.80, the Kotlin-var-generated setter from 0.81 (javap-verified on
the 0.86 AAR) — and Java source resolves against bytecode. Add a minimal
Java shim (SRMaskViewPointerEvents.forceBoxNone) and call it from init and
onLayout. Sweep verification: stock tarball compile on RN 0.73 old-arch,
0.77.2, 0.79, 0.86.0, and 0.88.0-nightly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@aliaksandr-kazarez
aliaksandr-kazarez requested a review from a team July 9, 2026 22:19
@linear-code

linear-code Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

SDKRN-41

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
packages/analytics-browser/lib/scripts/amplitude-min.js.gz 60.91 KB (0%)
packages/session-replay-browser/lib/scripts/session-replay-browser-min.js.gz 134.34 KB (0%)
packages/unified/lib/scripts/amplitude-min.umd.js.gz 214.79 KB (0%)
@amplitude/element-selector (gzipped esm) 2.67 KB (0%)

@aliaksandr-kazarez
aliaksandr-kazarez marked this pull request as ready for review July 9, 2026 22:36
@aliaksandr-kazarez

Copy link
Copy Markdown
Contributor Author

bugbot review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 4c14be0. Configure here.

Copilot AI 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.

Pull request overview

Fixes an Android New Architecture build break in @amplitude/session-replay-react-native by making SRMaskView’s pointerEvents = BOX_NONE enforcement compile across React Native 0.73–0.88 despite RN’s incompatible pointerEvents source-level API shapes.

Changes:

  • Added a small Java shim that calls ReactViewGroup#setPointerEvents(PointerEvents.BOX_NONE) against stable bytecode across RN versions.
  • Updated SRMaskView to use the shim (in init and again in onLayout) and removed the Kotlin accessor override that fails to compile across RN eras.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
packages/session-replay-react-native/android/src/newarch/java/com/amplitude/sessionreplayreactnative/fabric/SRMaskViewPointerEvents.java Adds the Java bytecode-level shim to set pointerEvents in a cross-RN-compatible way.
packages/session-replay-react-native/android/src/newarch/java/com/amplitude/sessionreplayreactnative/fabric/SRMaskView.kt Replaces Kotlin pointerEvents handling with the shim and reasserts BOX_NONE during layout to survive recycling resets.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@aliaksandr-kazarez
aliaksandr-kazarez merged commit 01ec004 into main Jul 9, 2026
17 checks passed
@aliaksandr-kazarez
aliaksandr-kazarez deleted the fix/sdkrn-41-pointerevents-cross-version branch July 9, 2026 22:42
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.

3 participants