Skip to content

perf(react-native-sdk): reduce re-renders, fix listener cleanup, prune dead code#2274

Open
oliverlaz wants to merge 1 commit into
mainfrom
perf/react-native-sdk-audit-fixes
Open

perf(react-native-sdk): reduce re-renders, fix listener cleanup, prune dead code#2274
oliverlaz wants to merge 1 commit into
mainfrom
perf/react-native-sdk-audit-fixes

Conversation

@oliverlaz

@oliverlaz oliverlaz commented Jun 8, 2026

Copy link
Copy Markdown
Member

💡 Overview

A batch of React Native SDK performance and cleanup changes: re-render reductions across the participant and livestream render chain, an event-listener memory-leak fix, an unmount-safety guard, and dead-icon cleanup. No public API changes.

📝 Implementation notes

Fixes

  • BusyTonePlayer: the call.rejected disposer was returned from the event handler, not the effect, so it never ran. Moved it to the effect body so the listener, timeout, and tone are cleaned up on unmount.
  • AppStateListener: added a cancelled guard so the mount effect's isInPiPMode().then cannot write to the shared subject after unmount.

Re-render / allocation reductions

  • ParticipantView and VideoRenderer memoized; VideoRenderer's visibility-effect deps narrowed to the two primitives it reads.
  • CallParticipantsList: memoized the participant prop-bag and corrected renderItem deps (dropped a stale-closure eslint-disable).
  • CallContent: stores a remote-participant count bucket plus the first participant instead of the full array, re-rendering only on bucket-boundary crossings.
  • LivestreamLayout: derives objectFit during render instead of via a renderless child effect.
  • Lobby and FloatingParticipantView: memoized per-render literals; hoisted a constant lookup map.
  • useAutoEnterPiPEffect: merged two duplicate [disablePictureInPicture] effects.
  • CallContent, CallParticipantsGrid, and CallParticipantsSpotlight: landscapeStyles converted to StyleSheet constants; memoized the spotlight tile's style.
  • LivestreamEnded: extracted a memoized RecordingRow; useCallback'd renderItem, keyExtractor, and openUrl.
  • getCallDisplayName: collapsed filter/map/filter into a single flatMap.
  • DeviceStats: constructs NativeEventEmitter inside the gated effect instead of at module load.

Dead-code cleanup

  • Removed 5 unused icons (LeaveStreamIcon, Settings, ShieldBadge, Spotlight, ThreeDots) and their barrel entries.
  • Moved TopViewBackground into the dogfood app (its only consumer, via a deep src/icons import) and repointed the import.

Verification: SDK tsc, eslint, jest (5/5 suites, 13/13), and NODE_ENV=production yarn build all green; dogfood tsc green.

🎫 Ticket: TBD

📑 Docs: N/A

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved Picture-in-Picture mode handling on Android with better cleanup and initialization logic.
  • Performance

    • Optimized component rendering to reduce unnecessary re-renders across call layouts and participant views.
    • Enhanced memoization of styles and participant data for improved app responsiveness.
  • Chores

    • Removed unused icon assets from the SDK.

…e dead code

Fixes:
- BusyTonePlayer: unsubscribe the call.rejected listener on unmount
- AppStateListener: add a cancelled guard so the mount effect cannot write to the shared subject after unmount

Perf:
- Memoize ParticipantView and VideoRenderer; narrow VideoRenderer's visibility-effect deps to the two primitives it reads
- Stabilize CallParticipantsList's participant prop-bag and fix renderItem deps
- CallContent: store a remote-participant count bucket plus the first participant instead of the whole array
- LivestreamLayout: derive objectFit during render instead of via a renderless child effect
- Lobby and FloatingParticipantView: memoize per-render literals; hoist a constant lookup map
- useAutoEnterPiPEffect: merge two duplicate effects
- landscapeStyles to StyleSheet constants; memoize the spotlight tile style
- LivestreamEnded: extract a memoized RecordingRow and stabilize callbacks
- getCallDisplayName: collapse filter/map/filter into a single flatMap
- DeviceStats: build NativeEventEmitter inside the effect

