From c96e81bfd375262be9c992cbd1885c7d878f5b8d Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 8 Oct 2025 16:40:06 -0400 Subject: [PATCH] Simplify type of audio participants exposed from CallViewModel --- src/livekit/MatrixAudioRenderer.test.tsx | 21 ++++-------------- src/livekit/MatrixAudioRenderer.tsx | 27 ++++++++---------------- src/room/InCallView.tsx | 4 ++-- src/state/CallViewModel.ts | 23 ++++++++++++++++++-- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/livekit/MatrixAudioRenderer.test.tsx b/src/livekit/MatrixAudioRenderer.test.tsx index c1ee6f83..9519ccc2 100644 --- a/src/livekit/MatrixAudioRenderer.test.tsx +++ b/src/livekit/MatrixAudioRenderer.test.tsx @@ -23,12 +23,7 @@ import { useTracks } from "@livekit/components-react"; import { testAudioContext } from "../useAudioContext.test"; import * as MediaDevicesContext from "../MediaDevicesContext"; import { LivekitRoomAudioRenderer } from "./MatrixAudioRenderer"; -import { - mockMatrixRoomMember, - mockMediaDevices, - mockRtcMembership, - mockTrack, -} from "../utils/test"; +import { mockMediaDevices, mockTrack } from "../utils/test"; export const TestAudioContextConstructor = vi.fn(() => testAudioContext); @@ -80,17 +75,11 @@ function renderTestComponent( isLocal, } as unknown as RemoteParticipant); }); - const participants = rtcMembers.map(({ userId, deviceId }) => { + const participants = rtcMembers.flatMap(({ userId, deviceId }) => { const p = liveKitParticipants.find( (p) => p.identity === `${userId}:${deviceId}`, ); - const localRtcMember = mockRtcMembership(userId, deviceId); - const member = mockMatrixRoomMember(localRtcMember); - return { - id: `${userId}:${deviceId}`, - participant: p, - member, - }; + return p === undefined ? [] : [p]; }); const livekitRoom = vi.mocked({ remoteParticipants: new Map( @@ -98,9 +87,7 @@ function renderTestComponent( ), } as unknown as Room); - tracks = participants - .filter((p) => p.participant) - .map((p) => mockTrack(p.participant!)) as TrackReference[]; + tracks = participants.map((p) => mockTrack(p)); vi.mocked(useTracks).mockReturnValue(tracks); return render( diff --git a/src/livekit/MatrixAudioRenderer.tsx b/src/livekit/MatrixAudioRenderer.tsx index 24455f70..fb1400b4 100644 --- a/src/livekit/MatrixAudioRenderer.tsx +++ b/src/livekit/MatrixAudioRenderer.tsx @@ -6,7 +6,10 @@ Please see LICENSE in the repository root for full details. */ import { getTrackReferenceId } from "@livekit/components-core"; -import { type Room as LivekitRoom, type Participant } from "livekit-client"; +import { + type RemoteParticipant, + type Room as LivekitRoom, +} from "livekit-client"; import { type RemoteAudioTrack, Track } from "livekit-client"; import { useEffect, useMemo, useRef, useState, type ReactNode } from "react"; import { @@ -14,13 +17,13 @@ import { AudioTrack, type AudioTrackProps, } from "@livekit/components-react"; -import { type RoomMember } from "matrix-js-sdk"; import { logger } from "matrix-js-sdk/lib/logger"; import { useEarpieceAudioConfig } from "../MediaDevicesContext"; import { useReactiveState } from "../useReactiveState"; import * as controls from "../controls"; import {} from "@livekit/components-core"; + export interface MatrixAudioRendererProps { /** * The service URL of the LiveKit room. @@ -32,13 +35,7 @@ export interface MatrixAudioRendererProps { * This list needs to be composed based on the matrixRTC members so that we do not play audio from users * that are not expected to be in the rtc session. */ - // TODO: Why do we have this structure? looks like we only need the valid/active participants (not the room member or id)? - participants: { - id: string; - // TODO it appears to be optional as per InCallView? but what does that mean here? a rtc member not yet joined in livekit? - participant: Participant | undefined; - member: RoomMember; - }[]; + participants: RemoteParticipant[]; /** * If set to `true`, mutes all audio tracks rendered by the component. * @remarks @@ -48,8 +45,7 @@ export interface MatrixAudioRendererProps { } /** - * The `MatrixAudioRenderer` component is a drop-in solution for adding audio to your LiveKit app. - * It takes care of handling remote participants’ audio tracks and makes sure that microphones and screen share are audible. + * Takes care of handling remote participants’ audio tracks and makes sure that microphones and screen share are audible. * * It also takes care of the earpiece audio configuration for iOS devices. * This is done by using the WebAudio API to create a stereo pan effect that mimics the earpiece audio. @@ -70,12 +66,7 @@ export function LivekitRoomAudioRenderer({ // This is the list of valid identities that are allowed to play audio. // It is derived from the list of matrix rtc members. const validIdentities = useMemo( - () => - new Set( - participants - .filter(({ participant }) => participant) // filter out participants that are not yet joined in livekit - .map(({ participant }) => participant!.identity), - ), + () => new Set(participants.map((p) => p.identity)), [participants], ); @@ -92,7 +83,7 @@ export function LivekitRoomAudioRenderer({ if (loggedInvalidIdentities.current.has(identity)) return; logger.warn( `[MatrixAudioRenderer] Audio track ${identity} from ${url} has no matching matrix call member`, - `current members: ${participants.map((p) => p.participant?.identity)}`, + `current members: ${participants.map((p) => p.identity)}`, `track will not get rendered`, ); loggedInvalidIdentities.current.add(identity); diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 8474c2fd..dacb7eb1 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -286,7 +286,7 @@ export const InCallView: FC = ({ ); const allLivekitRooms = useBehavior(vm.allLivekitRooms$); - const participantsByRoom = useBehavior(vm.participantsByRoom$); + const audioParticipants = useBehavior(vm.audioParticipants$); const participantCount = useBehavior(vm.participantCount$); const reconnecting = useBehavior(vm.reconnecting$); const windowMode = useBehavior(vm.windowMode$); @@ -860,7 +860,7 @@ export const InCallView: FC = ({ ) } - {participantsByRoom.map(({ livekitRoom, url, participants }) => ( + {audioParticipants.map(({ livekitRoom, url, participants }) => ( + data.map(({ livekitRoom, url, participants }) => ({ + livekitRoom, + url, + participants: participants.flatMap(({ participant }) => + participant instanceof RemoteParticipant ? [participant] : [], + ), + })), + ), + ), + ); + /** * Displaynames for each member of the call. This will disambiguate * any displaynames that clashes with another member. Only members