mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-29 21:15:19 +00:00
review. Dont pass full vm to developer settings
This commit is contained in:
@@ -122,12 +122,13 @@ function createInCallView(args: CreateInCallViewArgs = {}): RenderResult & {
|
||||
remoteParticipants$: of([remoteParticipant]),
|
||||
},
|
||||
);
|
||||
const { vm, footerVm, rtcSession } = getBasicCallViewModelEnvironment(
|
||||
[local, alice],
|
||||
undefined,
|
||||
mediaDevices,
|
||||
args.callViewModelOptions,
|
||||
);
|
||||
const { vm, footerVm, developerSettingsVm, rtcSession } =
|
||||
getBasicCallViewModelEnvironment(
|
||||
[local, alice],
|
||||
undefined,
|
||||
mediaDevices,
|
||||
args.callViewModelOptions,
|
||||
);
|
||||
|
||||
rtcSession.joined = true;
|
||||
const room = rtcSession.room;
|
||||
@@ -140,6 +141,7 @@ function createInCallView(args: CreateInCallViewArgs = {}): RenderResult & {
|
||||
muteStates={muteState}
|
||||
vm={vm}
|
||||
footerVm={footerVm}
|
||||
developerSettingsVm={developerSettingsVm}
|
||||
matrixInfo={{
|
||||
userId: "",
|
||||
displayName: "",
|
||||
|
||||
@@ -83,6 +83,8 @@ import { ObservableScope } from "../state/ObservableScope.ts";
|
||||
import { CallFooter, type FooterSnapshot } from "../components/CallFooter.tsx";
|
||||
import { SettingsIconButton } from "../button/Button.tsx";
|
||||
import { createCallFooterViewModel } from "../components/CallFooterViewModel.tsx";
|
||||
import { createDeveloperSettingsTabViewModel } from "../settings/DeveloperSettingsTabViewModel.ts";
|
||||
import { type DeveloperSettingsSnapshot } from "../settings/DeveloperSettingsTab.tsx";
|
||||
import { type ViewModel } from "../state/ViewModel.ts";
|
||||
import { RingingStatus } from "../tile/RingingStatus.tsx";
|
||||
import { RingingAudioRenderer } from "./RingingAudioRenderer.tsx";
|
||||
@@ -96,7 +98,7 @@ declare module "react" {
|
||||
|
||||
export interface ActiveCallProps extends Omit<
|
||||
InCallViewProps,
|
||||
"vm" | "livekitRoom" | "connState" | "footerVm"
|
||||
"vm" | "livekitRoom" | "connState" | "footerVm" | "developerSettingsVm"
|
||||
> {
|
||||
e2eeSystem: EncryptionSystem;
|
||||
// TODO refactor those reasons into an enum
|
||||
@@ -110,6 +112,9 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
||||
const [footerVm, setFooterVm] = useState<ViewModel<FooterSnapshot> | null>(
|
||||
null,
|
||||
);
|
||||
const [developerSettingsVm, setDeveloperSettingsVm] =
|
||||
useState<ViewModel<DeveloperSettingsSnapshot> | null>(null);
|
||||
|
||||
const urlParams = useUrlParams();
|
||||
const mediaDevices = useMediaDevices();
|
||||
const trackProcessorState$ = useTrackProcessorObservable$();
|
||||
@@ -168,7 +173,9 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
||||
mediaDevices,
|
||||
`${props.client.getUserId()}:${props.client.getDeviceId()}`,
|
||||
);
|
||||
const developerSettingsVm = createDeveloperSettingsTabViewModel(scope, vm);
|
||||
setFooterVm(footerVm);
|
||||
setDeveloperSettingsVm(developerSettingsVm);
|
||||
|
||||
return (): void => {
|
||||
scope.end();
|
||||
@@ -188,10 +195,16 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
||||
|
||||
if (vm === null) return null;
|
||||
if (footerVm === null) return null;
|
||||
if (developerSettingsVm === null) return null;
|
||||
|
||||
return (
|
||||
<ReactionsSenderProvider vm={vm} rtcSession={props.rtcSession}>
|
||||
<InCallView {...props} vm={vm} footerVm={footerVm} />
|
||||
<InCallView
|
||||
{...props}
|
||||
vm={vm}
|
||||
footerVm={footerVm}
|
||||
developerSettingsVm={developerSettingsVm}
|
||||
/>
|
||||
</ReactionsSenderProvider>
|
||||
);
|
||||
};
|
||||
@@ -200,6 +213,7 @@ export interface InCallViewProps {
|
||||
client: MatrixClient;
|
||||
vm: CallViewModel;
|
||||
footerVm: ViewModel<FooterSnapshot>;
|
||||
developerSettingsVm: ViewModel<DeveloperSettingsSnapshot>;
|
||||
matrixInfo: MatrixInfo;
|
||||
rtcSession: MatrixRTCSession;
|
||||
matrixRoom: MatrixRoom;
|
||||
@@ -211,6 +225,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
client,
|
||||
vm,
|
||||
footerVm,
|
||||
developerSettingsVm,
|
||||
matrixInfo,
|
||||
matrixRoom,
|
||||
muteStates,
|
||||
@@ -632,7 +647,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
onDismiss={(): void => setSettingsOpen(false)}
|
||||
tab={settingsTab}
|
||||
onTabChange={setSettingsTab}
|
||||
vm={vm}
|
||||
developerSettingsVm={developerSettingsVm}
|
||||
livekitRooms={allConnections
|
||||
.getConnections()
|
||||
.map((connectionItem) => ({
|
||||
|
||||
@@ -9,11 +9,15 @@ import { afterEach, describe, expect, it, type Mock, vi } from "vitest";
|
||||
import { render, waitFor, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import type { MatrixClient } from "matrix-js-sdk";
|
||||
import type { Room as LivekitRoom } from "livekit-client";
|
||||
import { DeveloperSettingsTab } from "./DeveloperSettingsTab";
|
||||
import {
|
||||
DeveloperSettingsTab,
|
||||
type DeveloperSettingsSnapshot,
|
||||
} from "./DeveloperSettingsTab";
|
||||
import { outOfCallDeveloperSettingsTabViewModel } from "./DeveloperSettingsTabViewModel";
|
||||
import { createStaticViewModel } from "../state/ViewModel";
|
||||
import { getSFUConfigWithOpenID } from "../livekit/openIDSFU";
|
||||
import {
|
||||
customLivekitUrl as customLivekitUrlSetting,
|
||||
@@ -108,6 +112,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
roomId={"#room:example.org"}
|
||||
livekitRooms={livekitRooms}
|
||||
env={{ MY_MOCK_ENV: 10, ENV: "test" } as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -137,6 +142,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -160,6 +166,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
client={client}
|
||||
roomId="#testRoom"
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -182,6 +189,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
client={client}
|
||||
roomId="#testRoom"
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -207,6 +215,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
client={client}
|
||||
roomId="#testRoom"
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -237,6 +246,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
client={client}
|
||||
roomId="#testRoom"
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -274,6 +284,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -306,6 +317,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -350,6 +362,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -389,6 +402,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -417,17 +431,16 @@ describe("DeveloperSettingsTab", () => {
|
||||
describe("KeyRotationStatus", () => {
|
||||
it("displays active status when key rotation is not suppressed", async () => {
|
||||
const client = createMockMatrixClient();
|
||||
const mockVm = {
|
||||
keyRotationSuppressed$: new BehaviorSubject(false),
|
||||
participantCount$: new BehaviorSubject(5),
|
||||
} as unknown as any;
|
||||
const vm = createStaticViewModel<DeveloperSettingsSnapshot>({
|
||||
keyRotation: { suppressed: false, participantCount: 5 },
|
||||
});
|
||||
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={mockVm}
|
||||
vm={vm}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -443,17 +456,16 @@ describe("DeveloperSettingsTab", () => {
|
||||
|
||||
it("displays suppressed status when key rotation is suppressed", async () => {
|
||||
const client = createMockMatrixClient();
|
||||
const mockVm = {
|
||||
keyRotationSuppressed$: new BehaviorSubject(true),
|
||||
participantCount$: new BehaviorSubject(50),
|
||||
} as unknown as any;
|
||||
const vm = createStaticViewModel<DeveloperSettingsSnapshot>({
|
||||
keyRotation: { suppressed: true, participantCount: 50 },
|
||||
});
|
||||
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={mockVm}
|
||||
vm={vm}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
@@ -469,7 +481,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render KeyRotationStatus when vm is not provided", async () => {
|
||||
it("does not render KeyRotationStatus when not in a call", async () => {
|
||||
const client = createMockMatrixClient();
|
||||
|
||||
render(
|
||||
@@ -477,6 +489,7 @@ describe("DeveloperSettingsTab", () => {
|
||||
<DeveloperSettingsTab
|
||||
client={client}
|
||||
env={{} as unknown as ImportMetaEnv}
|
||||
vm={outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
@@ -67,25 +67,40 @@ import styles from "./DeveloperSettingsTab.module.css";
|
||||
import settingsStyles from "./SettingsModal.module.css";
|
||||
import { Slider } from "../Slider";
|
||||
import { useUrlParams } from "../UrlParams";
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel.ts";
|
||||
import { getSFUConfigWithOpenID } from "../livekit/openIDSFU";
|
||||
import { useBehavior } from "../useBehavior";
|
||||
import { type ViewModel } from "../state/ViewModel.ts";
|
||||
|
||||
/**
|
||||
* The state of MatrixRTC's media key rotation.
|
||||
*/
|
||||
export interface KeyRotationInfo {
|
||||
/** Whether the call is large enough that MatrixRTC has stopped rotating the media key. */
|
||||
suppressed: boolean;
|
||||
participantCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Snapshot combines all fields the developer settings tab needs from the
|
||||
* surrounding call. Everything else in this tab is read from the settings store
|
||||
* or the environment directly.
|
||||
*/
|
||||
export interface DeveloperSettingsSnapshot {
|
||||
/** The media key rotation state, or `null` when we are not in a call. */
|
||||
keyRotation: KeyRotationInfo | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows whether the call is large enough that MatrixRTC has stopped rotating the media key.
|
||||
*/
|
||||
const KeyRotationStatus: FC<{ vm: CallViewModel }> = ({ vm }) => {
|
||||
const suppressed = useBehavior(vm.keyRotationSuppressed$);
|
||||
const participantCount = useBehavior(vm.participantCount$);
|
||||
return (
|
||||
<p>
|
||||
Media key rotation:{" "}
|
||||
{suppressed
|
||||
? `suppressed, participant limit reached (${participantCount} participants)`
|
||||
: `active (${participantCount} participants)`}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
const KeyRotationStatus: FC<{ info: KeyRotationInfo }> = ({ info }) => (
|
||||
<p>
|
||||
Media key rotation:{" "}
|
||||
{info.suppressed
|
||||
? `suppressed, participant limit reached (${info.participantCount} participants)`
|
||||
: `active (${info.participantCount} participants)`}
|
||||
</p>
|
||||
);
|
||||
|
||||
interface Props {
|
||||
client: MatrixClient;
|
||||
@@ -97,8 +112,7 @@ interface Props {
|
||||
livekitAlias?: string;
|
||||
}[];
|
||||
env: ImportMetaEnv;
|
||||
/** Only available while in a call. */
|
||||
vm?: CallViewModel;
|
||||
vm: ViewModel<DeveloperSettingsSnapshot>;
|
||||
}
|
||||
|
||||
export const DeveloperSettingsTab: FC<Props> = ({
|
||||
@@ -109,6 +123,7 @@ export const DeveloperSettingsTab: FC<Props> = ({
|
||||
vm,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const keyRotation = useBehavior(vm.keyRotation$);
|
||||
const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting);
|
||||
const [debugTileLayout, setDebugTileLayout] = useSetting(
|
||||
debugTileLayoutSetting,
|
||||
@@ -385,7 +400,7 @@ export const DeveloperSettingsTab: FC<Props> = ({
|
||||
id: client.getDeviceId() || "unknown",
|
||||
})}
|
||||
</p>
|
||||
{vm && <KeyRotationStatus vm={vm} />}
|
||||
{keyRotation !== null && <KeyRotationStatus info={keyRotation} />}
|
||||
<Separator />
|
||||
<FieldRow>
|
||||
<InputField
|
||||
|
||||
48
src/settings/DeveloperSettingsTabViewModel.test.ts
Normal file
48
src/settings/DeveloperSettingsTabViewModel.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { testScope } from "../utils/test";
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||
import {
|
||||
createDeveloperSettingsTabViewModel,
|
||||
outOfCallDeveloperSettingsTabViewModel,
|
||||
} from "./DeveloperSettingsTabViewModel";
|
||||
|
||||
describe("createDeveloperSettingsTabViewModel", () => {
|
||||
it("projects the key rotation state of the call", () => {
|
||||
const keyRotationSuppressed$ = new BehaviorSubject(false);
|
||||
const participantCount$ = new BehaviorSubject(5);
|
||||
const vm = createDeveloperSettingsTabViewModel(testScope(), {
|
||||
keyRotationSuppressed$,
|
||||
participantCount$,
|
||||
} as unknown as CallViewModel);
|
||||
|
||||
expect(vm.keyRotation$.value).toEqual({
|
||||
suppressed: false,
|
||||
participantCount: 5,
|
||||
});
|
||||
|
||||
participantCount$.next(50);
|
||||
keyRotationSuppressed$.next(true);
|
||||
|
||||
expect(vm.keyRotation$.value).toEqual({
|
||||
suppressed: true,
|
||||
participantCount: 50,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("outOfCallDeveloperSettingsTabViewModel", () => {
|
||||
it("has no key rotation state", () => {
|
||||
expect(outOfCallDeveloperSettingsTabViewModel.keyRotation$.value).toBe(
|
||||
null,
|
||||
);
|
||||
});
|
||||
});
|
||||
43
src/settings/DeveloperSettingsTabViewModel.ts
Normal file
43
src/settings/DeveloperSettingsTabViewModel.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { combineLatest } from "rxjs";
|
||||
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel.ts";
|
||||
import { type ObservableScope } from "../state/ObservableScope.ts";
|
||||
import { createStaticViewModel, type ViewModel } from "../state/ViewModel.ts";
|
||||
import { type DeveloperSettingsSnapshot } from "./DeveloperSettingsTab.tsx";
|
||||
|
||||
/**
|
||||
* Creates the ViewModel for the developer settings tab while in a call.
|
||||
*
|
||||
* Only the call state the tab actually renders is projected here, so that the
|
||||
* tab does not need to know about the CallViewModel.
|
||||
*
|
||||
* @param scope - ObservableScope that bounds the lifetime of derived behaviors.
|
||||
* @param callModel - The root CallViewModel; provides the key rotation state.
|
||||
*/
|
||||
export function createDeveloperSettingsTabViewModel(
|
||||
scope: ObservableScope,
|
||||
callModel: CallViewModel,
|
||||
): ViewModel<DeveloperSettingsSnapshot> {
|
||||
return {
|
||||
keyRotation$: scope.behavior(
|
||||
combineLatest(
|
||||
[callModel.keyRotationSuppressed$, callModel.participantCount$],
|
||||
(suppressed, participantCount) => ({ suppressed, participantCount }),
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The ViewModel for the developer settings tab outside of a call (lobby, user
|
||||
* menu), where no call state exists. All call specific fields are `null`.
|
||||
*/
|
||||
export const outOfCallDeveloperSettingsTabViewModel: ViewModel<DeveloperSettingsSnapshot> =
|
||||
createStaticViewModel<DeveloperSettingsSnapshot>({ keyRotation: null });
|
||||
@@ -29,12 +29,16 @@ import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
|
||||
import { Slider } from "../Slider";
|
||||
import { DeviceSelection } from "./DeviceSelection";
|
||||
import { useTrackProcessor } from "../livekit/TrackProcessorContext";
|
||||
import { DeveloperSettingsTab } from "./DeveloperSettingsTab";
|
||||
import {
|
||||
DeveloperSettingsTab,
|
||||
type DeveloperSettingsSnapshot,
|
||||
} from "./DeveloperSettingsTab";
|
||||
import { FieldRow, InputField } from "../input/Input";
|
||||
import { useSubmitRageshake } from "./submit-rageshake";
|
||||
import { useUrlParams } from "../UrlParams";
|
||||
import { useBehavior } from "../useBehavior";
|
||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel.ts";
|
||||
import { type ViewModel } from "../state/ViewModel.ts";
|
||||
import { outOfCallDeveloperSettingsTabViewModel } from "./DeveloperSettingsTabViewModel";
|
||||
|
||||
type SettingsTab =
|
||||
| "audio"
|
||||
@@ -58,7 +62,7 @@ interface Props {
|
||||
isLocal?: boolean;
|
||||
}[];
|
||||
/** Only available while in a call. Used by the developer tab. */
|
||||
vm?: CallViewModel;
|
||||
developerSettingsVm?: ViewModel<DeveloperSettingsSnapshot>;
|
||||
}
|
||||
|
||||
export const defaultSettingsTab: SettingsTab = "audio";
|
||||
@@ -71,7 +75,7 @@ export const SettingsModal: FC<Props> = ({
|
||||
client,
|
||||
roomId,
|
||||
livekitRooms,
|
||||
vm,
|
||||
developerSettingsVm,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -224,7 +228,7 @@ export const SettingsModal: FC<Props> = ({
|
||||
client={client}
|
||||
livekitRooms={livekitRooms}
|
||||
roomId={roomId}
|
||||
vm={vm}
|
||||
vm={developerSettingsVm ?? outOfCallDeveloperSettingsTabViewModel}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -42,6 +42,8 @@ import { MatrixRTCMode } from "../config/ConfigOptions";
|
||||
import { createCallFooterViewModel } from "../components/CallFooterViewModel";
|
||||
import { type FooterSnapshot } from "../components/CallFooter";
|
||||
import { type ViewModel } from "../state/ViewModel";
|
||||
import { createDeveloperSettingsTabViewModel } from "../settings/DeveloperSettingsTabViewModel";
|
||||
import { type DeveloperSettingsSnapshot } from "../settings/DeveloperSettingsTab";
|
||||
|
||||
mockConfig({ livekit: { livekit_service_url: "https://example.com" } });
|
||||
|
||||
@@ -140,6 +142,7 @@ export function getBasicCallViewModelEnvironment(
|
||||
): {
|
||||
vm: CallViewModel;
|
||||
footerVm: ViewModel<FooterSnapshot>;
|
||||
developerSettingsVm: ViewModel<DeveloperSettingsSnapshot>;
|
||||
rtcMemberships$: BehaviorSubject<CallMembership[]>;
|
||||
rtcSession: MockRTCSession;
|
||||
handRaisedSubject$: BehaviorSubject<Record<string, RaisedHandInfo>>;
|
||||
@@ -188,6 +191,7 @@ export function getBasicCallViewModelEnvironment(
|
||||
return {
|
||||
vm,
|
||||
footerVm,
|
||||
developerSettingsVm: createDeveloperSettingsTabViewModel(testScope(), vm),
|
||||
rtcMemberships$,
|
||||
rtcSession,
|
||||
handRaisedSubject$: handRaisedSubject$,
|
||||
|
||||
Reference in New Issue
Block a user