Chore:
- Remove 5 unused icons; move TopViewBackground into the dogfood app
@changeset-bot

changeset-bot Bot commented Jun 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d0b2d1b

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR improves the React Native SDK's performance and architecture through floating participant state optimization, component memoization, livestream refactoring, icon cleanup, and lifecycle management fixes. Changes span from participant rendering logic to provider cleanup, optimizing re-render frequency and fixing potential race conditions in native module initialization.

Changes

React Native SDK Refactoring & Performance Improvements

Layer / File(s) Summary
Floating participant state optimization
packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx, packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx, packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx
CallContent replaces remoteParticipants array storage with bucketed floatingViewState tracking clamped participant count and first remote participant identity. Floating view conditions and participant selection updated to use the new state. Landscape styling across Grid, Spotlight, and Content is refactored from inline ViewStyle objects to StyleSheet.create blocks with row/column variants.
Component memoization for render optimization
packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantView.tsx, packages/react-native-sdk/src/components/Participant/ParticipantView/VideoRenderer/index.tsx, packages/react-native-sdk/src/components/Participant/FloatingParticipantView/index.tsx, packages/react-native-sdk/src/components/Call/CallParticipantsList/CallParticipantsList.tsx, packages/react-native-sdk/src/components/Call/Lobby/Lobby.tsx
ParticipantView and VideoRenderer wrapped in React.memo with stable displayName assignments. FloatingParticipantView memoizes participantViewProps with proper dependencies; floatingAlignmentMap moved to module scope. CallParticipantsList and Lobby memoize participantProps and connectedUserAsParticipant via useMemo to stabilize object references across renders.
Livestream layout and player refactoring
packages/react-native-sdk/src/components/Livestream/LivestreamLayout/LivestreamLayout.tsx, packages/react-native-sdk/src/components/Livestream/LivestreamPlayer/LivestreamEnded.tsx
LivestreamLayout derives objectFit during render via useTrackDimensions instead of storing in state and using a renderless probe component. VideoRenderer now renders directly with explicit trackType, participant, and computed objectFit. LivestreamEnded modularizes recording list rendering with a typed RecordingRow component and useCallback-based keyExtractor and renderItem callbacks.
Icon cleanup and barrel export consolidation
packages/react-native-sdk/src/icons/index.tsx
Removes unused icon re-exports (Settings, ThreeDots, Spotlight, ShieldBadge, LeaveStreamIcon, TopViewBackground) from barrel exports; retains only actively used video, screen-share, reaction, and stream control icons.
Provider and hook lifecycle management fixes
packages/react-native-sdk/src/providers/BusyTonePlayer.tsx, packages/react-native-sdk/src/providers/StreamCall/AppStateListener.tsx, packages/react-native-sdk/src/providers/StreamCall/DeviceStats.tsx, packages/react-native-sdk/src/hooks/useAutoEnterPiPEffect.tsx
AppStateListener adds cancelled flag to guard async PiP initialization from running after unmount. BusyTonePlayer hoists logger/timeout to effect scope for correct cleanup ordering. DeviceStats moves NativeEventEmitter initialization into effect lifecycle. useAutoEnterPiPEffect immediately disables PiP when disablePictureInPicture prop is set.
Utility and sample app updates
packages/react-native-sdk/src/utils/internal/callingx/callingx.ts, sample-apps/react-native/dogfood/src/components/CallControlls/TopControls.tsx
getCallDisplayName refactored to use flatMap with conditional array returns instead of chained filter/map/filter. TopControls updates TopViewBackground import from SDK icons module to local assets module.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Floating views now dance with grace,
State bucketed to find its place.
Memoized components skip their dance,
While livestream layouts take their stance.
Icons fade, providers align,
A cleaner SDK by design! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main changes: performance improvements through re-render reduction, listener cleanup fixes, and unused code removal.
Description check ✅ Passed The description is comprehensive with detailed implementation notes, organized fixes and changes, and verification details. All required template sections are present and filled out appropriately.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/react-native-sdk-audit-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
packages/react-native-sdk/src/providers/StreamCall/AppStateListener.tsx (3)

