feat(react-sdk): show blocked audio overlay per participant#2341
feat(react-sdk): show blocked audio overlay per participant#2341jdimovska wants to merge 2 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughBlocked audio tracking now retains participant session IDs, exposes them through React bindings, and renders a configurable participant-view notification with resume behavior. Audio lifecycle cleanup and shared shallow-array comparison utilities are also updated. ChangesBlocked audio experience
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant DynascaleManager
participant BlockedAudioTracker
participant ReactHook
participant ParticipantView
DynascaleManager->>BlockedAudioTracker: markBlocked(audioElement, true, sessionId)
BlockedAudioTracker-->>ReactHook: emit blockedSessionIds$
ReactHook-->>ParticipantView: provide blocked session IDs
ParticipantView->>ParticipantView: render audio blocked notification
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Bundle sizeBuilt package output. Sizes in KB; delta vs
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
packages/client/src/store/CallState.ts (1)
424-427: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winMulticast
blockedUserIds$.Add
shareReplay({ bufferSize: 1, refCount: true })afterduc(...)so subscribers share the derived stream and cached value.Proposed fix
- this.blockedUserIds$ = duc( - this.blockedUserIdsSubject, - RxUtils.isShallowArrayEqual, - ); + this.blockedUserIds$ = duc( + this.blockedUserIdsSubject, + RxUtils.isShallowArrayEqual, + ).pipe(shareReplay({ bufferSize: 1, refCount: true }));As per coding guidelines, “For RxJS observables, use shareReplay(1) for multicast, distinctUntilChanged() to prevent unnecessary re-renders, and cache computed derived observables.”
🤖 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/client/src/store/CallState.ts` around lines 424 - 427, Update the blockedUserIds$ initialization in CallState to pipe the duc(...) result through shareReplay with bufferSize 1 and refCount true, ensuring subscribers share the derived stream and receive its cached latest value. Reuse the existing RxJS import conventions.Source: Coding guidelines
packages/react-sdk/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx (1)
48-54: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winHandle element overrides before invoking them as components.
The prop only accepts
ComponentType | null, and the render path always uses<AudioBlockedNotification />. This prevents ReactElement overrides and does not follow the SDK override contract. AcceptComponentType | ReactElement | null, useisComponentType(), and render either the component or element accordingly.As per coding guidelines: “Handle optional component override props with defaults. Check if prop is ComponentType using
isComponentType()before rendering, otherwise render as ReactElement or null.”Also applies to: 162-164
🤖 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-sdk/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx` around lines 48 - 54, The AudioBlockedNotification override contract and render path must support both component types and React elements. Update the AudioBlockedNotification prop type, use isComponentType() before rendering it, and render the component when true or the provided ReactElement otherwise, while preserving null/default behavior.Source: Coding guidelines
🤖 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.
Inline comments:
In `@packages/client/src/store/rxUtils.ts`:
- Around line 17-25: Update isShallowArrayEqual to compare element occurrence
counts rather than only membership, so arrays with different duplicate
frequencies are unequal while preserving order-independent shallow equality. Add
a regression test covering arrays such as [a, a, b] and [a, b, b], verifying
distinctUntilChanged does not suppress the update.
In
`@packages/react-sdk/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx`:
- Around line 90-103: Update DefaultAudioBlockedNotification to validate that
useCall() returns a call context before rendering the button; throw a clear
error when it is rendered outside StreamCall, and keep call.resumeAudio as the
click handler only after the context is confirmed.
---
Nitpick comments:
In `@packages/client/src/store/CallState.ts`:
- Around line 424-427: Update the blockedUserIds$ initialization in CallState to
pipe the duc(...) result through shareReplay with bufferSize 1 and refCount
true, ensuring subscribers share the derived stream and receive its cached
latest value. Reuse the existing RxJS import conventions.
In
`@packages/react-sdk/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx`:
- Around line 48-54: The AudioBlockedNotification override contract and render
path must support both component types and React elements. Update the
AudioBlockedNotification prop type, use isComponentType() before rendering it,
and render the component when true or the provided ReactElement otherwise, while
preserving null/default behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: cfd678f7-62c5-4379-98ea-e3ca43e902a3
📒 Files selected for processing (13)
packages/client/src/Call.tspackages/client/src/helpers/BlockedAudioTracker.tspackages/client/src/helpers/DynascaleManager.tspackages/client/src/helpers/__tests__/BlockedAudioTracker.test.tspackages/client/src/store/CallState.tspackages/client/src/store/rxUtils.tspackages/react-bindings/src/hooks/callStateHooks.tspackages/react-sdk/src/core/components/ParticipantView/DefaultParticipantViewUI.tsxpackages/react-sdk/src/translations/en.jsonpackages/styling/index.scsspackages/styling/src/AudioBlockedNotification/AudioBlockedNotification-layout.scsspackages/styling/src/AudioBlockedNotification/AudioBlockedNotification-theme.scsspackages/styling/src/AudioBlockedNotification/index.scss
💡 Overview
This PR adds a blocked-audio overlay to the default React participant UI. When the browser blocks playback for a remote participant’s audio, the affected participant tile shows an overlay that lets the user tap/click to resume call audio. This mainly addresses WebKit/Safari autoplay behavior. WebKit allows audible playback when there is an active transient user gesture, but that activation is short-lived. In current WebKit source, the default transient activation duration is
5s. If remote audio arrives, or is reattached after mute/unmute, outside that activation window,audioElement.play()can reject withNotAllowedError.📝 Implementation notes
The overlay can be customized through
DefaultParticipantViewUIvia theAudioBlockedNotificationprop, or disabled withAudioBlockedNotification={null}.🎫 Ticket: https://linear.app/stream/issue/REACT-1045/display-blocked-audio-per-participant
📑 Docs: https://github.com/GetStream/docs-content/pull/1449
Summary by CodeRabbit
New Features
Bug Fixes