All my Friday work. Demoable!

This commit is contained in:
Robin
2025-08-29 18:46:24 +02:00
committed by Timo K
parent 386dc6c84d
commit e08f16f889
8 changed files with 220 additions and 298 deletions

View File

@@ -34,6 +34,7 @@ import {
Subject,
combineLatest,
concat,
concatMap,
distinctUntilChanged,
endWith,
filter,
@@ -121,6 +122,7 @@ import { E2eeType } from "../e2ee/e2eeType";
import { MatrixKeyProvider } from "../e2ee/matrixKeyProvider";
import { type ECConnectionState } from "../livekit/useECConnectionState";
import { Connection, PublishConnection } from "./Connection";
import { type MuteStates } from "./MuteStates";
export interface CallViewModelOptions {
encryptionSystem: EncryptionSystem;
@@ -447,6 +449,7 @@ export class CallViewModel extends ViewModel {
this.scope,
this.membershipsAndFocusMap$,
this.mediaDevices,
this.muteStates,
this.livekitE2EERoomOptions,
),
);
@@ -536,6 +539,14 @@ export class CallViewModel extends ViewModel {
return { start, stop };
}),
this.scope.share,
);
private readonly startConnection$ = this.connectionInstructions$.pipe(
concatMap(({ start }) => start),
);
private readonly stopConnection$ = this.connectionInstructions$.pipe(
concatMap(({ stop }) => stop),
);
private readonly userId = this.matrixRoom.client.getUserId();
@@ -623,15 +634,15 @@ export class CallViewModel extends ViewModel {
),
);
private readonly participants$ = this.scope
.behavior<
{
participant: LocalParticipant | RemoteParticipant;
member: RoomMember;
livekitRoom: LivekitRoom;
}[]
>(
from(this.localConnection).pipe(
private readonly participants$ = this.scope.behavior<
{
participant: LocalParticipant | RemoteParticipant;
member: RoomMember;
livekitRoom: LivekitRoom;
}[]
>(
from(this.localConnection)
.pipe(
switchMap((localConnection) => {
const memberError = (): never => {
throw new Error("No room member for call membership");
@@ -645,7 +656,7 @@ export class CallViewModel extends ViewModel {
return this.remoteConnections$.pipe(
switchMap((connections) =>
combineLatest(
[...connections.values()].map((c) =>
[localConnection, ...connections.values()].map((c) =>
c.publishingParticipants$.pipe(
map((ps) =>
ps.map(({ participant, membership }) => ({
@@ -663,14 +674,14 @@ export class CallViewModel extends ViewModel {
),
),
map((remoteParticipants) => [
...remoteParticipants.flat(1),
localParticipant,
...remoteParticipants.flat(1),
]),
);
}),
),
)
.pipe(startWith([]), pauseWhen(this.pretendToBeDisconnected$));
)
.pipe(startWith([]), pauseWhen(this.pretendToBeDisconnected$)),
);
/**
* Displaynames for each member of the call. This will disambiguate
@@ -681,18 +692,23 @@ export class CallViewModel extends ViewModel {
// than on Chrome/Firefox). This means it is important that we multicast the result so that we
// don't do this work more times than we need to. This is achieved by converting to a behavior:
public readonly memberDisplaynames$ = this.scope.behavior(
// React to call memberships and also display name updates
// (calculateDisplayName implicitly depends on the room member data)
combineLatest(
[
this.memberships$,
fromEvent(this.matrixRoom, RoomStateEvent.Members).pipe(
startWith(null),
pauseWhen(this.pretendToBeDisconnected$),
),
],
(memberships, _members) => {
const displaynameMap = new Map<string, string>();
merge(
// Handle call membership changes.
fromEvent(
this.matrixRTCSession,
MatrixRTCSessionEvent.MembershipsChanged,
),
// Handle room membership changes (and displayname updates)
fromEvent(this.matrixRoom, RoomStateEvent.Members),
// TODO: do we need: pauseWhen(this.pretendToBeDisconnected$),
).pipe(
startWith(null),
map(() => {
const memberships = this.matrixRTCSession.memberships;
const displaynameMap = new Map<string, string>([
["local", this.matrixRoom.getMember(this.userId!)!.rawDisplayName],
]);
const room = this.matrixRoom;
// We only consider RTC members for disambiguation as they are the only visible members.
@@ -1753,6 +1769,7 @@ export class CallViewModel extends ViewModel {
private readonly matrixRTCSession: MatrixRTCSession,
private readonly matrixRoom: MatrixRoom,
private readonly mediaDevices: MediaDevices,
private readonly muteStates: MuteStates,
private readonly options: CallViewModelOptions,
private readonly handsRaisedSubject$: Observable<
Record<string, RaisedHandInfo>
@@ -1774,12 +1791,12 @@ export class CallViewModel extends ViewModel {
// eslint-disable-next-line no-console
.catch((e) => console.error("failed to start publishing", e)),
);
this.connectionInstructions$
this.startConnection$
.pipe(this.scope.bind())
.subscribe(({ start, stop }) => {
for (const connection of start) void connection.start();
for (const connection of stop) connection.stop();
});
.subscribe((c) => void c.start());
this.stopConnection$.pipe(this.scope.bind()).subscribe((c) => c.stop());
combineLatest([this.localFocus, this.join$])
.pipe(this.scope.bind())
.subscribe(([localFocus]) => {
@@ -1789,6 +1806,7 @@ export class CallViewModel extends ViewModel {
this.options.encryptionSystem.kind !== E2eeType.PER_PARTICIPANT,
);
});
this.join$.pipe(this.scope.bind()).subscribe(() => {
leaveRTCSession(
this.matrixRTCSession,
@@ -1861,6 +1879,7 @@ function getE2eeOptions(
e2eeSystem: EncryptionSystem,
rtcSession: MatrixRTCSession,
): E2EEOptions | undefined {
return undefined;
if (e2eeSystem.kind === E2eeType.NONE) return undefined;
if (e2eeSystem.kind === E2eeType.PER_PARTICIPANT) {