42-51: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add error handling for native module promise.

The isInPiPMode() call lacks a .catch() handler. Native module calls can reject due to platform errors or missing implementations.

🛡️ Proposed fix
     NativeModules?.StreamVideoReactNative?.isInPiPMode().then(
       (isInPiP: boolean | null | undefined) => {
         if (cancelled) return;
         isInPiPMode$.next(!!isInPiP);
         logger.debug(
           'Initial PiP mode on mount (after asking native module) set to ',
           !!isInPiP,
         );
       },
-    );
+    ).catch((err) => 
+      logger.warn('Failed to query initial PiP mode:', err)
+    );

As per coding guidelines: "Wrap native method calls in try-catch and always handle promise rejection from native modules".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-sdk/src/providers/StreamCall/AppStateListener.tsx`
around lines 42 - 51, The call to
NativeModules?.StreamVideoReactNative?.isInPiPMode() can reject but has no
rejection handler; update the initialization to handle errors by adding a
.catch() (or wrapping in async/try-catch) that checks cancelled, logs the error
via logger.error with context ("isInPiPMode failed") and keeps isInPiPMode$
unchanged, and retain the existing success path that sets isInPiPMode$ and calls
logger.debug; reference NativeModules?.StreamVideoReactNative?.isInPiPMode,
isInPiPMode$, logger.debug, and cancelled when making the change.

Source: Coding guidelines


106-115: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add error handling for isCallAliveConfigured promise.

The native module call lacks a .catch() handler. If the promise rejects, it will surface as an unhandled rejection.

🛡️ Proposed fix
             NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(
               (isCallAliveConfigured: boolean) => {
                 if (!isCallAliveConfigured) {
                   renableCamera();
                 }
               },
-            );
+            ).catch((err) =>
+              logger.warn('Failed to check isCallAliveConfigured:', err)
+            );

As per coding guidelines: "Wrap native method calls in try-catch and always handle promise rejection from native modules".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-sdk/src/providers/StreamCall/AppStateListener.tsx`
around lines 106 - 115, The call to
NativeModules.StreamVideoReactNative.isCallAliveConfigured is missing rejection
handling and may cause unhandled promise rejections; wrap the native call in a
try/catch (or add a .catch) and on error log the exception and call
renableCamera() as a safe fallback. Specifically, update the
isCallAliveConfigured invocation (or convert it to await inside a try/catch) so
that any thrown error is caught, processLogger or console.error records the
error, and renableCamera() is invoked in the catch handler.

Source: Coding guidelines


161-182: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add error handling for native module promises.

Both isInPiPMode() and isCallAliveConfigured() lack .catch() handlers. These calls are on the background transition path and should handle rejection gracefully.

🛡️ Proposed fix
             NativeModules?.StreamVideoReactNative?.isInPiPMode().then(
               (isInPiP: boolean | null | undefined) => {
                 isInPiPMode$.next(!!isInPiP);
                 if (!isInPiP) {
                   if (AppState.currentState === 'active') {
                     return;
                   }
                   NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(
                     (isCallAliveConfigured: boolean) => {
                       if (!isCallAliveConfigured) {
                         disableCameraIfNeeded();
                       }
                     },
-                  );
+                  ).catch((err) =>
+                    logger.warn('Failed to check isCallAliveConfigured:', err)
+                  );
                 }
               },
-            );
+            ).catch((err) => 
+              logger.warn('Failed to query PiP mode on background:', err)
+            );

