Merge pull request #3961 from element-hq/toger5/view-model-call-footer-example

Implement fast switcher (+ ViewModel with snapshot example)
This commit is contained in:
Timo
2026-05-20 23:38:00 +08:00
committed by GitHub
24 changed files with 1652 additions and 490 deletions

View File

@@ -15,6 +15,7 @@ import {
} from "livekit-client";
import { type Room as MatrixRoom } from "matrix-js-sdk";
import {
BehaviorSubject,
catchError,
combineLatest,
distinctUntilChanged,
@@ -38,7 +39,6 @@ import {
tap,
throttleTime,
timer,
BehaviorSubject,
} from "rxjs";
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
import {
@@ -356,6 +356,9 @@ export interface CallViewModel {
*/
edgeToEdge$: Behavior<boolean>;
settingsOpen$: Behavior<boolean>;
setSettingsOpen$: Behavior<(open: boolean) => void>;
// audio routing
/**
* Whether audio is currently being output through the earpiece.
@@ -1397,6 +1400,10 @@ export function createCallViewModel$(
map((naturallyShowFooter) => naturallyShowFooter && showFooterUrlParams),
),
);
const settingsOpen$ = new BehaviorSubject(false);
const setSettingsOpen$ = constant((open: boolean) => {
settingsOpen$.next(open);
});
const showHeader$ = scope.behavior<boolean>(
windowMode$.pipe(
@@ -1751,6 +1758,8 @@ export function createCallViewModel$(
showNameTags$,
showHeader$: showHeader$,
showFooter$: showFooter$,
settingsOpen$: settingsOpen$,
setSettingsOpen$: setSettingsOpen$,
edgeToEdge$,
earpieceMode$: earpieceMode$,
audioOutputSwitcher$: audioOutputSwitcher$,

49
src/state/ViewModel.ts Normal file
View File

@@ -0,0 +1,49 @@
/*
Copyright 2026 Element Software Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import { BehaviorSubject } from "rxjs";
import { useState, useEffect } from "react";
import { type Behavior } from "./Behavior";
export type ViewModel<Snapshot> = {
[K in keyof Snapshot as `${string & K}$`]: Behavior<Snapshot[K]>;
};
/**
* This allows to build a view model (or Partial view model)
* with BehaviorSubjects.
* It can be used in tests and for simplifying view model creation for non reactive snapshot parameters.
*
* @param snapshot The snapshot values this view model with start with. ({a: number, b: string})
* @returns A view model: ({a$: BehaviroSubject<number>, b$: BehaviroSubject<string>}) (note the automatic addition of $ at the end of the keys)
*/
export function createStaticViewModel<Snapshot>(
snapshot: Snapshot,
): ViewModel<Snapshot> {
const vm = {} as ViewModel<Snapshot>;
for (const key in snapshot) {
(vm as Record<string, Behavior<unknown>>)[`${key}$`] = new BehaviorSubject(
snapshot[key],
);
}
return vm;
}
export function useStaticViewModel<Snapshot>(
snapshot: Snapshot,
): ViewModel<Snapshot> {
const [vm] = useState(() => createStaticViewModel(snapshot));
useEffect(() => {
for (const key in snapshot) {
(vm as unknown as Record<string, BehaviorSubject<unknown>>)[
`${key}$`
].next(snapshot[key]);
}
}, [snapshot, vm]);
return vm;
}