From 444c50d8c4a37eb4908e5dce84e61cada4520b85 Mon Sep 17 00:00:00 2001 From: Timo K Date: Fri, 9 Jan 2026 18:27:56 +0100 Subject: [PATCH 1/3] Change code style of user media --- src/state/CallViewModel/CallViewModel.ts | 82 +++++++------------ .../remoteMembers/MatrixLivekitMembers.ts | 2 +- 2 files changed, 30 insertions(+), 54 deletions(-) diff --git a/src/state/CallViewModel/CallViewModel.ts b/src/state/CallViewModel/CallViewModel.ts index bf5ea441..28b098d2 100644 --- a/src/state/CallViewModel/CallViewModel.ts +++ b/src/state/CallViewModel/CallViewModel.ts @@ -124,9 +124,9 @@ import { } from "./remoteMembers/ConnectionManager.ts"; import { createMatrixLivekitMembers$, - type TaggedParticipant, type LocalMatrixLivekitMember, type RemoteMatrixLivekitMember, + type MatrixLivekitMember, } from "./remoteMembers/MatrixLivekitMembers.ts"; import { type AutoLeaveReason, @@ -717,62 +717,38 @@ export function createCallViewModel$( matrixLivekitMembers, duplicateTiles, ]) { - let localUserMediaId: string | undefined = undefined; - // add local member if available - if (localMatrixLivekitMember) { - const { userId, participant, connection$, membership$ } = - localMatrixLivekitMember; + const computeMediaId = (m: MatrixLivekitMember): string => + `${m.userId}:${m.membership$.value.deviceId}`; - localUserMediaId = `${userId}:${membership$.value.deviceId}`; + const localUserMediaId = localMatrixLivekitMember + ? computeMediaId(localMatrixLivekitMember) + : undefined; + + const localAsArray = localMatrixLivekitMember + ? [localMatrixLivekitMember] + : []; + const remoteWithoutLocal = matrixLivekitMembers.value.filter( + (m) => computeMediaId(m) !== localUserMediaId, + ); + const allMatrixLivekitMembers = [ + ...localAsArray, + ...remoteWithoutLocal, + ]; + + for (const matrixLivekitMember of allMatrixLivekitMembers) { + const { userId, participant, connection$, membership$ } = + matrixLivekitMember; + const memb = membership$.value; + const mediaId = computeMediaId(matrixLivekitMember); for (let dup = 0; dup < 1 + duplicateTiles; dup++) { yield { - keys: [ - dup, - localUserMediaId, - userId, - participant satisfies TaggedParticipant as TaggedParticipant, // Widen the type safely - connection$, - membership$.value, - ], - data: undefined, - }; - } - } - // add remote members that are available - for (const { - userId, - participant, - connection$, - membership$, - } of matrixLivekitMembers.value) { - const userMediaId = `${userId}:${membership$.value.deviceId}`; - // skip local user as we added them manually before - if (userMediaId === localUserMediaId) continue; - for (let dup = 0; dup < 1 + duplicateTiles; dup++) { - yield { - keys: [ - dup, - userMediaId, - userId, - participant, - connection$, - membership$.value, - ], + keys: [dup, mediaId, userId, participant, connection$, memb], data: undefined, }; } } }, - ( - scope, - _data$, - dup, - userMediaId, - userId, - participant, - connection$, - membership, - ) => { + (scope, _, dup, mediaId, userId, participant, connection$, memb) => { const livekitRoom$ = scope.behavior( connection$.pipe(map((c) => c?.livekitRoom)), ); @@ -787,9 +763,9 @@ export function createCallViewModel$( return new UserMedia( scope, - `${userMediaId}:${dup}`, + `${mediaId}:${dup}`, userId, - membership, + memb, participant, options.encryptionSystem, livekitRoom$, @@ -798,8 +774,8 @@ export function createCallViewModel$( localMembership.reconnecting$, displayName$, matrixMemberMetadataStore.createAvatarUrlBehavior$(userId), - handsRaised$.pipe(map((v) => v[userMediaId]?.time ?? null)), - reactions$.pipe(map((v) => v[userMediaId] ?? undefined)), + handsRaised$.pipe(map((v) => v[mediaId]?.time ?? null)), + reactions$.pipe(map((v) => v[mediaId] ?? undefined)), ); }, ), diff --git a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts index 10a3e2cb..db52ce2e 100644 --- a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts +++ b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts @@ -33,7 +33,7 @@ export type TaggedParticipant = | LocalTaggedParticipant | RemoteTaggedParticipant; -interface MatrixLivekitMember { +export interface MatrixLivekitMember { membership$: Behavior; connection$: Behavior; // participantId: string; We do not want a participantId here since it will be generated by the jwt From 7a8f5cc8596afffed6fdcced4dbd540c1b9e0f4e Mon Sep 17 00:00:00 2001 From: Timo K Date: Fri, 9 Jan 2026 19:40:21 +0100 Subject: [PATCH 2/3] use `crypto.randomUUID()` --- src/state/CallViewModel/CallViewModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state/CallViewModel/CallViewModel.ts b/src/state/CallViewModel/CallViewModel.ts index bf3e9521..aea0f7fa 100644 --- a/src/state/CallViewModel/CallViewModel.ts +++ b/src/state/CallViewModel/CallViewModel.ts @@ -47,7 +47,7 @@ import { } from "matrix-js-sdk/lib/matrixrtc"; import { type IWidgetApiRequest } from "matrix-widget-api"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { LocalUserMediaViewModel, @@ -432,7 +432,7 @@ export function createCallViewModel$( userId, deviceId, // This will only be consumed by the sticky membership manager. So it has no impact on legacy calls. - memberId: uuidv4(), + memberId: randomUUID(), }; const localTransport$ = createLocalTransport$({ From 89e5e5b10aaa4c4679e8b76e2eb74e32eb8ac3e6 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 12 Jan 2026 12:34:50 +0100 Subject: [PATCH 3/3] fix uuid --- src/state/CallViewModel/CallViewModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state/CallViewModel/CallViewModel.ts b/src/state/CallViewModel/CallViewModel.ts index aea0f7fa..bf3e9521 100644 --- a/src/state/CallViewModel/CallViewModel.ts +++ b/src/state/CallViewModel/CallViewModel.ts @@ -47,7 +47,7 @@ import { } from "matrix-js-sdk/lib/matrixrtc"; import { type IWidgetApiRequest } from "matrix-widget-api"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; -import { randomUUID } from "crypto"; +import { v4 as uuidv4 } from "uuid"; import { LocalUserMediaViewModel, @@ -432,7 +432,7 @@ export function createCallViewModel$( userId, deviceId, // This will only be consumed by the sticky membership manager. So it has no impact on legacy calls. - memberId: randomUUID(), + memberId: uuidv4(), }; const localTransport$ = createLocalTransport$({