As per coding guidelines: "Wrap native method calls in try-catch and always handle promise rejection from native modules".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-sdk/src/providers/StreamCall/AppStateListener.tsx`
around lines 161 - 182, The native-promises returned by
NativeModules.StreamVideoReactNative.isInPiPMode() and .isCallAliveConfigured()
are unhandled; wrap these calls to handle rejections (either convert to
async/await inside the AppStateListener flow or add .catch() handlers) and log
or swallow errors appropriately so they don't crash background transitions;
specifically update the block using isInPiPMode() to handle promise rejection
and likewise handle .isCallAliveConfigured() rejections before calling
disableCameraIfNeeded(), referencing the NativeModules.StreamVideoReactNative,
isInPiPMode, isCallAliveConfigured, and disableCameraIfNeeded symbols.

Source: Coding guidelines

packages/react-native-sdk/src/providers/StreamCall/DeviceStats.tsx (3)

39-44: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add error handling for currentThermalState promise.

The native module call lacks a .catch() handler. If the promise rejects, it will surface as an unhandled rejection.

🛡️ Proposed fix
     StreamVideoReactNative.currentThermalState().then(
       (initialState: string) => {
         setThermalState(initialState);
         call.tracer.trace('device.thermalState', initialState);
       },
-    );
+    ).catch((err) => {
+      // Ignore error - thermal state will remain at default
+      call.tracer.trace('device.thermalState.error', err);
+    });

As per coding guidelines: "Wrap native method calls in try-catch and always handle promise rejection from native modules".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-sdk/src/providers/StreamCall/DeviceStats.tsx` around
lines 39 - 44, The call to StreamVideoReactNative.currentThermalState() in
DeviceStats.tsx is missing rejection handling; update the invocation (where
setThermalState and call.tracer.trace are used) to handle errors from the
promise (either add a .catch(...) to the promise chain or wrap the call in an
async try/catch) and log the error via the existing logging/tracing (e.g.,
processLogger or call.tracer.trace with an error message) so rejections are not
left unhandled while preserving the existing success path that calls
setThermalState and call.tracer.trace('device.thermalState', initialState).

Source: Coding guidelines


54-60: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add error handling for getBatteryState promise.

The native module call in pollBatteryState lacks a .catch() handler. This function is called repeatedly via setInterval, so unhandled rejections could accumulate.

🛡️ Proposed fix
     const pollBatteryState = () => {
       StreamVideoReactNative.getBatteryState().then(
         (data: { charging: boolean; level: number }) => {
           call.tracer.trace('device.batteryState', data);
         },
-      );
+      ).catch((err) => {
+        // Ignore error - battery polling will retry on next interval
+        call.tracer.trace('device.batteryState.error', err);
+      });
     };

As per coding guidelines: "Wrap native method calls in try-catch and always handle promise rejection from native modules".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-sdk/src/providers/StreamCall/DeviceStats.tsx` around
lines 54 - 60, pollBatteryState is calling
StreamVideoReactNative.getBatteryState() without handling rejections, leading to
potential unhandled promise rejections when run repeatedly (via setInterval).
Update pollBatteryState to handle errors: either convert it to an async function
and wrap the await StreamVideoReactNative.getBatteryState() in try/catch, or add
a .catch() to the existing promise chain; in the catch block call
call.tracer.trace or call.tracer.error (or process logger used elsewhere) with a
clear message and the error object so failures are recorded and do not
accumulate.

Source: Coding guidelines


24-29: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add error handling for isLowPowerModeEnabled promise.

The native module call lacks a .catch() handler. If the promise rejects, it will surface as an unhandled rejection.

🛡️ Proposed fix
     StreamVideoReactNative.isLowPowerModeEnabled().then(
       (initialPowerMode: boolean) => {
         setPowerState(initialPowerMode);
         call.tracer.trace('device.lowPowerMode', initialPowerMode);
       },
-    );
+    ).catch((err) => {
+      // Ignore error - power mode state will remain at default
+      call.tracer.trace('device.lowPowerMode.error', err);
+    });

As per coding guidelines: "Wrap native method calls in try-catch and always handle promise rejection from native modules".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-native-sdk/src/providers/StreamCall/DeviceStats.tsx` around
lines 24 - 29, The call to StreamVideoReactNative.isLowPowerModeEnabled lacks
error handling and can produce an unhandled rejection; update the usage (the
promise returned by StreamVideoReactNative.isLowPowerModeEnabled) to either
await it in a try/catch or add a .catch() handler that handles failures, logs
the error via call.tracer.trace (e.g., 'device.lowPowerMode.error') and avoids
calling setPowerState on failure; ensure setPowerState and
call.tracer.trace('device.lowPowerMode', ...) remain in the success path and
that the catch path handles/logs the error and prevents an unhandled promise
rejection.

