mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-23 20:59:21 +00:00
Compare commits
3 Commits
toger5/cre
...
valere/ref
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9420b605d8 | ||
|
|
8730249fb0 | ||
|
|
5edd30a91d |
@@ -59,8 +59,7 @@ import { type MatrixInfo } from "./VideoPreview";
|
||||
import { InviteButton } from "../button/InviteButton";
|
||||
import { LayoutToggle } from "./LayoutToggle";
|
||||
import {
|
||||
type CallViewModel,
|
||||
createCallViewModel$,
|
||||
CallViewModel,
|
||||
type GridMode,
|
||||
} from "../state/CallViewModel/CallViewModel.ts";
|
||||
import { Grid, type TileProps } from "../grid/Grid";
|
||||
@@ -129,7 +128,7 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
||||
const reactionsReader = new ReactionsReader(scope, props.rtcSession);
|
||||
const { autoLeaveWhenOthersLeft, waitForCallPickup, sendNotificationType } =
|
||||
urlParams;
|
||||
const vm = createCallViewModel$(
|
||||
const vm = new CallViewModel(
|
||||
scope,
|
||||
props.rtcSession,
|
||||
props.matrixRoom,
|
||||
@@ -249,6 +248,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
() => void toggleRaisedHand(),
|
||||
);
|
||||
|
||||
// const allLivekitRooms = useBehavior(vm.allLivekitRooms$);
|
||||
const audioParticipants = useBehavior(vm.audioParticipants$);
|
||||
const participantCount = useBehavior(vm.participantCount$);
|
||||
const reconnecting = useBehavior(vm.reconnecting$);
|
||||
@@ -263,7 +263,6 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$);
|
||||
const sharingScreen = useBehavior(vm.sharingScreen$);
|
||||
|
||||
const ringOverlay = useBehavior(vm.ringOverlay$);
|
||||
const fatalCallError = useBehavior(vm.configError$);
|
||||
// Stop the rendering and throw for the error boundary
|
||||
if (fatalCallError) throw fatalCallError;
|
||||
@@ -300,26 +299,47 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
|
||||
// Waiting UI overlay
|
||||
const waitingOverlay: JSX.Element | null = useMemo(() => {
|
||||
return ringOverlay ? (
|
||||
// No overlay if not in ringing state
|
||||
if (callPickupState !== "ringing") 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…`
|
||||
: "Waiting for other participants…";
|
||||
const avatarMxc = isOneOnOne
|
||||
? (otherMember.getMxcAvatarUrl?.() ?? undefined)
|
||||
: (matrixRoom.getMxcAvatarUrl() ?? undefined);
|
||||
|
||||
return (
|
||||
<div className={classNames(overlayStyles.bg, waitingStyles.overlay)}>
|
||||
<div
|
||||
className={classNames(overlayStyles.content, waitingStyles.content)}
|
||||
>
|
||||
<div className={waitingStyles.pulse}>
|
||||
<Avatar
|
||||
id={ringOverlay.idForAvatar}
|
||||
name={ringOverlay.name}
|
||||
src={ringOverlay.avatarMxc}
|
||||
id={isOneOnOne ? otherMember.userId : matrixRoom.roomId}
|
||||
name={isOneOnOne ? otherMember.name : matrixRoom.name}
|
||||
src={avatarMxc}
|
||||
size={AvatarSize.XL}
|
||||
/>
|
||||
</div>
|
||||
<Text size="md" className={waitingStyles.text}>
|
||||
{ringOverlay.text}
|
||||
{text}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
}, [ringOverlay]);
|
||||
);
|
||||
}, [callPickupState, client, matrixRoom]);
|
||||
|
||||
// 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
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
import { deepCompare } from "matrix-js-sdk/lib/utils";
|
||||
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
|
||||
|
||||
import { createCallViewModel$ } from "./CallViewModel";
|
||||
import { CallViewModel } from "./CallViewModel";
|
||||
import { type Layout } from "../layout-types.ts";
|
||||
import {
|
||||
mockLocalParticipant,
|
||||
@@ -277,7 +277,7 @@ describe("CallViewModel", () => {
|
||||
|
||||
vi.spyOn(AutoDiscovery, "getRawClientConfig").mockResolvedValue({});
|
||||
|
||||
const callVM = createCallViewModel$(
|
||||
const callVM = new CallViewModel(
|
||||
testScope(),
|
||||
fakeRtcSession.asMockedSession(),
|
||||
matrixRoom,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,11 +24,7 @@ import * as ComponentsCore from "@livekit/components-core";
|
||||
import type { CallMembership } from "matrix-js-sdk/lib/matrixrtc";
|
||||
import { E2eeType } from "../../e2ee/e2eeType";
|
||||
import { type RaisedHandInfo, type ReactionInfo } from "../../reactions";
|
||||
import {
|
||||
type CallViewModel,
|
||||
createCallViewModel$,
|
||||
type CallViewModelOptions,
|
||||
} from "./CallViewModel";
|
||||
import { CallViewModel, type CallViewModelOptions } from "./CallViewModel";
|
||||
import {
|
||||
mockConfig,
|
||||
mockLivekitRoom,
|
||||
@@ -126,7 +122,6 @@ export function withCallViewModel(
|
||||
}
|
||||
})() as Partial<MatrixClient> as MatrixClient,
|
||||
getMembers: () => Array.from(roomMembers.values()),
|
||||
getMembersWithMembership: () => Array.from(roomMembers.values()),
|
||||
});
|
||||
const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$);
|
||||
const participantsSpy = vi
|
||||
@@ -158,7 +153,7 @@ export function withCallViewModel(
|
||||
const raisedHands$ = new BehaviorSubject<Record<string, RaisedHandInfo>>({});
|
||||
const reactions$ = new BehaviorSubject<Record<string, ReactionInfo>>({});
|
||||
|
||||
const vm = createCallViewModel$(
|
||||
const vm = new CallViewModel(
|
||||
testScope(),
|
||||
rtcSession.asMockedSession(),
|
||||
room,
|
||||
|
||||
@@ -58,10 +58,6 @@ 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;
|
||||
});
|
||||
|
||||
|
||||
@@ -9,10 +9,7 @@ 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 {
|
||||
KnownMembership,
|
||||
type Room as MatrixRoom,
|
||||
} from "matrix-js-sdk/lib/matrix";
|
||||
import { type Room as MatrixRoom } from "matrix-js-sdk/lib/matrix";
|
||||
// eslint-disable-next-line rxjs/no-internal
|
||||
|
||||
import { type ObservableScope } from "../../ObservableScope";
|
||||
@@ -29,10 +26,7 @@ export type RoomMemberMap = Map<
|
||||
Pick<RoomMember, "userId" | "getMxcAvatarUrl" | "rawDisplayName">
|
||||
>;
|
||||
export function roomToMembersMap(matrixRoom: MatrixRoom): RoomMemberMap {
|
||||
const members = matrixRoom
|
||||
.getMembersWithMembership(KnownMembership.Join)
|
||||
.concat(matrixRoom.getMembersWithMembership(KnownMembership.Invite));
|
||||
return members.reduce((acc, member) => {
|
||||
return matrixRoom.getMembers().reduce((acc, member) => {
|
||||
acc.set(member.userId, {
|
||||
userId: member.userId,
|
||||
getMxcAvatarUrl: member.getMxcAvatarUrl.bind(member),
|
||||
@@ -53,32 +47,6 @@ export function createRoomMembers$(
|
||||
roomToMembersMap(matrixRoom),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates the member that this DM is with in case it is a DM (two members) otherwise null
|
||||
*/
|
||||
export function createDMMember$(
|
||||
scope: ObservableScope,
|
||||
roomMembers$: Behavior<RoomMemberMap>,
|
||||
matrixRoom: MatrixRoom,
|
||||
): Behavior<Pick<
|
||||
RoomMember,
|
||||
"userId" | "getMxcAvatarUrl" | "rawDisplayName"
|
||||
> | null> {
|
||||
// We cannot use the normal direct check from matrix since we do not have access to the account data.
|
||||
// use primitive member count === 2 check instead.
|
||||
return scope.behavior(
|
||||
roomMembers$.pipe(
|
||||
map((membersMap) => {
|
||||
// primitive appraoch do to no access to account data.
|
||||
const isDM = membersMap.size === 2;
|
||||
if (!isDM) return null;
|
||||
return matrixRoom.getMember(matrixRoom.guessDMUserId());
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displayname for each member of the call. This will disambiguate
|
||||
* any displayname that clashes with another member. Only members
|
||||
|
||||
@@ -25,7 +25,7 @@ import { roomToMembersMap } from "../state/CallViewModel/remoteMembers/MatrixMem
|
||||
describe("shouldDisambiguate", () => {
|
||||
test("should not disambiguate a solo member", () => {
|
||||
const room = mockMatrixRoom({
|
||||
getMembersWithMembership: () => [],
|
||||
getMembers: () => [],
|
||||
});
|
||||
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({
|
||||
getMembersWithMembership: () => [alice, aliceDoppelganger],
|
||||
getMembers: () => [alice, aliceDoppelganger],
|
||||
});
|
||||
expect(
|
||||
shouldDisambiguate(
|
||||
@@ -44,14 +44,14 @@ describe("shouldDisambiguate", () => {
|
||||
).toEqual(false);
|
||||
});
|
||||
test("should disambiguate a member with RTL characters", () => {
|
||||
const room = mockMatrixRoom({ getMembersWithMembership: () => [] });
|
||||
const room = mockMatrixRoom({ getMembers: () => [] });
|
||||
expect(shouldDisambiguate(daveRTL, [], roomToMembersMap(room))).toEqual(
|
||||
true,
|
||||
);
|
||||
});
|
||||
test("should disambiguate a member with a matching displayname", () => {
|
||||
const room = mockMatrixRoom({
|
||||
getMembersWithMembership: () => [alice, aliceDoppelganger],
|
||||
getMembers: () => [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({
|
||||
getMembersWithMembership: () => [bob, bobZeroWidthSpace],
|
||||
getMembers: () => [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({
|
||||
getMembersWithMembership: () => [alice, aliceDoppelganger],
|
||||
getMembers: () => [alice, aliceDoppelganger],
|
||||
});
|
||||
expect(
|
||||
shouldDisambiguate(
|
||||
|
||||
@@ -20,8 +20,7 @@ import { ConnectionState, type Room as LivekitRoom } from "livekit-client";
|
||||
|
||||
import { E2eeType } from "../e2ee/e2eeType";
|
||||
import {
|
||||
type CallViewModel,
|
||||
createCallViewModel$,
|
||||
CallViewModel,
|
||||
type CallViewModelOptions,
|
||||
} from "../state/CallViewModel/CallViewModel";
|
||||
import {
|
||||
@@ -84,8 +83,6 @@ export function getBasicRTCSession(
|
||||
} as Partial<MatrixClient> as MatrixClient,
|
||||
getMember: (userId) => matrixRoomMembers.get(userId) ?? null,
|
||||
getMembers: () => Array.from(matrixRoomMembers.values()),
|
||||
getMembersWithMembership: () => Array.from(matrixRoomMembers.values()),
|
||||
guessDMUserId: vitest.fn(),
|
||||
roomId: matrixRoomId,
|
||||
on: vitest
|
||||
.fn()
|
||||
@@ -146,7 +143,7 @@ export function getBasicCallViewModelEnvironment(
|
||||
|
||||
// const remoteParticipants$ = of([aliceParticipant]);
|
||||
|
||||
const vm = createCallViewModel$(
|
||||
const vm = new CallViewModel(
|
||||
testScope(),
|
||||
rtcSession.asMockedSession(),
|
||||
matrixRoom,
|
||||
|
||||
Reference in New Issue
Block a user