From f1bccb896e0e22b9c401f2d65db954368ac9caa5 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 17 Nov 2025 15:09:58 +0100 Subject: [PATCH] move ononone layout into CallViewModel --- src/room/InCallView.tsx | 34 ++++++------------- .../CallViewModel/CallViewModelTestUtils.ts | 1 + .../MatrixMemberMetadata.test.ts | 4 +++ .../remoteMembers/MatrixMemberMetadata.ts | 10 ++++-- src/utils/displayname.test.ts | 12 +++---- src/utils/test-viewmodel.ts | 1 + 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 1474fb81..a9b9cca2 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -262,6 +262,8 @@ export const InCallView: FC = ({ const earpieceMode = useBehavior(vm.earpieceMode$); const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$); const sharingScreen = useBehavior(vm.sharingScreen$); + const localUserIsAlone = useBehavior(vm.localUserIsAlone$); + const oneOnOneMember = useBehavior(vm.isOneOnOneWith$); const fatalCallError = useBehavior(vm.configError$); // Stop the rendering and throw for the error boundary @@ -300,24 +302,15 @@ export const InCallView: FC = ({ // Waiting UI overlay const waitingOverlay: JSX.Element | null = useMemo(() => { // No overlay if not in ringing state - if (callPickupState !== "ringing") return null; + if (callPickupState !== "ringing" || localUserIsAlone) return null; - // Use room state for other participants data (the one that we likely want to reach) - // TODO: this screams it wants to be a behavior in the vm. - const roomOthers = [ - ...matrixRoom.getMembersWithMembership("join"), - ...matrixRoom.getMembersWithMembership("invite"), - ].filter((m) => m.userId !== client.getUserId()); - // Yield if there are not other members in the room. - if (roomOthers.length === 0) return null; - - const otherMember = roomOthers.length > 0 ? roomOthers[0] : undefined; - const isOneOnOne = roomOthers.length === 1 && otherMember; - const text = isOneOnOne - ? `Waiting for ${otherMember.name ?? otherMember.userId} to join…` + const name = oneOnOneMember ? oneOnOneMember.userId : matrixRoom.roomId; + const id = oneOnOneMember ? oneOnOneMember.userId : matrixRoom.roomId; + const text = oneOnOneMember + ? `Waiting for ${name ?? oneOnOneMember.userId} to join…` : "Waiting for other participants…"; - const avatarMxc = isOneOnOne - ? (otherMember.getMxcAvatarUrl?.() ?? undefined) + const avatarMxc = oneOnOneMember + ? (oneOnOneMember.getMxcAvatarUrl?.() ?? undefined) : (matrixRoom.getMxcAvatarUrl() ?? undefined); return ( @@ -326,12 +319,7 @@ export const InCallView: FC = ({ className={classNames(overlayStyles.content, waitingStyles.content)} >
- +
{text} @@ -339,7 +327,7 @@ export const InCallView: FC = ({ ); - }, [callPickupState, client, matrixRoom]); + }, [callPickupState, localUserIsAlone, matrixRoom, oneOnOneMember]); // Ideally we could detect taps by listening for click events and checking // that the pointerType of the event is "touch", but this isn't yet supported diff --git a/src/state/CallViewModel/CallViewModelTestUtils.ts b/src/state/CallViewModel/CallViewModelTestUtils.ts index 81671971..fefc57a0 100644 --- a/src/state/CallViewModel/CallViewModelTestUtils.ts +++ b/src/state/CallViewModel/CallViewModelTestUtils.ts @@ -122,6 +122,7 @@ export function withCallViewModel( } })() as Partial as MatrixClient, getMembers: () => Array.from(roomMembers.values()), + getMembersWithMembership: () => Array.from(roomMembers.values()), }); const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$); const participantsSpy = vi diff --git a/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.test.ts b/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.test.ts index bc4d329c..d27fe4c9 100644 --- a/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.test.ts +++ b/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.test.ts @@ -58,6 +58,10 @@ describe("MatrixMemberMetadata", () => { const members = Array.from(fakeMembersMap.values()); return members; }), + getMembersWithMembership: vi.fn().mockImplementation(() => { + const members = Array.from(fakeMembersMap.values()); + return members; + }), } as unknown as MatrixRoom; }); diff --git a/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.ts b/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.ts index ad603708..e6a968f2 100644 --- a/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.ts +++ b/src/state/CallViewModel/remoteMembers/MatrixMemberMetadata.ts @@ -9,7 +9,10 @@ import { type RoomMember, RoomStateEvent } from "matrix-js-sdk"; import { combineLatest, fromEvent, map } from "rxjs"; import { type CallMembership } from "matrix-js-sdk/lib/matrixrtc"; import { logger as rootLogger } from "matrix-js-sdk/lib/logger"; -import { type Room as MatrixRoom } from "matrix-js-sdk/lib/matrix"; +import { + KnownMembership, + type Room as MatrixRoom, +} from "matrix-js-sdk/lib/matrix"; // eslint-disable-next-line rxjs/no-internal import { type ObservableScope } from "../../ObservableScope"; @@ -26,7 +29,10 @@ export type RoomMemberMap = Map< Pick >; export function roomToMembersMap(matrixRoom: MatrixRoom): RoomMemberMap { - return matrixRoom.getMembers().reduce((acc, member) => { + const members = matrixRoom + .getMembersWithMembership(KnownMembership.Join) + .concat(matrixRoom.getMembersWithMembership(KnownMembership.Invite)); + return members.reduce((acc, member) => { acc.set(member.userId, { userId: member.userId, getMxcAvatarUrl: member.getMxcAvatarUrl.bind(member), diff --git a/src/utils/displayname.test.ts b/src/utils/displayname.test.ts index 442b928a..fa732f19 100644 --- a/src/utils/displayname.test.ts +++ b/src/utils/displayname.test.ts @@ -25,7 +25,7 @@ import { roomToMembersMap } from "../state/CallViewModel/remoteMembers/MatrixMem describe("shouldDisambiguate", () => { test("should not disambiguate a solo member", () => { const room = mockMatrixRoom({ - getMembers: () => [], + getMembersWithMembership: () => [], }); expect(shouldDisambiguate(alice, [], roomToMembersMap(room))).toEqual( false, @@ -33,7 +33,7 @@ describe("shouldDisambiguate", () => { }); test("should not disambiguate a member with an empty displayname", () => { const room = mockMatrixRoom({ - getMembers: () => [alice, aliceDoppelganger], + getMembersWithMembership: () => [alice, aliceDoppelganger], }); expect( shouldDisambiguate( @@ -44,14 +44,14 @@ describe("shouldDisambiguate", () => { ).toEqual(false); }); test("should disambiguate a member with RTL characters", () => { - const room = mockMatrixRoom({ getMembers: () => [] }); + const room = mockMatrixRoom({ getMembersWithMembership: () => [] }); expect(shouldDisambiguate(daveRTL, [], roomToMembersMap(room))).toEqual( true, ); }); test("should disambiguate a member with a matching displayname", () => { const room = mockMatrixRoom({ - getMembers: () => [alice, aliceDoppelganger], + getMembersWithMembership: () => [alice, aliceDoppelganger], }); expect( shouldDisambiguate( @@ -70,7 +70,7 @@ describe("shouldDisambiguate", () => { }); test("should disambiguate a member with a matching displayname with hidden spaces", () => { const room = mockMatrixRoom({ - getMembers: () => [bob, bobZeroWidthSpace], + getMembersWithMembership: () => [bob, bobZeroWidthSpace], }); expect( shouldDisambiguate( @@ -91,7 +91,7 @@ describe("shouldDisambiguate", () => { "should disambiguate a member with a displayname containing a mxid-like string '%s'", (rawDisplayName) => { const room = mockMatrixRoom({ - getMembers: () => [alice, aliceDoppelganger], + getMembersWithMembership: () => [alice, aliceDoppelganger], }); expect( shouldDisambiguate( diff --git a/src/utils/test-viewmodel.ts b/src/utils/test-viewmodel.ts index 314b6aa9..ef3d276f 100644 --- a/src/utils/test-viewmodel.ts +++ b/src/utils/test-viewmodel.ts @@ -83,6 +83,7 @@ export function getBasicRTCSession( } as Partial as MatrixClient, getMember: (userId) => matrixRoomMembers.get(userId) ?? null, getMembers: () => Array.from(matrixRoomMembers.values()), + getMembersWithMembership: () => Array.from(matrixRoomMembers.values()), roomId: matrixRoomId, on: vitest .fn()