Split MatrixLivekitMembers more verbosely into two types

This commit is contained in:
Robin
2025-12-15 13:14:45 -05:00
parent 00d4b8e985
commit 8a18e70e20
4 changed files with 46 additions and 34 deletions

View File

@@ -116,7 +116,7 @@ import { createConnectionManager$ } from "./remoteMembers/ConnectionManager.ts";
import { import {
createMatrixLivekitMembers$, createMatrixLivekitMembers$,
type TaggedParticipant, type TaggedParticipant,
type MatrixLivekitMember, type LocalMatrixLivekitMember,
} from "./remoteMembers/MatrixLivekitMembers.ts"; } from "./remoteMembers/MatrixLivekitMembers.ts";
import { import {
type AutoLeaveReason, type AutoLeaveReason,
@@ -510,7 +510,7 @@ export function createCallViewModel$(
), ),
); );
const localMatrixLivekitMember$: Behavior<MatrixLivekitMember<"local"> | null> = const localMatrixLivekitMember$: Behavior<LocalMatrixLivekitMember | null> =
scope.behavior( scope.behavior(
localRtcMembership$.pipe( localRtcMembership$.pipe(
filterBehavior((membership) => membership !== null), filterBehavior((membership) => membership !== null),
@@ -682,10 +682,8 @@ export function createCallViewModel$(
let localParticipantId: string | undefined = undefined; let localParticipantId: string | undefined = undefined;
// add local member if available // add local member if available
if (localMatrixLivekitMember) { if (localMatrixLivekitMember) {
const { userId, connection$, membership$ } = const { userId, participant, connection$, membership$ } =
localMatrixLivekitMember; localMatrixLivekitMember;
const participant: TaggedParticipant =
localMatrixLivekitMember.participant; // Widen the type
localParticipantId = `${userId}:${membership$.value.deviceId}`; // should be membership$.value.membershipID which is not optional localParticipantId = `${userId}:${membership$.value.deviceId}`; // should be membership$.value.membershipID which is not optional
// const participantId = membership$.value.membershipID; // const participantId = membership$.value.membershipID;
if (localParticipantId) { if (localParticipantId) {
@@ -695,7 +693,7 @@ export function createCallViewModel$(
dup, dup,
localParticipantId, localParticipantId,
userId, userId,
participant, participant satisfies TaggedParticipant as TaggedParticipant, // Widen the type safely
connection$, connection$,
], ],
data: undefined, data: undefined,
@@ -727,7 +725,7 @@ export function createCallViewModel$(
dup, dup,
participantId, participantId,
userId, userId,
participant$, participant,
connection$, connection$,
) => { ) => {
const livekitRoom$ = scope.behavior( const livekitRoom$ = scope.behavior(
@@ -746,7 +744,7 @@ export function createCallViewModel$(
scope, scope,
`${participantId}:${dup}`, `${participantId}:${dup}`,
userId, userId,
participant$, participant,
options.encryptionSystem, options.encryptionSystem,
livekitRoom$, livekitRoom$,
focusUrl$, focusUrl$,

View File

@@ -15,7 +15,7 @@ import { combineLatest, map, type Observable } from "rxjs";
import { type IConnectionManager } from "./ConnectionManager.ts"; import { type IConnectionManager } from "./ConnectionManager.ts";
import { import {
type MatrixLivekitMember, type RemoteMatrixLivekitMember,
createMatrixLivekitMembers$, createMatrixLivekitMembers$,
} from "./MatrixLivekitMembers.ts"; } from "./MatrixLivekitMembers.ts";
import { import {
@@ -100,7 +100,7 @@ test("should signal participant not yet connected to livekit", () => {
}); });
expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe("a", { expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe("a", {
a: expect.toSatisfy((data: MatrixLivekitMember<"remote">[]) => { a: expect.toSatisfy((data: RemoteMatrixLivekitMember[]) => {
expect(data.length).toEqual(1); expect(data.length).toEqual(1);
expectObservable(data[0].membership$).toBe("a", { expectObservable(data[0].membership$).toBe("a", {
a: bobMembership, a: bobMembership,
@@ -180,7 +180,7 @@ test("should signal participant on a connection that is publishing", () => {
}); });
expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe("a", { expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe("a", {
a: expect.toSatisfy((data: MatrixLivekitMember<"remote">[]) => { a: expect.toSatisfy((data: RemoteMatrixLivekitMember[]) => {
expect(data.length).toEqual(1); expect(data.length).toEqual(1);
expectObservable(data[0].membership$).toBe("a", { expectObservable(data[0].membership$).toBe("a", {
a: bobMembership, a: bobMembership,
@@ -231,7 +231,7 @@ test("should signal participant on a connection that is not publishing", () => {
}); });
expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe("a", { expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe("a", {
a: expect.toSatisfy((data: MatrixLivekitMember<"remote">[]) => { a: expect.toSatisfy((data: RemoteMatrixLivekitMember[]) => {
expect(data.length).toEqual(1); expect(data.length).toEqual(1);
expectObservable(data[0].membership$).toBe("a", { expectObservable(data[0].membership$).toBe("a", {
a: bobMembership, a: bobMembership,
@@ -296,7 +296,7 @@ describe("Publication edge case", () => {
expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe( expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe(
"a", "a",
{ {
a: expect.toSatisfy((data: MatrixLivekitMember<"remote">[]) => { a: expect.toSatisfy((data: RemoteMatrixLivekitMember[]) => {
expect(data.length).toEqual(2); expect(data.length).toEqual(2);
expectObservable(data[0].membership$).toBe("a", { expectObservable(data[0].membership$).toBe("a", {
a: bobMembership, a: bobMembership,
@@ -362,7 +362,7 @@ describe("Publication edge case", () => {
expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe( expectObservable(matrixLivekitMember$.pipe(map((e) => e.value))).toBe(
"a", "a",
{ {
a: expect.toSatisfy((data: MatrixLivekitMember<"remote">[]) => { a: expect.toSatisfy((data: RemoteMatrixLivekitMember[]) => {
expect(data.length).toEqual(2); expect(data.length).toEqual(2);
expectObservable(data[0].membership$).toBe("a", { expectObservable(data[0].membership$).toBe("a", {
a: bobMembership, a: bobMembership,

View File

@@ -21,30 +21,44 @@ import { generateItemsWithEpoch } from "../../../utils/observable";
const logger = rootLogger.getChild("[MatrixLivekitMembers]"); const logger = rootLogger.getChild("[MatrixLivekitMembers]");
/** interface LocalTaggedParticipant {
* A dynamic participant value with a static tag to tell what kind of type: "local";
* participant it can be (local vs. remote). value$: Behavior<LocalParticipant | null>;
*/ }
interface RemoteTaggedParticipant {
type: "remote";
value$: Behavior<RemoteParticipant | null>;
}
export type TaggedParticipant = export type TaggedParticipant =
| { type: "local"; value$: Behavior<LocalParticipant | null> } | LocalTaggedParticipant
| { type: "remote"; value$: Behavior<RemoteParticipant | null> }; | RemoteTaggedParticipant;
/** interface MatrixLivekitMember {
* Represents a Matrix call member and their associated LiveKit participation.
* `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 MatrixLivekitMember<
ParticipantType extends TaggedParticipant["type"],
> {
membership$: Behavior<CallMembership>; membership$: Behavior<CallMembership>;
participant: TaggedParticipant & { type: ParticipantType };
connection$: Behavior<Connection | null>; connection$: Behavior<Connection | null>;
// participantId: string; We do not want a participantId here since it will be generated by the jwt // participantId: string; We do not want a participantId here since it will be generated by the jwt
// TODO decide if we can also drop the userId. Its in the matrix membership anyways. // TODO decide if we can also drop the userId. Its in the matrix membership anyways.
userId: string; userId: string;
} }
/**
* Represents the local Matrix call member and their associated LiveKit participation.
* `livekitParticipant` can be null if the member is not yet connected to the livekit room
* or if it has no livekit transport at all.
*/
export interface LocalMatrixLivekitMember extends MatrixLivekitMember {
participant: LocalTaggedParticipant;
}
/**
* Represents a remote Matrix call member and their associated LiveKit participation.
* `livekitParticipant` can be null if the member is not yet connected to the livekit room
* or if it has no livekit transport at all.
*/
export interface RemoteMatrixLivekitMember extends MatrixLivekitMember {
participant: RemoteTaggedParticipant;
}
interface Props { interface Props {
scope: ObservableScope; scope: ObservableScope;
membershipsWithTransport$: Behavior< membershipsWithTransport$: Behavior<
@@ -66,7 +80,7 @@ export function createMatrixLivekitMembers$({
scope, scope,
membershipsWithTransport$, membershipsWithTransport$,
connectionManager, connectionManager,
}: Props): Behavior<Epoch<MatrixLivekitMember<"remote">[]>> { }: Props): Behavior<Epoch<RemoteMatrixLivekitMember[]>> {
/** /**
* Stream of all the call members and their associated livekit data (if available). * Stream of all the call members and their associated livekit data (if available).
*/ */

View File

@@ -29,7 +29,7 @@ import { type ProcessorState } from "../../../livekit/TrackProcessorContext.tsx"
import { import {
areLivekitTransportsEqual, areLivekitTransportsEqual,
createMatrixLivekitMembers$, createMatrixLivekitMembers$,
type MatrixLivekitMember, type RemoteMatrixLivekitMember,
} from "./MatrixLivekitMembers.ts"; } from "./MatrixLivekitMembers.ts";
import { createConnectionManager$ } from "./ConnectionManager.ts"; import { createConnectionManager$ } from "./ConnectionManager.ts";
import { membershipsAndTransports$ } from "../../SessionBehaviors.ts"; import { membershipsAndTransports$ } from "../../SessionBehaviors.ts";
@@ -132,7 +132,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
}); });
expectObservable(matrixLivekitItems$).toBe(vMarble, { expectObservable(matrixLivekitItems$).toBe(vMarble, {
a: expect.toSatisfy((e: Epoch<MatrixLivekitMember<"remote">[]>) => { a: expect.toSatisfy((e: Epoch<RemoteMatrixLivekitMember[]>) => {
const items = e.value; const items = e.value;
expect(items.length).toBe(1); expect(items.length).toBe(1);
const item = items[0]!; const item = items[0]!;
@@ -152,7 +152,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
}); });
return true; return true;
}), }),
b: expect.toSatisfy((e: Epoch<MatrixLivekitMember<"remote">[]>) => { b: expect.toSatisfy((e: Epoch<RemoteMatrixLivekitMember[]>) => {
const items = e.value; const items = e.value;
expect(items.length).toBe(2); expect(items.length).toBe(2);
@@ -189,7 +189,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
} }
return true; return true;
}), }),
c: expect.toSatisfy((e: Epoch<MatrixLivekitMember<"remote">[]>) => { c: expect.toSatisfy((e: Epoch<RemoteMatrixLivekitMember[]>) => {
const items = e.value; const items = e.value;
expect(items.length).toBe(3); expect(items.length).toBe(3);