start moving over/removing things from the CallViewModel

This commit is contained in:
Timo K
2025-10-30 01:13:06 +01:00
parent 6b513534f1
commit c8ef8d6a24
7 changed files with 231 additions and 469 deletions

View File

@@ -17,7 +17,7 @@ import {
} from "vitest";
import { BehaviorSubject, of } from "rxjs";
import {
ConnectionState,
ConnectionState as LivekitConnectionState,
type LocalParticipant,
type RemoteParticipant,
type Room as LivekitRoom,
@@ -36,12 +36,11 @@ import {
type ConnectionOpts,
type ConnectionState,
type PublishingParticipant,
RemoteConnection,
Connection,
} from "./Connection.ts";
import { ObservableScope } from "../ObservableScope.ts";
import { type OpenIDClientParts } from "../../livekit/openIDSFU.ts";
import { FailToGetOpenIdToken } from "../../utils/errors.ts";
import { PublishConnection } from "../ownMember/Publisher.ts";
import { mockMediaDevices, mockMuteStates } from "../../utils/test.ts";
import type { ProcessorState } from "../../livekit/TrackProcessorContext.tsx";
import { type MuteStates } from "../MuteStates.ts";

View File

@@ -42,11 +42,11 @@ export type ParticipantByMemberIdMap = Map<
export class ConnectionManager {
private livekitRoomFactory: () => LivekitRoom;
public constructor(
private client: MatrixClient,
private scope: ObservableScope,
private client: MatrixClient,
private devices: MediaDevices,
private processorState: ProcessorState,
private e2eeLivekitOptions$: Behavior<E2EEOptions | undefined>,
private processorState$: Behavior<ProcessorState>,
private e2eeLivekitOptions: E2EEOptions | undefined,
private logger?: Logger,
livekitRoomFactory?: () => LivekitRoom,
) {
@@ -55,8 +55,8 @@ export class ConnectionManager {
new LivekitRoom(
generateRoomOption(
this.devices,
this.processorState,
this.e2eeLivekitOptions$.value,
this.processorState$.value,
this.e2eeLivekitOptions,
),
);
this.livekitRoomFactory = livekitRoomFactory ?? defaultFactory;

View File

@@ -5,11 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import { type Room, type RoomMember, RoomStateEvent } from "matrix-js-sdk";
import { type RoomMember, RoomStateEvent } from "matrix-js-sdk";
import { combineLatest, fromEvent, type Observable, startWith } from "rxjs";
import { type CallMembership } from "matrix-js-sdk/lib/matrixrtc";
import { logger } from "matrix-js-sdk/lib/logger";
import { type Room as MatrixRoom } from "matrix-js-sdk/lib/matrix";
// eslint-disable-next-line rxjs/no-internal
import { type HasEventTargetAddRemove } from "rxjs/internal/observable/fromEvent";
import { type ObservableScope } from "../ObservableScope";
import {
@@ -22,11 +24,13 @@ import { type Behavior } from "../Behavior";
* Displayname for each member of the call. This will disambiguate
* any displayname that clashes with another member. Only members
* joined to the call are considered here.
*
* @returns Map<member.id, displayname> uses the rtc member idenitfier as the key.
*/
// don't do this work more times than we need to. This is achieved by converting to a behavior:
export const memberDisplaynames$ = (
scope: ObservableScope,
matrixRoom: Room,
matrixRoom: Pick<MatrixRoom, "getMember"> & HasEventTargetAddRemove<unknown>,
memberships$: Observable<CallMembership[]>,
userId: string,
deviceId: string,
@@ -73,7 +77,7 @@ export const memberDisplaynames$ = (
export function getRoomMemberFromRtcMember(
rtcMember: CallMembership,
room: MatrixRoom,
room: Pick<MatrixRoom, "getMember">,
): { id: string; member: RoomMember | undefined } {
return {
id: rtcMember.userId + ":" + rtcMember.deviceId,

View File

@@ -5,38 +5,23 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import {
type RemoteParticipant,
type Participant as LivekitParticipant,
} from "livekit-client";
import { type Participant as LivekitParticipant } from "livekit-client";
import {
isLivekitTransport,
type LivekitTransport,
type CallMembership,
} from "matrix-js-sdk/lib/matrixrtc";
import { combineLatest, map, startWith, type Observable } from "rxjs";
// eslint-disable-next-line rxjs/no-internal
import { type HasEventTargetAddRemove } from "rxjs/internal/observable/fromEvent";
import type { Room as MatrixRoom, RoomMember } from "matrix-js-sdk";
// import type { Logger } from "matrix-js-sdk/lib/logger";
import { type Behavior } from "../Behavior";
import { type ObservableScope } from "../ObservableScope";
import { type ConnectionManager } from "./ConnectionManager";
import { getRoomMemberFromRtcMember } from "./displayname";
/**
* Represents participant publishing or expected to publish on the connection.
* It is paired with its associated rtc membership.
*/
export type PublishingParticipant = {
/**
* The LiveKit participant publishing on this connection, or undefined if the participant is not currently (yet) connected to the livekit room.
*/
participant: RemoteParticipant | undefined;
/**
* The rtc call membership associated with this participant.
*/
membership: CallMembership;
};
import { getRoomMemberFromRtcMember, memberDisplaynames$ } from "./displayname";
import { type Connection } from "./Connection";
/**
* Represent a matrix call member and his associated livekit participation.
@@ -45,10 +30,16 @@ export type PublishingParticipant = {
*/
export interface MatrixLivekitItem {
membership: CallMembership;
livekitParticipant?: LivekitParticipant;
//TODO Try to remove this! Its waaay to much information
// Just use to get the member's avatar
displayName: string;
participant?: LivekitParticipant;
connection?: Connection;
/**
* TODO Try to remove this! Its waaay to much information.
* Just get the member's avatar
* @deprecated
*/
member?: RoomMember;
mxcAvatarUrl?: string;
}
// Alternative structure idea:
@@ -73,13 +64,17 @@ export class MatrixLivekitMerger {
// private readonly logger: Logger;
public constructor(
private scope: ObservableScope,
private memberships$: Observable<CallMembership[]>,
private connectionManager: ConnectionManager,
private scope: ObservableScope,
// TODO this is too much information for that class,
// apparently needed to get a room member to later get the Avatar
// => Extract an AvatarService instead?
private matrixRoom: MatrixRoom,
// Better with just `getMember`
private matrixRoom: Pick<MatrixRoom, "getMember"> &
HasEventTargetAddRemove<unknown>,
private userId: string,
private deviceId: string,
// parentLogger: Logger,
) {
// this.logger = parentLogger.getChild("MatrixLivekitMerger");
@@ -93,6 +88,13 @@ export class MatrixLivekitMerger {
/// PRIVATES
// =======================================
private start$(): Observable<MatrixLivekitItem[]> {
const displaynameMap$ = memberDisplaynames$(
this.scope,
this.matrixRoom,
this.memberships$,
this.userId,
this.deviceId,
);
const membershipsWithTransport$ =
this.mapMembershipsToMembershipWithTransport$();
@@ -101,26 +103,33 @@ export class MatrixLivekitMerger {
return combineLatest([
membershipsWithTransport$,
this.connectionManager.allParticipantsByMemberId$,
displaynameMap$,
]).pipe(
map(([memberships, participantsByMemberId]) => {
const items = memberships.map(({ membership, transport }) => {
const participantsWithConnection = participantsByMemberId.get(
membership.membershipID,
);
const participant =
transport &&
participantsWithConnection?.find((p) =>
areLivekitTransportsEqual(p.connection.transport, transport),
map(([memberships, participantsByMemberId, displayNameMap]) => {
const items: MatrixLivekitItem[] = memberships.map(
({ membership, transport }) => {
const participantsWithConnection = participantsByMemberId.get(
membership.membershipID,
);
return {
livekitParticipant: participant,
membership,
// This makes sense to add the the js-sdk callMembership (we only need the avatar so probably the call memberhsip just should aquire the avatar)
member:
// Why a member error? if we have a call membership there is a room member
getRoomMemberFromRtcMember(membership, this.matrixRoom)?.member,
} as MatrixLivekitItem;
});
const participant =
transport &&
participantsWithConnection?.find((p) =>
areLivekitTransportsEqual(p.connection.transport, transport),
);
const member = getRoomMemberFromRtcMember(
membership,
this.matrixRoom,
)?.member;
return {
...participant,
membership,
// This makes sense to add the the js-sdk callMembership (we only need the avatar so probably the call memberhsip just should aquire the avatar)
member,
displayName: displayNameMap.get(membership.membershipID) ?? "---",
mxcAvatarUrl: member?.getMxcAvatarUrl(),
};
},
);
return items;
}),
);