Skip to content

feat(layouts): overridable participant mirroring#2106

Merged
oliverlaz merged 2 commits intomainfrom
rn-mirror-override
Feb 3, 2026
Merged

feat(layouts): overridable participant mirroring#2106
oliverlaz merged 2 commits intomainfrom
rn-mirror-override

Conversation

@oliverlaz
Copy link
Member

@oliverlaz oliverlaz commented Feb 2, 2026

💡 Overview

Allows integrators to control the video mirroring for any participant on the call.

🎫 Ticket: https://linear.app/stream/issue/RN-341/overridable-participant-mirroring
📑 Docs: https://github.com/GetStream/docs-content/pull/969

Summary by CodeRabbit

Release Notes

  • New Features
    • Added configurable video mirroring support for participant views. A new mirror option allows conditional control over whether participant videos are horizontally flipped across all layout types including spotlight, grid, floating, and paginated views.

@changeset-bot
Copy link

changeset-bot bot commented Feb 2, 2026

⚠️ No Changeset found

Latest commit: 52fb0a6

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
Copy link
Contributor

coderabbitai bot commented Feb 2, 2026

Warning

Rate limit exceeded

@oliverlaz has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This pull request introduces a mirror prop throughout the React Native SDK component hierarchy to enable optional video mirroring control. The prop flows from configuration context through layout components to ParticipantView and VideoRenderer, allowing conditional mirroring of participant videos.

Changes

Cohort / File(s) Summary
SDK Component Prop Threading
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
Adds optional mirror prop to call layout components and threads it through to ParticipantView via participantViewProps and nested component hierarchies.
Participant View Component Chain
packages/react-native-sdk/src/components/Participant/FloatingParticipantView/index.tsx, packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantView.tsx
Extends ParticipantView and FloatingParticipantView to accept and forward mirror prop to VideoRenderer for video rendering control.
Video Renderer Implementation
packages/react-native-sdk/src/components/Participant/ParticipantView/VideoRenderer/index.tsx
Implements mirror prop handling with mirrorOverride pattern, preferring provided mirror value when defined, otherwise applying default logic based on participant type and screen sharing status.
Sample App Configuration & Integration
sample-apps/react/egress-composite/src/ConfigurationContext.tsx, sample-apps/react/egress-composite/src/components/layouts/DominantSpeaker/DominantSpeaker.tsx, sample-apps/react/egress-composite/src/components/layouts/DominantSpeaker/DominantSpeakerScreenShare.tsx, sample-apps/react/egress-composite/src/components/layouts/PaginatedGrid/PaginatedGrid.tsx, sample-apps/react/egress-composite/src/components/layouts/Spotlight/Spotlight.tsx
Adds layout.forceMirrorParticipants configuration option and integrates it across layout components to conditionally pass mirror or mirrorLocalParticipantVideo props to participant views.

Sequence Diagram(s)

sequenceDiagram
    participant Config as Configuration Context
    participant Layout as Layout Component
    participant ParticipantView as ParticipantView
    participant VideoRenderer as VideoRenderer

    Config->>Layout: Provide forceMirrorParticipants config
    Layout->>ParticipantView: Pass mirror prop (from config)
    ParticipantView->>VideoRenderer: Pass mirror prop
    VideoRenderer->>VideoRenderer: Compute RTCView.mirror<br/>(prefer mirrorOverride if defined)
    VideoRenderer->>VideoRenderer: Apply mirror setting to video
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A mirror prop hops through the trees,
From config down to all we see—
Each component passes with care,
The mirror setting everywhere!
Video reflects the speaker's face,
Control and grace in every place. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main feature: allowing overridable participant mirroring, which matches the core functionality added across multiple layout components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description includes the required overview and ticket reference, but is missing the implementation notes section specified in the template.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rn-mirror-override

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
packages/react-native-sdk/src/components/Call/CallParticipantsList/CallParticipantsList.tsx (1)

205-228: ⚠️ Potential issue | 🟡 Minor

Missing mirror in renderItem dependency array.

The mirror prop is used inside renderItem (line 219) but is not included in the dependency array (line 227). If mirror changes after initial render, the callback will use a stale value. While mirror is likely static from configuration in practice, consider adding it to the dependency array for correctness, or document why it's intentionally excluded.

🔧 Suggested fix
   const renderItem = useCallback<NonNullable<FlatListProps['renderItem']>>(
     ({ item: participant }) => {
       const isVisible = viewableParticipantSessionIds.current.has(
         participant.sessionId,
       );
       return (
         <>
           {ParticipantView && (
             <ParticipantView
               participant={participant}
               style={itemContainerStyle}
               trackType="videoTrack"
               isVisible={isVisible}
               supportedReactions={supportedReactions}
               mirror={mirror}
               {...participantProps}
             />
           )}
         </>
       );
     },
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-    [itemContainerStyle],
+    // eslint-disable-next-line react-hooks/exhaustive-deps -- participantProps intentionally excluded to avoid re-renders
+    [itemContainerStyle, mirror, supportedReactions, ParticipantView],
   );
🤖 Fix all issues with AI agents
In
`@packages/react-native-sdk/src/components/Participant/ParticipantView/VideoRenderer/index.tsx`:
- Around line 114-117: The mirroring logic in the const mirror calculation lets
mirrorOverride=true force mirroring even for screen shares; update the
expression in ParticipantView's VideoRenderer (the const named mirror that reads
mirrorOverride, isLocalParticipant, isScreenSharing, and direction) so that if
isScreenSharing is true it always results in false (no mirror), otherwise
preserve the existing behavior (use mirrorOverride when provided, else mirror
only for local camera with direction === 'front'). Ensure the check
short-circuits screen-share first before applying mirrorOverride.

@oliverlaz oliverlaz merged commit e1c5f31 into main Feb 3, 2026
19 checks passed
@oliverlaz oliverlaz deleted the rn-mirror-override branch February 3, 2026 08:16
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