Source: Coding guidelines

🧹 Nitpick comments (1)
packages/react-native-sdk/src/components/Livestream/LivestreamPlayer/LivestreamEnded.tsx (1)

18-42: ⚡ Quick win

Consider adding accessibility props to Pressable.

The RecordingRow component is well-structured with proper memoization, but the Pressable at line 34 lacks accessibility properties. Consider adding accessibilityRole="button" and accessibilityLabel to improve screen reader support.

♿ Suggested accessibility enhancement
-      <Pressable style={buttonStyle} onPress={handlePress}>
+      <Pressable 
+        style={buttonStyle} 
+        onPress={handlePress}
+        accessibilityRole="button"
+        accessibilityLabel={`Open recording ${recording.url.substring(0, 70)}`}
+      >
         <Text style={textStyle}>{recording.url.substring(0, 70)}...</Text>
       </Pressable>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/react-native-sdk/src/components/Livestream/LivestreamPlayer/LivestreamEnded.tsx`
around lines 18 - 42, RecordingRow's Pressable is missing accessibility props;
update the Pressable in the RecordingRow component to include
accessibilityRole="button" and a descriptive accessibilityLabel (e.g., derived
from recording.url or a passed-in prop) so screen readers can announce it,
ensuring you keep the existing onPress handler (handlePress) and memoization
intact; locate the Pressable inside RecordingRow and add the accessibilityRole
and accessibilityLabel attributes (or accept an accessibilityLabel prop on
RecordingRow and pass it through) so the button is accessible.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/react-native-sdk/src/providers/StreamCall/AppStateListener.tsx`:
- Around line 42-51: The call to
NativeModules?.StreamVideoReactNative?.isInPiPMode() can reject but has no
rejection handler; update the initialization to handle errors by adding a
.catch() (or wrapping in async/try-catch) that checks cancelled, logs the error
via logger.error with context ("isInPiPMode failed") and keeps isInPiPMode$
unchanged, and retain the existing success path that sets isInPiPMode$ and calls
logger.debug; reference NativeModules?.StreamVideoReactNative?.isInPiPMode,
isInPiPMode$, logger.debug, and cancelled when making the change.
- Around line 106-115: The call to
NativeModules.StreamVideoReactNative.isCallAliveConfigured is missing rejection
handling and may cause unhandled promise rejections; wrap the native call in a
try/catch (or add a .catch) and on error log the exception and call
renableCamera() as a safe fallback. Specifically, update the
isCallAliveConfigured invocation (or convert it to await inside a try/catch) so
that any thrown error is caught, processLogger or console.error records the
error, and renableCamera() is invoked in the catch handler.
- Around line 161-182: The native-promises returned by
NativeModules.StreamVideoReactNative.isInPiPMode() and .isCallAliveConfigured()
are unhandled; wrap these calls to handle rejections (either convert to
async/await inside the AppStateListener flow or add .catch() handlers) and log
or swallow errors appropriately so they don't crash background transitions;
specifically update the block using isInPiPMode() to handle promise rejection
and likewise handle .isCallAliveConfigured() rejections before calling
disableCameraIfNeeded(), referencing the NativeModules.StreamVideoReactNative,
isInPiPMode, isCallAliveConfigured, and disableCameraIfNeeded symbols.

