Refactor Matrix/LiveKit session merging

- Replace MatrixLivekitItem with MatrixLivekitMember, add displayName$
  and participantId, and use explicit LiveKit participant types
- Make sessionBehaviors$ accept a props object and return a typed
  RxRtcSession
- Update CallViewModel to use the new session behaviors, rebuild media
  items from matrixLivekitMembers, handle missing connections and use
  participantId-based keys
- Change localMembership/localTransport to accept Behavior-based
  options, read options.value for enterRTCSession, and fix advertised
  transport selection order
- Update tests and minor UI adjustments (settings modal livekitRooms
  stubbed) and fix JSON formatting in locales
This commit is contained in:
Timo K
2025-11-05 17:55:36 +01:00
parent 107ef16d94
commit 4d0de2fb71
10 changed files with 172 additions and 130 deletions

View File

@@ -5,7 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import { type Participant as LivekitParticipant } from "livekit-client";
import {
type LocalParticipant as LocalLivekitParticipant,
type RemoteParticipant as RemoteLivekitParticipant,
} from "livekit-client";
import {
type LivekitTransport,
type CallMembership,
@@ -27,22 +30,23 @@ import { type Connection } from "./Connection";
* `livekitParticipant` can be undefined if the member is not yet connected to the livekit room
* or if it has no livekit transport at all.
*/
export interface MatrixLivekitItem {
export interface MatrixLivekitMember {
membership: CallMembership;
displayName: string;
participant?: LivekitParticipant;
displayName$: Behavior<string>;
participant?: LocalLivekitParticipant | RemoteLivekitParticipant;
connection?: Connection;
/**
* TODO Try to remove this! Its waaay to much information.
* Just get the member's avatar
* @deprecated
*/
member?: RoomMember;
member: RoomMember;
mxcAvatarUrl?: string;
participantId: string;
}
// Alternative structure idea:
// const livekitMatrixItems$ = (callMemberships$,connectionManager,scope): Observable<MatrixLivekitItem[]> => {
// const livekitMatrixMember$ = (callMemberships$,connectionManager,scope): Observable<MatrixLivekitMember[]> => {
/**
* Combines MatrixRtc and Livekit worlds.
@@ -52,13 +56,13 @@ export interface MatrixLivekitItem {
* - an observable of CallMembership[] to track the call members (The matrix side)
* - a `ConnectionManager` for the lk rooms (The livekit side)
* - out (via public Observable):
* - `remoteMatrixLivekitItems` an observable of MatrixLivekitItem[] to track the remote members and associated livekit data.
* - `remoteMatrixLivekitMember` an observable of MatrixLivekitMember[] to track the remote members and associated livekit data.
*/
export class MatrixLivekitMerger {
/**
* Stream of all the call members and their associated livekit data (if available).
*/
public matrixLivekitItems$: Behavior<MatrixLivekitItem[]>;
public matrixLivekitMember$: Behavior<MatrixLivekitMember[]>;
// private readonly logger: Logger;
@@ -79,7 +83,7 @@ export class MatrixLivekitMerger {
) {
// this.logger = parentLogger.getChild("MatrixLivekitMerger");
this.matrixLivekitItems$ = this.scope.behavior(
this.matrixLivekitMember$ = this.scope.behavior(
this.start$().pipe(startWith([])),
);
}
@@ -87,7 +91,7 @@ export class MatrixLivekitMerger {
// =======================================
/// PRIVATES
// =======================================
private start$(): Observable<MatrixLivekitItem[]> {
private start$(): Observable<MatrixLivekitMember[]> {
const displaynameMap$ = memberDisplaynames$(
this.scope,
this.matrixRoom,
@@ -102,10 +106,9 @@ export class MatrixLivekitMerger {
return combineLatest([
membershipsWithTransport$,
this.connectionManager.connectionManagerData$,
displaynameMap$,
]).pipe(
map(([memberships, managerData, displayNameMap]) => {
const items: MatrixLivekitItem[] = memberships.map(
map(([memberships, managerData]) => {
const items: MatrixLivekitMember[] = memberships.map(
({ membership, transport }) => {
// TODO! cannot use membership.membershipID yet, Currently its hardcoded by the jwt service to
const participantId = /*membership.membershipID*/ `${membership.userId}:${membership.deviceId}`;
@@ -123,14 +126,23 @@ export class MatrixLivekitMerger {
const connection = transport
? managerData.getConnectionForTransport(transport)
: undefined;
const displayName$ = this.scope.behavior(
displaynameMap$.pipe(
map(
(displayNameMap) =>
displayNameMap.get(membership.membershipID) ?? "---",
),
),
);
return {
participant,
membership,
connection,
// 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) ?? "---",
displayName$,
mxcAvatarUrl: member?.getMxcAvatarUrl(),
participantId,
};
},
);