Fix lints, move CallViewModel.test.ts. Fix audio renderer

This commit is contained in:
Timo K
2025-11-07 14:04:40 +01:00
parent 28047217b8
commit e741285b11
19 changed files with 71 additions and 67 deletions

View File

@@ -13,7 +13,7 @@ import { type ReactNode } from "react";
import { ReactionToggleButton } from "./ReactionToggleButton"; import { ReactionToggleButton } from "./ReactionToggleButton";
import { ElementCallReactionEventType } from "../reactions"; import { ElementCallReactionEventType } from "../reactions";
import { type CallViewModel } from "../state/CallViewModel"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { getBasicCallViewModelEnvironment } from "../utils/test-viewmodel"; import { getBasicCallViewModelEnvironment } from "../utils/test-viewmodel";
import { alice, local, localRtcMember } from "../utils/test-fixtures"; import { alice, local, localRtcMember } from "../utils/test-fixtures";
import { type MockRTCSession } from "../utils/test"; import { type MockRTCSession } from "../utils/test";

View File

@@ -33,7 +33,7 @@ import {
ReactionsRowSize, ReactionsRowSize,
} from "../reactions"; } from "../reactions";
import { Modal } from "../Modal"; import { Modal } from "../Modal";
import { type CallViewModel } from "../state/CallViewModel"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { useBehavior } from "../useBehavior"; import { useBehavior } from "../useBehavior";
interface InnerButtonProps extends ComponentPropsWithoutRef<"button"> { interface InnerButtonProps extends ComponentPropsWithoutRef<"button"> {

View File

@@ -20,7 +20,7 @@ import { logger } from "matrix-js-sdk/lib/logger";
import { useMatrixRTCSessionMemberships } from "../useMatrixRTCSessionMemberships"; import { useMatrixRTCSessionMemberships } from "../useMatrixRTCSessionMemberships";
import { useClientState } from "../ClientContext"; import { useClientState } from "../ClientContext";
import { ElementCallReactionEventType, type ReactionOption } from "."; import { ElementCallReactionEventType, type ReactionOption } from ".";
import { type CallViewModel } from "../state/CallViewModel"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { useBehavior } from "../useBehavior"; import { useBehavior } from "../useBehavior";
interface ReactionsSenderContextType { interface ReactionsSenderContextType {

View File

@@ -38,7 +38,7 @@ import {
local, local,
localRtcMember, localRtcMember,
} from "../utils/test-fixtures"; } from "../utils/test-fixtures";
import { MAX_PARTICIPANT_COUNT_FOR_SOUND } from "../state/CallViewModel"; import { MAX_PARTICIPANT_COUNT_FOR_SOUND } from "../state/CallViewModel/CallViewModel";
vitest.mock("livekit-client/e2ee-worker?worker"); vitest.mock("livekit-client/e2ee-worker?worker");
vitest.mock("../useAudioContext"); vitest.mock("../useAudioContext");

View File

@@ -7,7 +7,7 @@ Please see LICENSE in the repository root for full details.
import { type ReactNode, useEffect } from "react"; import { type ReactNode, useEffect } from "react";
import { type CallViewModel } from "../state/CallViewModel"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import joinCallSoundMp3 from "../sound/join_call.mp3"; import joinCallSoundMp3 from "../sound/join_call.mp3";
import joinCallSoundOgg from "../sound/join_call.ogg"; import joinCallSoundOgg from "../sound/join_call.ogg";
import leftCallSoundMp3 from "../sound/left_call.mp3"; import leftCallSoundMp3 from "../sound/left_call.mp3";

View File

@@ -734,7 +734,8 @@ export const InCallView: FC<InCallViewProps> = ({
<ShareScreenButton <ShareScreenButton
key="share_screen" key="share_screen"
className={styles.shareScreen} className={styles.shareScreen}
enabled={sharingScreen} disabled={sharingScreen === undefined}
enabled={sharingScreen === true}
onClick={vm.toggleScreenSharing} onClick={vm.toggleScreenSharing}
onTouchEnd={onControlsTouchEnd} onTouchEnd={onControlsTouchEnd}
data-testid="incall_screenshare" data-testid="incall_screenshare"
@@ -822,7 +823,7 @@ export const InCallView: FC<InCallViewProps> = ({
key={url} key={url}
url={url} url={url}
livekitRoom={livekitRoom} livekitRoom={livekitRoom}
validIdentities={participants.map((p) => p.identity)} validIdentities={participants}
muted={muteAllAudio} muted={muteAllAudio}
/> />
))} ))}

View File

@@ -27,7 +27,7 @@ import {
import { useAudioContext } from "../useAudioContext"; import { useAudioContext } from "../useAudioContext";
import { GenericReaction, ReactionSet } from "../reactions"; import { GenericReaction, ReactionSet } from "../reactions";
import { prefetchSounds } from "../soundUtils"; import { prefetchSounds } from "../soundUtils";
import { type CallViewModel } from "../state/CallViewModel"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { getBasicCallViewModelEnvironment } from "../utils/test-viewmodel"; import { getBasicCallViewModelEnvironment } from "../utils/test-viewmodel";
import { import {
alice, alice,

View File

@@ -12,7 +12,7 @@ import { GenericReaction, ReactionSet } from "../reactions";
import { useAudioContext } from "../useAudioContext"; import { useAudioContext } from "../useAudioContext";
import { prefetchSounds } from "../soundUtils"; import { prefetchSounds } from "../soundUtils";
import { useLatest } from "../useLatest"; import { useLatest } from "../useLatest";
import { type CallViewModel } from "../state/CallViewModel"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
const soundMap = Object.fromEntries([ const soundMap = Object.fromEntries([
...ReactionSet.filter((v) => v.sound !== undefined).map((v) => [ ...ReactionSet.filter((v) => v.sound !== undefined).map((v) => [

View File

@@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details.
import { type ReactNode } from "react"; import { type ReactNode } from "react";
import styles from "./ReactionsOverlay.module.css"; import styles from "./ReactionsOverlay.module.css";
import { type CallViewModel } from "../state/CallViewModel"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { useBehavior } from "../useBehavior"; import { useBehavior } from "../useBehavior";
export function ReactionsOverlay({ vm }: { vm: CallViewModel }): ReactNode { export function ReactionsOverlay({ vm }: { vm: CallViewModel }): ReactNode {

View File

@@ -26,7 +26,6 @@ import {
duplicateTiles as duplicateTilesSetting, duplicateTiles as duplicateTilesSetting,
debugTileLayout as debugTileLayoutSetting, debugTileLayout as debugTileLayoutSetting,
showConnectionStats as showConnectionStatsSetting, showConnectionStats as showConnectionStatsSetting,
multiSfu as multiSfuSetting,
muteAllAudio as muteAllAudioSetting, muteAllAudio as muteAllAudioSetting,
alwaysShowIphoneEarpiece as alwaysShowIphoneEarpieceSetting, alwaysShowIphoneEarpiece as alwaysShowIphoneEarpieceSetting,
matrixRTCMode as matrixRTCModeSetting, matrixRTCMode as matrixRTCModeSetting,

View File

@@ -50,7 +50,7 @@ import { deepCompare } from "matrix-js-sdk/lib/utils";
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery"; import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
import { CallViewModel, type CallViewModelOptions } from "./CallViewModel"; import { CallViewModel, type CallViewModelOptions } from "./CallViewModel";
import { type Layout } from "./layout-types"; import { type Layout } from "../layout-types.ts";
import { import {
mockLocalParticipant, mockLocalParticipant,
mockMatrixRoom, mockMatrixRoom,
@@ -65,9 +65,9 @@ import {
testScope, testScope,
mockLivekitRoom, mockLivekitRoom,
exampleTransport, exampleTransport,
} from "../utils/test"; } from "../../utils/test.ts";
import { E2eeType } from "../e2ee/e2eeType"; import { E2eeType } from "../../e2ee/e2eeType.ts";
import type { RaisedHandInfo, ReactionInfo } from "../reactions"; import type { RaisedHandInfo, ReactionInfo } from "../../reactions/index.ts";
import { import {
alice, alice,
aliceDoppelganger, aliceDoppelganger,
@@ -89,15 +89,15 @@ import {
localId, localId,
localRtcMember, localRtcMember,
localRtcMemberDevice2, localRtcMemberDevice2,
} from "../utils/test-fixtures"; } from "../../utils/test-fixtures.ts";
import { MediaDevices } from "./MediaDevices"; import { MediaDevices } from "../MediaDevices.ts";
import { getValue } from "../utils/observable"; import { getValue } from "../../utils/observable.ts";
import { type Behavior, constant } from "./Behavior"; import { type Behavior, constant } from "../Behavior.ts";
import type { ProcessorState } from "../livekit/TrackProcessorContext.tsx"; import type { ProcessorState } from "../../livekit/TrackProcessorContext.tsx";
import { import {
type ElementCallError, type ElementCallError,
MatrixRTCTransportMissingError, MatrixRTCTransportMissingError,
} from "../utils/errors.ts"; } from "../../utils/errors.ts";
vi.mock("rxjs", async (importOriginal) => ({ vi.mock("rxjs", async (importOriginal) => ({
...(await importOriginal()), ...(await importOriginal()),

View File

@@ -156,7 +156,11 @@ interface LayoutScanState {
} }
type MediaItem = UserMedia | ScreenShare; type MediaItem = UserMedia | ScreenShare;
type AudioLivekitItem = {
livekitRoom: LivekitRoom;
participants: string[];
url: string;
};
/** /**
* A view model providing all the application logic needed to show the in-call * A view model providing all the application logic needed to show the in-call
* UI (may eventually be expanded to cover the lobby and feedback screens in the * UI (may eventually be expanded to cover the lobby and feedback screens in the
@@ -166,8 +170,6 @@ type MediaItem = UserMedia | ScreenShare;
// state and LiveKit state. We use the common terminology of room "members", RTC // state and LiveKit state. We use the common terminology of room "members", RTC
// "memberships", and LiveKit "participants". // "memberships", and LiveKit "participants".
export class CallViewModel { export class CallViewModel {
private readonly urlParams = getUrlParams();
private readonly userId = this.matrixRoom.client.getUserId()!; private readonly userId = this.matrixRoom.client.getUserId()!;
private readonly deviceId = this.matrixRoom.client.getDeviceId()!; private readonly deviceId = this.matrixRoom.client.getDeviceId()!;
@@ -285,6 +287,7 @@ export class CallViewModel {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// ROOM MEMBER tracking TODO // ROOM MEMBER tracking TODO
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private roomMembers$ = createRoomMembers$(this.scope, this.matrixRoom); private roomMembers$ = createRoomMembers$(this.scope, this.matrixRoom);
/** /**
* If there is a configuration error with the call (e.g. misconfigured E2EE). * If there is a configuration error with the call (e.g. misconfigured E2EE).
@@ -311,6 +314,7 @@ export class CallViewModel {
* than whether all connections are truly up and running. * than whether all connections are truly up and running.
*/ */
// DISCUSS ? lets think why we need joined and how to do it better // DISCUSS ? lets think why we need joined and how to do it better
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private readonly joined$ = this.localMembership.connected$; private readonly joined$ = this.localMembership.connected$;
/** /**
@@ -327,7 +331,23 @@ export class CallViewModel {
public readonly audioParticipants$ = this.scope.behavior( public readonly audioParticipants$ = this.scope.behavior(
this.matrixLivekitMembers$.pipe( this.matrixLivekitMembers$.pipe(
map((members) => members.value.map((m) => m.participant)), map((members) =>
members.value.reduce<AudioLivekitItem[]>((acc, curr) => {
const url = curr.connection?.transport.livekit_service_url;
const livekitRoom = curr.connection?.livekitRoom;
const participant = curr.participant?.identity;
if (!url || !livekitRoom || !participant) return acc;
const existing = acc.find((item) => item.url === url);
if (existing) {
existing.participants.push(participant);
} else {
acc.push({ livekitRoom, participants: [participant], url });
}
return acc;
}, []),
),
), ),
); );

View File

@@ -135,7 +135,8 @@ export const createLocalMembership$ = ({
startTracks: () => Behavior<LocalTrack[]>; startTracks: () => Behavior<LocalTrack[]>;
requestDisconnect: () => Observable<LocalMemberLivekitState> | null; requestDisconnect: () => Observable<LocalMemberLivekitState> | null;
connectionState: LocalMemberConnectionState; connectionState: LocalMemberConnectionState;
sharingScreen$: Behavior<boolean | undefined>; // Use null here since behavior cannot be initialised with undefined.
sharingScreen$: Behavior<boolean | null>;
toggleScreenSharing: (() => void) | null; toggleScreenSharing: (() => void) | null;
// deprecated fields // deprecated fields
@@ -432,7 +433,7 @@ export const createLocalMembership$ = ({
const sharingScreen$ = scope.behavior( const sharingScreen$ = scope.behavior(
connection$.pipe( connection$.pipe(
switchMap((c) => { switchMap((c) => {
if (!c) return of(undefined); if (!c) return of(null);
if (c.state$.value.state === "ConnectedToLkRoom") if (c.state$.value.state === "ConnectedToLkRoom")
return observeSharingScreen$(c.livekitRoom.localParticipant); return observeSharingScreen$(c.livekitRoom.localParticipant);
return of(false); return of(false);

View File

@@ -31,7 +31,7 @@ import {
} from "../../../livekit/TrackProcessorContext.tsx"; } from "../../../livekit/TrackProcessorContext.tsx";
import { getUrlParams } from "../../../UrlParams.ts"; import { getUrlParams } from "../../../UrlParams.ts";
import { observeTrackReference$ } from "../../MediaViewModel.ts"; import { observeTrackReference$ } from "../../MediaViewModel.ts";
import { type Connection } from "../CallViewModel/remoteMembers/Connection.ts"; import { type Connection } from "../remoteMembers/Connection.ts";
import { type ObservableScope } from "../../ObservableScope.ts"; import { type ObservableScope } from "../../ObservableScope.ts";
/** /**
@@ -64,7 +64,7 @@ export class Publisher {
const room = connection.livekitRoom; const room = connection.livekitRoom;
room.setE2EEEnabled(e2eeLivekitOptions !== undefined)?.catch((e) => { room.setE2EEEnabled(e2eeLivekitOptions !== undefined)?.catch((e: Error) => {
this.logger?.error("Failed to set E2EE enabled on room", e); this.logger?.error("Failed to set E2EE enabled on room", e);
}); });
@@ -249,7 +249,7 @@ export class Publisher {
) { ) {
lkRoom lkRoom
.switchActiveDevice(kind, device.id) .switchActiveDevice(kind, device.id)
.catch((e) => .catch((e: Error) =>
this.logger?.error( this.logger?.error(
`Failed to sync ${kind} device with LiveKit`, `Failed to sync ${kind} device with LiveKit`,
e, e,

View File

@@ -22,6 +22,7 @@ import {
type Room as LivekitRoom, type Room as LivekitRoom,
RoomEvent, RoomEvent,
type RoomOptions, type RoomOptions,
ConnectionState as LivekitConnectionState,
} from "livekit-client"; } from "livekit-client";
import fetchMock from "fetch-mock"; import fetchMock from "fetch-mock";
import EventEmitter from "events"; import EventEmitter from "events";
@@ -32,6 +33,7 @@ import type {
LivekitTransport, LivekitTransport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { import {
Connection,
type ConnectionOpts, type ConnectionOpts,
type ConnectionState, type ConnectionState,
type PublishingParticipant, type PublishingParticipant,
@@ -103,7 +105,7 @@ function setupTest(): void {
disconnect: vi.fn(), disconnect: vi.fn(),
remoteParticipants: new Map(), remoteParticipants: new Map(),
localParticipant: fakeLocalParticipant, localParticipant: fakeLocalParticipant,
state: ConnectionState.Disconnected, state: LivekitConnectionState.Disconnected,
on: fakeRoomEventEmiter.on.bind(fakeRoomEventEmiter), on: fakeRoomEventEmiter.on.bind(fakeRoomEventEmiter),
off: fakeRoomEventEmiter.off.bind(fakeRoomEventEmiter), off: fakeRoomEventEmiter.off.bind(fakeRoomEventEmiter),
addListener: fakeRoomEventEmiter.addListener.bind(fakeRoomEventEmiter), addListener: fakeRoomEventEmiter.addListener.bind(fakeRoomEventEmiter),
@@ -115,11 +117,10 @@ function setupTest(): void {
} as unknown as LivekitRoom); } as unknown as LivekitRoom);
} }
function setupRemoteConnection(): RemoteConnection { function setupRemoteConnection(): Connection {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
transport: livekitFocus, transport: livekitFocus,
remoteTransports$: fakeMembershipsFocusMap$,
scope: testScope, scope: testScope,
livekitRoomFactory: () => fakeLivekitRoom, livekitRoomFactory: () => fakeLivekitRoom,
}; };
@@ -136,7 +137,7 @@ function setupRemoteConnection(): RemoteConnection {
fakeLivekitRoom.connect.mockResolvedValue(undefined); fakeLivekitRoom.connect.mockResolvedValue(undefined);
return new RemoteConnection(opts, undefined); return new Connection(opts);
} }
afterEach(() => { afterEach(() => {
@@ -152,11 +153,10 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
transport: livekitFocus, transport: livekitFocus,
remoteTransports$: fakeMembershipsFocusMap$,
scope: testScope, scope: testScope,
livekitRoomFactory: () => fakeLivekitRoom, livekitRoomFactory: () => fakeLivekitRoom,
}; };
const connection = new RemoteConnection(opts, undefined); const connection = new Connection(opts);
expect(connection.state$.getValue().state).toEqual("Initialized"); expect(connection.state$.getValue().state).toEqual("Initialized");
}); });
@@ -168,12 +168,11 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
transport: livekitFocus, transport: livekitFocus,
remoteTransports$: fakeMembershipsFocusMap$,
scope: testScope, scope: testScope,
livekitRoomFactory: () => fakeLivekitRoom, livekitRoomFactory: () => fakeLivekitRoom,
}; };
const connection = new RemoteConnection(opts, undefined); const connection = new Connection(opts, undefined);
const capturedStates: ConnectionState[] = []; const capturedStates: ConnectionState[] = [];
const s = connection.state$.subscribe((value) => { const s = connection.state$.subscribe((value) => {
@@ -221,12 +220,11 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
transport: livekitFocus, transport: livekitFocus,
remoteTransports$: fakeMembershipsFocusMap$,
scope: testScope, scope: testScope,
livekitRoomFactory: () => fakeLivekitRoom, livekitRoomFactory: () => fakeLivekitRoom,
}; };
const connection = new RemoteConnection(opts, undefined); const connection = new Connection(opts, undefined);
const capturedStates: ConnectionState[] = []; const capturedStates: ConnectionState[] = [];
const s = connection.state$.subscribe((value) => { const s = connection.state$.subscribe((value) => {
@@ -278,12 +276,11 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
transport: livekitFocus, transport: livekitFocus,
remoteTransports$: fakeMembershipsFocusMap$,
scope: testScope, scope: testScope,
livekitRoomFactory: () => fakeLivekitRoom, livekitRoomFactory: () => fakeLivekitRoom,
}; };
const connection = new RemoteConnection(opts, undefined); const connection = new Connection(opts, undefined);
const capturedStates: ConnectionState[] = []; const capturedStates: ConnectionState[] = [];
const s = connection.state$.subscribe((value) => { const s = connection.state$.subscribe((value) => {

View File

@@ -55,7 +55,7 @@ interface Props {
// => Extract an AvatarService instead? // => Extract an AvatarService instead?
// Better with just `getMember` // Better with just `getMember`
matrixRoom: Pick<MatrixRoom, "getMember"> & NodeStyleEventEmitter; matrixRoom: Pick<MatrixRoom, "getMember"> & NodeStyleEventEmitter;
roomMember$: Behavior<Pick<RoomMember, "userId" | "getMxcAvatarUrl">>; // roomMember$: Behavior<Pick<RoomMember, "userId" | "getMxcAvatarUrl">>;
} }
// Alternative structure idea: // Alternative structure idea:
// const livekitMatrixMember$ = (callMemberships$,connectionManager,scope): Observable<MatrixLivekitMember[]> => { // const livekitMatrixMember$ = (callMemberships$,connectionManager,scope): Observable<MatrixLivekitMember[]> => {

View File

@@ -14,7 +14,7 @@ import {
} from "matrix-js-sdk"; } from "matrix-js-sdk";
import EventEmitter from "events"; import EventEmitter from "events";
import { ObservableScope } from "../../ObservableScope.ts"; import { ObservableScope, trackEpoch } from "../../ObservableScope.ts";
import type { Room as MatrixRoom } from "matrix-js-sdk/lib/models/room"; import type { Room as MatrixRoom } from "matrix-js-sdk/lib/models/room";
import { mockCallMembership, withTestScheduler } from "../../../utils/test.ts"; import { mockCallMembership, withTestScheduler } from "../../../utils/test.ts";
import { memberDisplaynames$ } from "./displayname.ts"; import { memberDisplaynames$ } from "./displayname.ts";
@@ -90,9 +90,7 @@ test("should always have our own user", () => {
mockMatrixRoom, mockMatrixRoom,
cold("a", { cold("a", {
a: [], a: [],
}), }).pipe(trackEpoch()),
"@local:example.com",
"DEVICE000",
); );
expectObservable(dn$).toBe("a", { expectObservable(dn$).toBe("a", {
@@ -125,9 +123,7 @@ test("should get displayName for users", () => {
mockCallMembership("@alice:example.com", "DEVICE1"), mockCallMembership("@alice:example.com", "DEVICE1"),
mockCallMembership("@bob:example.com", "DEVICE1"), mockCallMembership("@bob:example.com", "DEVICE1"),
], ],
}), }).pipe(trackEpoch()),
"@local:example.com",
"DEVICE000",
); );
expectObservable(dn$).toBe("a", { expectObservable(dn$).toBe("a", {
@@ -149,9 +145,7 @@ test("should use userId if no display name", () => {
mockMatrixRoom, mockMatrixRoom,
cold("a", { cold("a", {
a: [mockCallMembership("@no-name:foo.bar", "D000")], a: [mockCallMembership("@no-name:foo.bar", "D000")],
}), }).pipe(trackEpoch()),
"@local:example.com",
"DEVICE000",
); );
expectObservable(dn$).toBe("a", { expectObservable(dn$).toBe("a", {
@@ -178,9 +172,7 @@ test("should disambiguate users with same display name", () => {
mockCallMembership("@carl:example.com", "C000"), mockCallMembership("@carl:example.com", "C000"),
mockCallMembership("@evil:example.com", "E000"), mockCallMembership("@evil:example.com", "E000"),
], ],
}), }).pipe(trackEpoch()),
"@local:example.com",
"DEVICE000",
); );
expectObservable(dn$).toBe("a", { expectObservable(dn$).toBe("a", {
@@ -209,9 +201,7 @@ test("should disambiguate when needed", () => {
mockCallMembership("@bob:example.com", "DEVICE1"), mockCallMembership("@bob:example.com", "DEVICE1"),
mockCallMembership("@bob:foo.bar", "BOB000"), mockCallMembership("@bob:foo.bar", "BOB000"),
], ],
}), }).pipe(trackEpoch()),
"@local:example.com",
"DEVICE000",
); );
expectObservable(dn$).toBe("ab", { expectObservable(dn$).toBe("ab", {
@@ -241,9 +231,7 @@ test.skip("should keep disambiguated name when other leave", () => {
mockCallMembership("@bob:foo.bar", "BOB000"), mockCallMembership("@bob:foo.bar", "BOB000"),
], ],
b: [mockCallMembership("@bob:example.com", "DEVICE1")], b: [mockCallMembership("@bob:example.com", "DEVICE1")],
}), }).pipe(trackEpoch()),
"@local:example.com",
"DEVICE000",
); );
expectObservable(dn$).toBe("ab", { expectObservable(dn$).toBe("ab", {
@@ -272,9 +260,7 @@ test("should disambiguate on name change", () => {
mockCallMembership("@bob:example.com", "B000"), mockCallMembership("@bob:example.com", "B000"),
mockCallMembership("@carl:example.com", "C000"), mockCallMembership("@carl:example.com", "C000"),
], ],
}), }).pipe(trackEpoch()),
"@local:example.com",
"DEVICE000",
); );
schedule("-a", { schedule("-a", {

View File

@@ -15,7 +15,7 @@ import { GridTile } from "./GridTile";
import { mockRtcMembership, createRemoteMedia } from "../utils/test"; import { mockRtcMembership, createRemoteMedia } from "../utils/test";
import { GridTileViewModel } from "../state/TileViewModel"; import { GridTileViewModel } from "../state/TileViewModel";
import { ReactionsSenderProvider } from "../reactions/useReactionsSender"; import { ReactionsSenderProvider } from "../reactions/useReactionsSender";
import type { CallViewModel } from "../state/CallViewModel"; import type { CallViewModel } from "../state/CallViewModel/CallViewModel";
import { constant } from "../state/Behavior"; import { constant } from "../state/Behavior";
global.IntersectionObserver = class MockIntersectionObserver { global.IntersectionObserver = class MockIntersectionObserver {

View File

@@ -22,7 +22,7 @@ import { E2eeType } from "../e2ee/e2eeType";
import { import {
CallViewModel, CallViewModel,
type CallViewModelOptions, type CallViewModelOptions,
} from "../state/CallViewModel"; } from "../state/CallViewModel/CallViewModel";
import { import {
mockLivekitRoom, mockLivekitRoom,
mockLocalParticipant, mockLocalParticipant,