From 13636b78d97f4e7c21f55e58c9e9193670498c35 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 14 Oct 2025 12:07:51 -0400 Subject: [PATCH] Replace deprecated CallMembership.sender with userId --- src/button/ReactionToggleButton.test.tsx | 2 +- src/home/useGroupCallRooms.ts | 4 +-- src/reactions/ReactionsReader.test.tsx | 38 ++++++++++++------------ src/reactions/ReactionsReader.ts | 18 +++++------ src/reactions/useReactionsSender.tsx | 2 +- src/room/CallEventAudioRenderer.test.tsx | 2 +- src/room/GroupCallView.test.tsx | 2 +- src/room/GroupCallView.tsx | 2 +- src/room/InCallView.test.tsx | 2 +- src/state/CallViewModel.test.ts | 4 +-- src/state/CallViewModel.ts | 22 +++++++------- src/state/Connection.test.ts | 16 +++++----- src/state/Connection.ts | 2 +- src/utils/displayname.ts | 2 +- src/utils/test-viewmodel.ts | 2 +- src/utils/test.ts | 4 +-- 16 files changed, 61 insertions(+), 63 deletions(-) diff --git a/src/button/ReactionToggleButton.test.tsx b/src/button/ReactionToggleButton.test.tsx index b1af7ec8..1ac43ebc 100644 --- a/src/button/ReactionToggleButton.test.tsx +++ b/src/button/ReactionToggleButton.test.tsx @@ -19,7 +19,7 @@ import { alice, local, localRtcMember } from "../utils/test-fixtures"; import { type MockRTCSession } from "../utils/test"; import { ReactionsSenderProvider } from "../reactions/useReactionsSender"; -const localIdent = `${localRtcMember.sender}:${localRtcMember.deviceId}`; +const localIdent = `${localRtcMember.userId}:${localRtcMember.deviceId}`; function TestComponent({ rtcSession, diff --git a/src/home/useGroupCallRooms.ts b/src/home/useGroupCallRooms.ts index ad69b864..8000e585 100644 --- a/src/home/useGroupCallRooms.ts +++ b/src/home/useGroupCallRooms.ts @@ -150,8 +150,8 @@ export function useGroupCallRooms(client: MatrixClient): GroupCallRoom[] { room, session, participants: session.memberships - .filter((m) => m.sender) - .map((m) => room.getMember(m.sender!)) + .filter((m) => m.userId) + .map((m) => room.getMember(m.userId!)) .filter((m) => m) as RoomMember[], }; }), diff --git a/src/reactions/ReactionsReader.test.tsx b/src/reactions/ReactionsReader.test.tsx index 01815c82..76ea45be 100644 --- a/src/reactions/ReactionsReader.test.tsx +++ b/src/reactions/ReactionsReader.test.tsx @@ -47,7 +47,7 @@ test("handles a hand raised reaction", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: EventType.Reaction, origin_server_ts: localTimestamp.getTime(), content: { @@ -67,7 +67,7 @@ test("handles a hand raised reaction", () => { expectObservable(raisedHands$).toBe("ab", { a: {}, b: { - [`${localRtcMember.sender}:${localRtcMember.deviceId}`]: { + [`${localRtcMember.userId}:${localRtcMember.deviceId}`]: { reactionEventId, membershipEventId: localRtcMember.eventId, time: localTimestamp, @@ -95,7 +95,7 @@ test("handles a redaction", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: EventType.Reaction, origin_server_ts: localTimestamp.getTime(), content: { @@ -117,7 +117,7 @@ test("handles a redaction", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: EventType.RoomRedaction, redacts: reactionEventId, }), @@ -129,7 +129,7 @@ test("handles a redaction", () => { expectObservable(raisedHands$).toBe("abc", { a: {}, b: { - [`${localRtcMember.sender}:${localRtcMember.deviceId}`]: { + [`${localRtcMember.userId}:${localRtcMember.deviceId}`]: { reactionEventId, membershipEventId: localRtcMember.eventId, time: localTimestamp, @@ -156,7 +156,7 @@ test("handles waiting for event decryption", () => { const encryptedEvent = new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: EventType.Reaction, origin_server_ts: localTimestamp.getTime(), content: { @@ -183,7 +183,7 @@ test("handles waiting for event decryption", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: EventType.Reaction, origin_server_ts: localTimestamp.getTime(), content: { @@ -199,7 +199,7 @@ test("handles waiting for event decryption", () => { expectObservable(raisedHands$).toBe("a-c", { a: {}, c: { - [`${localRtcMember.sender}:${localRtcMember.deviceId}`]: { + [`${localRtcMember.userId}:${localRtcMember.deviceId}`]: { reactionEventId, membershipEventId: localRtcMember.eventId, time: localTimestamp, @@ -227,7 +227,7 @@ test("hands rejecting events without a proper membership", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: EventType.Reaction, origin_server_ts: localTimestamp.getTime(), content: { @@ -270,7 +270,7 @@ test("handles a reaction", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: ElementCallReactionEventType, content: { emoji: reaction.emoji, @@ -295,7 +295,7 @@ test("handles a reaction", () => { { a: {}, b: { - [`${localRtcMember.sender}:${localRtcMember.deviceId}`]: { + [`${localRtcMember.userId}:${localRtcMember.deviceId}`]: { reactionOption: reaction, expireAfter: new Date(REACTION_ACTIVE_TIME_MS), }, @@ -327,7 +327,7 @@ test("ignores bad reaction events", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: ElementCallReactionEventType, content: {}, }), @@ -342,7 +342,7 @@ test("ignores bad reaction events", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: ElementCallReactionEventType, content: { emoji: reaction.emoji, @@ -363,7 +363,7 @@ test("ignores bad reaction events", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: aliceRtcMember.sender, + sender: aliceRtcMember.userId, type: ElementCallReactionEventType, content: { emoji: reaction.emoji, @@ -384,7 +384,7 @@ test("ignores bad reaction events", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: ElementCallReactionEventType, content: { name: reaction.name, @@ -404,7 +404,7 @@ test("ignores bad reaction events", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: ElementCallReactionEventType, content: { emoji: " ", @@ -448,7 +448,7 @@ test("that reactions cannot be spammed", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: ElementCallReactionEventType, content: { emoji: reactionA.emoji, @@ -470,7 +470,7 @@ test("that reactions cannot be spammed", () => { new MatrixEvent({ room_id: rtcSession.room.roomId, event_id: reactionEventId, - sender: localRtcMember.sender, + sender: localRtcMember.userId, type: ElementCallReactionEventType, content: { emoji: reactionB.emoji, @@ -495,7 +495,7 @@ test("that reactions cannot be spammed", () => { { a: {}, b: { - [`${localRtcMember.sender}:${localRtcMember.deviceId}`]: { + [`${localRtcMember.userId}:${localRtcMember.deviceId}`]: { reactionOption: reactionA, expireAfter: new Date(REACTION_ACTIVE_TIME_MS), }, diff --git a/src/reactions/ReactionsReader.ts b/src/reactions/ReactionsReader.ts index b630f4b9..9e30eff5 100644 --- a/src/reactions/ReactionsReader.ts +++ b/src/reactions/ReactionsReader.ts @@ -130,7 +130,7 @@ export class ReactionsReader { private onMembershipsChanged = (oldMemberships: CallMembership[]): void => { // Remove any raised hands for users no longer joined to the call. for (const identifier of Object.keys(this.raisedHandsSubject$.value).filter( - (rhId) => oldMemberships.find((u) => u.sender == rhId), + (rhId) => oldMemberships.find((u) => u.userId == rhId), )) { this.removeRaisedHand(identifier); } @@ -138,10 +138,10 @@ export class ReactionsReader { // For each member in the call, check to see if a reaction has // been raised and adjust. for (const m of this.rtcSession.memberships) { - if (!m.sender || !m.eventId) { + if (!m.userId || !m.eventId) { continue; } - const identifier = `${m.sender}:${m.deviceId}`; + const identifier = `${m.userId}:${m.deviceId}`; if ( this.raisedHandsSubject$.value[identifier] && this.raisedHandsSubject$.value[identifier].membershipEventId !== @@ -151,13 +151,13 @@ export class ReactionsReader { // was raised, reset. this.removeRaisedHand(identifier); } - const reaction = this.getLastReactionEvent(m.eventId, m.sender); + const reaction = this.getLastReactionEvent(m.eventId, m.userId); if (reaction) { const eventId = reaction?.getId(); if (!eventId) { continue; } - this.addRaisedHand(`${m.sender}:${m.deviceId}`, { + this.addRaisedHand(`${m.userId}:${m.deviceId}`, { membershipEventId: m.eventId, reactionEventId: eventId, time: new Date(reaction.localTimestamp), @@ -219,7 +219,7 @@ export class ReactionsReader { const membershipEventId = content?.["m.relates_to"]?.event_id; const membershipEvent = this.rtcSession.memberships.find( - (e) => e.eventId === membershipEventId && e.sender === sender, + (e) => e.eventId === membershipEventId && e.userId === sender, ); // Check to see if this reaction was made to a membership event (and the // sender of the reaction matches the membership) @@ -229,7 +229,7 @@ export class ReactionsReader { ); return; } - const identifier = `${membershipEvent.sender}:${membershipEvent.deviceId}`; + const identifier = `${membershipEvent.userId}:${membershipEvent.deviceId}`; if (!content.emoji) { logger.warn(`Reaction had no emoji from ${reactionEventId}`); @@ -278,7 +278,7 @@ export class ReactionsReader { // Check to see if this reaction was made to a membership event (and the // sender of the reaction matches the membership) const membershipEvent = this.rtcSession.memberships.find( - (e) => e.eventId === membershipEventId && e.sender === sender, + (e) => e.eventId === membershipEventId && e.userId === sender, ); if (!membershipEvent) { logger.warn( @@ -289,7 +289,7 @@ export class ReactionsReader { if (content?.["m.relates_to"].key === "🖐️") { this.addRaisedHand( - `${membershipEvent.sender}:${membershipEvent.deviceId}`, + `${membershipEvent.userId}:${membershipEvent.deviceId}`, { reactionEventId, membershipEventId, diff --git a/src/reactions/useReactionsSender.tsx b/src/reactions/useReactionsSender.tsx index 5f509a0c..ec29c2af 100644 --- a/src/reactions/useReactionsSender.tsx +++ b/src/reactions/useReactionsSender.tsx @@ -65,7 +65,7 @@ export const ReactionsSenderProvider = ({ const myMembershipEvent = useMemo( () => memberships.find( - (m) => m.sender === myUserId && m.deviceId === myDeviceId, + (m) => m.userId === myUserId && m.deviceId === myDeviceId, )?.eventId, [memberships, myUserId, myDeviceId], ); diff --git a/src/room/CallEventAudioRenderer.test.tsx b/src/room/CallEventAudioRenderer.test.tsx index e7d7e85a..bc1a1b39 100644 --- a/src/room/CallEventAudioRenderer.test.tsx +++ b/src/room/CallEventAudioRenderer.test.tsx @@ -156,7 +156,7 @@ test("plays one sound when a hand is raised", () => { act(() => { handRaisedSubject$.next({ // TODO: What is this string supposed to be? - [`${bobRtcMember.sender}:${bobRtcMember.deviceId}`]: { + [`${bobRtcMember.userId}:${bobRtcMember.deviceId}`]: { time: new Date(), membershipEventId: "", reactionEventId: "", diff --git a/src/room/GroupCallView.test.tsx b/src/room/GroupCallView.test.tsx index 37f5c850..a96a9c7d 100644 --- a/src/room/GroupCallView.test.tsx +++ b/src/room/GroupCallView.test.tsx @@ -122,7 +122,7 @@ function createGroupCallView( } { const client = { getUser: () => null, - getUserId: () => localRtcMember.sender, + getUserId: () => localRtcMember.userId, getDeviceId: () => localRtcMember.deviceId, getRoom: (rId) => (rId === roomId ? room : null), } as Partial as MatrixClient; diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 49d8b60b..d229ec17 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -211,7 +211,7 @@ export const GroupCallView: FC = ({ // Count each member only once, regardless of how many devices they use const participantCount = useMemo( - () => new Set(memberships.map((m) => m.sender!)).size, + () => new Set(memberships.map((m) => m.userId!)).size, [memberships], ); diff --git a/src/room/InCallView.test.tsx b/src/room/InCallView.test.tsx index 1caa3e3d..d75fd598 100644 --- a/src/room/InCallView.test.tsx +++ b/src/room/InCallView.test.tsx @@ -110,7 +110,7 @@ function createInCallView(): RenderResult & { } { const client = { getUser: () => null, - getUserId: () => localRtcMember.sender, + getUserId: () => localRtcMember.userId, getDeviceId: () => localRtcMember.deviceId, getRoom: (rId) => (rId === roomId ? room : null), } as Partial as MatrixClient; diff --git a/src/state/CallViewModel.test.ts b/src/state/CallViewModel.test.ts index 9fa619f1..52d13ca4 100644 --- a/src/state/CallViewModel.test.ts +++ b/src/state/CallViewModel.test.ts @@ -305,7 +305,7 @@ function withCallViewModel( const room = mockMatrixRoom({ client: new (class extends EventEmitter { public getUserId(): string | undefined { - return localRtcMember.sender; + return localRtcMember.userId; } public getDeviceId(): string { return localRtcMember.deviceId; @@ -1070,7 +1070,7 @@ it("should rank raised hands above video feeds and below speakers and presenters }, b: () => { raisedHands$.next({ - [`${bobRtcMember.sender}:${bobRtcMember.deviceId}`]: { + [`${bobRtcMember.userId}:${bobRtcMember.deviceId}`]: { time: new Date(), reactionEventId: "", membershipEventId: "", diff --git a/src/state/CallViewModel.ts b/src/state/CallViewModel.ts index a2a224f3..2bd83dfb 100644 --- a/src/state/CallViewModel.ts +++ b/src/state/CallViewModel.ts @@ -315,7 +315,7 @@ export class CallViewModel extends ViewModel { const oldestMembership = this.matrixRTCSession.getOldestMembership(); const remote = memberships.flatMap((m) => { - if (m.sender === this.userId && m.deviceId === this.deviceId) + if (m.userId === this.userId && m.deviceId === this.deviceId) return []; const t = m.getTransport(oldestMembership ?? m); return t && isLivekitTransport(t) @@ -664,7 +664,7 @@ export class CallViewModel extends ViewModel { | undefined; member: RoomMember; }[] = ps.map(({ participant, membership }) => ({ - id: `${membership.sender}:${membership.deviceId}`, + id: `${membership.userId}:${membership.deviceId}`, participant, member: getRoomMemberFromRtcMember( @@ -741,7 +741,7 @@ export class CallViewModel extends ViewModel { // We only consider RTC members for disambiguation as they are the only visible members. for (const rtcMember of memberships) { - const matrixIdentifier = `${rtcMember.sender}:${rtcMember.deviceId}`; + const matrixIdentifier = `${rtcMember.userId}:${rtcMember.deviceId}`; const { member } = getRoomMemberFromRtcMember(rtcMember, room); if (!member) { logger.error( @@ -894,8 +894,8 @@ export class CallViewModel extends ViewModel { pairwise(), filter( ([prev, current]) => - current.every((m) => m.sender === this.userId) && - prev.some((m) => m.sender !== this.userId), + current.every((m) => m.userId === this.userId) && + prev.some((m) => m.userId !== this.userId), ), map(() => {}), ); @@ -970,7 +970,7 @@ export class CallViewModel extends ViewModel { * Whether some Matrix user other than ourself is joined to the call. */ private readonly someoneElseJoined$ = this.memberships$.pipe( - map((ms) => ms.some((m) => m.sender !== this.userId)), + map((ms) => ms.some((m) => m.userId !== this.userId)), ) as Behavior; /** @@ -1971,20 +1971,18 @@ function getRoomMemberFromRtcMember( rtcMember: CallMembership, room: MatrixRoom, ): { id: string; member: RoomMember | undefined } { - // WARN! This is not exactly the sender but the user defined in the state key. - // This will be available once we change to the new "member as object" format in the MatrixRTC object. - let id = rtcMember.sender + ":" + rtcMember.deviceId; + let id = rtcMember.userId + ":" + rtcMember.deviceId; - if (!rtcMember.sender) { + if (!rtcMember.userId) { return { id, member: undefined }; } if ( - rtcMember.sender === room.client.getUserId() && + rtcMember.userId === room.client.getUserId() && rtcMember.deviceId === room.client.getDeviceId() ) { id = "local"; } - const member = room.getMember(rtcMember.sender) ?? undefined; + const member = room.getMember(rtcMember.userId) ?? undefined; return { id, member }; } diff --git a/src/state/Connection.test.ts b/src/state/Connection.test.ts index 41be4e39..c58fa60d 100644 --- a/src/state/Connection.test.ts +++ b/src/state/Connection.test.ts @@ -435,16 +435,16 @@ describe("Start connection states", () => { }); function fakeRemoteLivekitParticipant(id: string): RemoteParticipant { - return vi.mocked({ + return { identity: id, - } as unknown as RemoteParticipant); + } as unknown as RemoteParticipant; } function fakeRtcMemberShip(userId: string, deviceId: string): CallMembership { - return vi.mocked({ - sender: userId, - deviceId: deviceId, - } as unknown as CallMembership); + return { + userId, + deviceId, + } as unknown as CallMembership; } describe("Publishing participants observations", () => { @@ -570,7 +570,7 @@ describe("Publishing participants observations", () => { // he is using that focus to publish, so he should still appear as a publisher expect(updatedPublishers?.length).toEqual(2); const pp = updatedPublishers?.find( - (p) => p.membership.sender == "@bob:example.org", + (p) => p.membership.userId == "@bob:example.org", ); expect(pp).toBeDefined(); expect(pp!.participant).not.toBeDefined(); @@ -581,7 +581,7 @@ describe("Publishing participants observations", () => { ).toBeTruthy(); // Now if bob is not in the rtc memberships, he should disappear const noBob = rtcMemberships.filter( - ({ membership }) => membership.sender !== "@bob:example.org", + ({ membership }) => membership.userId !== "@bob:example.org", ); fakeMembershipsFocusMap$.next(noBob); expect(observedPublishers.pop()?.length).toEqual(1); diff --git a/src/state/Connection.ts b/src/state/Connection.ts index 2048bf53..bb18daaa 100644 --- a/src/state/Connection.ts +++ b/src/state/Connection.ts @@ -242,7 +242,7 @@ export class Connection { ) // Pair with their associated LiveKit participant (if any) .map((membership) => { - const id = `${membership.sender}:${membership.deviceId}`; + const id = `${membership.userId}:${membership.deviceId}`; const participant = participants.find((p) => p.identity === id); return { participant, membership }; }), diff --git a/src/utils/displayname.ts b/src/utils/displayname.ts index d2375897..8e989d3b 100644 --- a/src/utils/displayname.ts +++ b/src/utils/displayname.ts @@ -65,7 +65,7 @@ export function shouldDisambiguate( // displayname, after hidden character removal. return ( memberships - .map((m) => m.sender && room.getMember(m.sender)) + .map((m) => m.userId && room.getMember(m.userId)) // NOTE: We *should* have a room member for everyone. .filter((m) => !!m) .filter((m) => m.userId !== userId) diff --git a/src/utils/test-viewmodel.ts b/src/utils/test-viewmodel.ts index 1b4d0cf0..d6a4bb10 100644 --- a/src/utils/test-viewmodel.ts +++ b/src/utils/test-viewmodel.ts @@ -49,7 +49,7 @@ export function getBasicRTCSession( getChildEventsForEvent: vitest.fn(), } as Partial as RelationsContainer, client: { - getUserId: () => localRtcMember.sender, + getUserId: () => localRtcMember.userId, getDeviceId: () => localRtcMember.deviceId, getSyncState: () => SyncState.Syncing, sendEvent: vitest.fn().mockResolvedValue({ event_id: "$fake:event" }), diff --git a/src/utils/test.ts b/src/utils/test.ts index 4d8ecace..d50c1af4 100644 --- a/src/utils/test.ts +++ b/src/utils/test.ts @@ -210,11 +210,11 @@ export function mockMatrixRoomMember( ): RoomMember { return { ...mockEmitter(), - userId: rtcMembership.sender, + userId: rtcMembership.userId, getMxcAvatarUrl(): string | undefined { return undefined; }, - rawDisplayName: rtcMembership.sender, + rawDisplayName: rtcMembership.userId, ...member, } as RoomMember; }