In `@packages/react-native-sdk/src/providers/StreamCall/DeviceStats.tsx`:
- Around line 39-44: The call to StreamVideoReactNative.currentThermalState() in
DeviceStats.tsx is missing rejection handling; update the invocation (where
setThermalState and call.tracer.trace are used) to handle errors from the
promise (either add a .catch(...) to the promise chain or wrap the call in an
async try/catch) and log the error via the existing logging/tracing (e.g.,
processLogger or call.tracer.trace with an error message) so rejections are not
left unhandled while preserving the existing success path that calls
setThermalState and call.tracer.trace('device.thermalState', initialState).
- Around line 54-60: pollBatteryState is calling
StreamVideoReactNative.getBatteryState() without handling rejections, leading to
potential unhandled promise rejections when run repeatedly (via setInterval).
Update pollBatteryState to handle errors: either convert it to an async function
and wrap the await StreamVideoReactNative.getBatteryState() in try/catch, or add
a .catch() to the existing promise chain; in the catch block call
call.tracer.trace or call.tracer.error (or process logger used elsewhere) with a
clear message and the error object so failures are recorded and do not
accumulate.
- Around line 24-29: The call to StreamVideoReactNative.isLowPowerModeEnabled
lacks error handling and can produce an unhandled rejection; update the usage
(the promise returned by StreamVideoReactNative.isLowPowerModeEnabled) to either
await it in a try/catch or add a .catch() handler that handles failures, logs
the error via call.tracer.trace (e.g., 'device.lowPowerMode.error') and avoids
calling setPowerState on failure; ensure setPowerState and
call.tracer.trace('device.lowPowerMode', ...) remain in the success path and
that the catch path handles/logs the error and prevents an unhandled promise
rejection.

---

Nitpick comments:
In
`@packages/react-native-sdk/src/components/Livestream/LivestreamPlayer/LivestreamEnded.tsx`:
- Around line 18-42: RecordingRow's Pressable is missing accessibility props;
update the Pressable in the RecordingRow component to include
accessibilityRole="button" and a descriptive accessibilityLabel (e.g., derived
from recording.url or a passed-in prop) so screen readers can announce it,
ensuring you keep the existing onPress handler (handlePress) and memoization
intact; locate the Pressable inside RecordingRow and add the accessibilityRole
and accessibilityLabel attributes (or accept an accessibilityLabel prop on
RecordingRow and pass it through) so the button is accessible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70ef5882-2cd9-4dbb-aa2f-e820a4b37160

📥 Commits

Reviewing files that changed from the base of the PR and between 4e6691c and d0b2d1b.

📒 Files selected for processing (23)
  • packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx
  • packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx
  • packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx
  • packages/react-native-sdk/src/components/Call/CallParticipantsList/CallParticipantsList.tsx
  • packages/react-native-sdk/src/components/Call/Lobby/Lobby.tsx
  • packages/react-native-sdk/src/components/Livestream/LivestreamLayout/LivestreamLayout.tsx
  • packages/react-native-sdk/src/components/Livestream/LivestreamPlayer/LivestreamEnded.tsx
  • packages/react-native-sdk/src/components/Participant/FloatingParticipantView/index.tsx
  • packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantView.tsx
  • packages/react-native-sdk/src/components/Participant/ParticipantView/VideoRenderer/index.tsx
  • packages/react-native-sdk/src/hooks/useAutoEnterPiPEffect.tsx
  • packages/react-native-sdk/src/icons/LeaveStreamIcon.tsx
  • packages/react-native-sdk/src/icons/Settings.tsx
  • packages/react-native-sdk/src/icons/ShieldBadge.tsx
  • packages/react-native-sdk/src/icons/Spotlight.tsx
  • packages/react-native-sdk/src/icons/ThreeDots.tsx
  • packages/react-native-sdk/src/icons/index.tsx
  • packages/react-native-sdk/src/providers/BusyTonePlayer.tsx
  • packages/react-native-sdk/src/providers/StreamCall/AppStateListener.tsx
  • packages/react-native-sdk/src/providers/StreamCall/DeviceStats.tsx
  • packages/react-native-sdk/src/utils/internal/callingx/callingx.ts
  • sample-apps/react-native/dogfood/src/assets/TopViewBackground.tsx
  • sample-apps/react-native/dogfood/src/components/CallControlls/TopControls.tsx
💤 Files with no reviewable changes (6)
  • packages/react-native-sdk/src/icons/Spotlight.tsx
  • packages/react-native-sdk/src/icons/ShieldBadge.tsx
  • packages/react-native-sdk/src/icons/LeaveStreamIcon.tsx
  • packages/react-native-sdk/src/icons/ThreeDots.tsx
  • packages/react-native-sdk/src/icons/Settings.tsx
  • packages/react-native-sdk/src/icons/index.tsx

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.

very good catch 🫡

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