mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-29 21:15:19 +00:00
move ononone layout into CallViewModel
This commit is contained in:
@@ -262,6 +262,8 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
const earpieceMode = useBehavior(vm.earpieceMode$);
|
const earpieceMode = useBehavior(vm.earpieceMode$);
|
||||||
const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$);
|
const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$);
|
||||||
const sharingScreen = useBehavior(vm.sharingScreen$);
|
const sharingScreen = useBehavior(vm.sharingScreen$);
|
||||||
|
const localUserIsAlone = useBehavior(vm.localUserIsAlone$);
|
||||||
|
const oneOnOneMember = useBehavior(vm.isOneOnOneWith$);
|
||||||
|
|
||||||
const fatalCallError = useBehavior(vm.configError$);
|
const fatalCallError = useBehavior(vm.configError$);
|
||||||
// Stop the rendering and throw for the error boundary
|
// Stop the rendering and throw for the error boundary
|
||||||
@@ -300,24 +302,15 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
// Waiting UI overlay
|
// Waiting UI overlay
|
||||||
const waitingOverlay: JSX.Element | null = useMemo(() => {
|
const waitingOverlay: JSX.Element | null = useMemo(() => {
|
||||||
// No overlay if not in ringing state
|
// 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)
|
const name = oneOnOneMember ? oneOnOneMember.userId : matrixRoom.roomId;
|
||||||
// TODO: this screams it wants to be a behavior in the vm.
|
const id = oneOnOneMember ? oneOnOneMember.userId : matrixRoom.roomId;
|
||||||
const roomOthers = [
|
const text = oneOnOneMember
|
||||||
...matrixRoom.getMembersWithMembership("join"),
|
? `Waiting for ${name ?? oneOnOneMember.userId} to 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…`
|
|
||||||
: "Waiting for other participants…";
|
: "Waiting for other participants…";
|
||||||
const avatarMxc = isOneOnOne
|
const avatarMxc = oneOnOneMember
|
||||||
? (otherMember.getMxcAvatarUrl?.() ?? undefined)
|
? (oneOnOneMember.getMxcAvatarUrl?.() ?? undefined)
|
||||||
: (matrixRoom.getMxcAvatarUrl() ?? undefined);
|
: (matrixRoom.getMxcAvatarUrl() ?? undefined);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -326,12 +319,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
className={classNames(overlayStyles.content, waitingStyles.content)}
|
className={classNames(overlayStyles.content, waitingStyles.content)}
|
||||||
>
|
>
|
||||||
<div className={waitingStyles.pulse}>
|
<div className={waitingStyles.pulse}>
|
||||||
<Avatar
|
<Avatar id={id} name={name} src={avatarMxc} size={AvatarSize.XL} />
|
||||||
id={isOneOnOne ? otherMember.userId : matrixRoom.roomId}
|
|
||||||
name={isOneOnOne ? otherMember.name : matrixRoom.name}
|
|
||||||
src={avatarMxc}
|
|
||||||
size={AvatarSize.XL}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<Text size="md" className={waitingStyles.text}>
|
<Text size="md" className={waitingStyles.text}>
|
||||||
{text}
|
{text}
|
||||||
@@ -339,7 +327,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [callPickupState, client, matrixRoom]);
|
}, [callPickupState, localUserIsAlone, matrixRoom, oneOnOneMember]);
|
||||||
|
|
||||||
// Ideally we could detect taps by listening for click events and checking
|
// 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
|
// that the pointerType of the event is "touch", but this isn't yet supported
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ export function withCallViewModel(
|
|||||||
}
|
}
|
||||||
})() as Partial<MatrixClient> as MatrixClient,
|
})() as Partial<MatrixClient> as MatrixClient,
|
||||||
getMembers: () => Array.from(roomMembers.values()),
|
getMembers: () => Array.from(roomMembers.values()),
|
||||||
|
getMembersWithMembership: () => Array.from(roomMembers.values()),
|
||||||
});
|
});
|
||||||
const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$);
|
const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$);
|
||||||
const participantsSpy = vi
|
const participantsSpy = vi
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ describe("MatrixMemberMetadata", () => {
|
|||||||
const members = Array.from(fakeMembersMap.values());
|
const members = Array.from(fakeMembersMap.values());
|
||||||
return members;
|
return members;
|
||||||
}),
|
}),
|
||||||
|
getMembersWithMembership: vi.fn().mockImplementation(() => {
|
||||||
|
const members = Array.from(fakeMembersMap.values());
|
||||||
|
return members;
|
||||||
|
}),
|
||||||
} as unknown as MatrixRoom;
|
} as unknown as MatrixRoom;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import { type RoomMember, RoomStateEvent } from "matrix-js-sdk";
|
|||||||
import { combineLatest, fromEvent, map } from "rxjs";
|
import { combineLatest, fromEvent, map } from "rxjs";
|
||||||
import { type CallMembership } from "matrix-js-sdk/lib/matrixrtc";
|
import { type CallMembership } from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
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
|
// eslint-disable-next-line rxjs/no-internal
|
||||||
|
|
||||||
import { type ObservableScope } from "../../ObservableScope";
|
import { type ObservableScope } from "../../ObservableScope";
|
||||||
@@ -26,7 +29,10 @@ export type RoomMemberMap = Map<
|
|||||||
Pick<RoomMember, "userId" | "getMxcAvatarUrl" | "rawDisplayName">
|
Pick<RoomMember, "userId" | "getMxcAvatarUrl" | "rawDisplayName">
|
||||||
>;
|
>;
|
||||||
export function roomToMembersMap(matrixRoom: MatrixRoom): RoomMemberMap {
|
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, {
|
acc.set(member.userId, {
|
||||||
userId: member.userId,
|
userId: member.userId,
|
||||||
getMxcAvatarUrl: member.getMxcAvatarUrl.bind(member),
|
getMxcAvatarUrl: member.getMxcAvatarUrl.bind(member),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { roomToMembersMap } from "../state/CallViewModel/remoteMembers/MatrixMem
|
|||||||
describe("shouldDisambiguate", () => {
|
describe("shouldDisambiguate", () => {
|
||||||
test("should not disambiguate a solo member", () => {
|
test("should not disambiguate a solo member", () => {
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
getMembers: () => [],
|
getMembersWithMembership: () => [],
|
||||||
});
|
});
|
||||||
expect(shouldDisambiguate(alice, [], roomToMembersMap(room))).toEqual(
|
expect(shouldDisambiguate(alice, [], roomToMembersMap(room))).toEqual(
|
||||||
false,
|
false,
|
||||||
@@ -33,7 +33,7 @@ describe("shouldDisambiguate", () => {
|
|||||||
});
|
});
|
||||||
test("should not disambiguate a member with an empty displayname", () => {
|
test("should not disambiguate a member with an empty displayname", () => {
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
getMembers: () => [alice, aliceDoppelganger],
|
getMembersWithMembership: () => [alice, aliceDoppelganger],
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
shouldDisambiguate(
|
shouldDisambiguate(
|
||||||
@@ -44,14 +44,14 @@ describe("shouldDisambiguate", () => {
|
|||||||
).toEqual(false);
|
).toEqual(false);
|
||||||
});
|
});
|
||||||
test("should disambiguate a member with RTL characters", () => {
|
test("should disambiguate a member with RTL characters", () => {
|
||||||
const room = mockMatrixRoom({ getMembers: () => [] });
|
const room = mockMatrixRoom({ getMembersWithMembership: () => [] });
|
||||||
expect(shouldDisambiguate(daveRTL, [], roomToMembersMap(room))).toEqual(
|
expect(shouldDisambiguate(daveRTL, [], roomToMembersMap(room))).toEqual(
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
test("should disambiguate a member with a matching displayname", () => {
|
test("should disambiguate a member with a matching displayname", () => {
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
getMembers: () => [alice, aliceDoppelganger],
|
getMembersWithMembership: () => [alice, aliceDoppelganger],
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
shouldDisambiguate(
|
shouldDisambiguate(
|
||||||
@@ -70,7 +70,7 @@ describe("shouldDisambiguate", () => {
|
|||||||
});
|
});
|
||||||
test("should disambiguate a member with a matching displayname with hidden spaces", () => {
|
test("should disambiguate a member with a matching displayname with hidden spaces", () => {
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
getMembers: () => [bob, bobZeroWidthSpace],
|
getMembersWithMembership: () => [bob, bobZeroWidthSpace],
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
shouldDisambiguate(
|
shouldDisambiguate(
|
||||||
@@ -91,7 +91,7 @@ describe("shouldDisambiguate", () => {
|
|||||||
"should disambiguate a member with a displayname containing a mxid-like string '%s'",
|
"should disambiguate a member with a displayname containing a mxid-like string '%s'",
|
||||||
(rawDisplayName) => {
|
(rawDisplayName) => {
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
getMembers: () => [alice, aliceDoppelganger],
|
getMembersWithMembership: () => [alice, aliceDoppelganger],
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
shouldDisambiguate(
|
shouldDisambiguate(
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ export function getBasicRTCSession(
|
|||||||
} as Partial<MatrixClient> as MatrixClient,
|
} as Partial<MatrixClient> as MatrixClient,
|
||||||
getMember: (userId) => matrixRoomMembers.get(userId) ?? null,
|
getMember: (userId) => matrixRoomMembers.get(userId) ?? null,
|
||||||
getMembers: () => Array.from(matrixRoomMembers.values()),
|
getMembers: () => Array.from(matrixRoomMembers.values()),
|
||||||
|
getMembersWithMembership: () => Array.from(matrixRoomMembers.values()),
|
||||||
roomId: matrixRoomId,
|
roomId: matrixRoomId,
|
||||||
on: vitest
|
on: vitest
|
||||||
.fn()
|
.fn()
|
||||||
|
|||||||
Reference in New Issue
Block a user