finish up most of our helper classes. there are no lint issues in the
new classes. The CallViewModel is not done yet however
This commit is contained in:
@@ -87,29 +87,31 @@ export class ConnectionManagerData {
|
||||
export class ConnectionManager {
|
||||
private readonly logger: Logger;
|
||||
|
||||
private running$ = new BehaviorSubject(true);
|
||||
/**
|
||||
* Crete a `ConnectionManager`
|
||||
* @param scope the observable scope used by this object.
|
||||
* @param connectionFactory used to create new connections.
|
||||
* @param _transportsSubscriptions$ A list of Behaviors each containing a LIST of LivekitTransport.
|
||||
* Each of these behaviors can be interpreted as subscribed list of transports.
|
||||
*
|
||||
* Using `registerTransports` independent external modules can control what connections
|
||||
* are created by the ConnectionManager.
|
||||
*
|
||||
* The connection manager will remove all duplicate transports in each subscibed list.
|
||||
*
|
||||
* See `unregisterAllTransports` and `unregisterTransport` for details on how to unsubscribe.
|
||||
*/
|
||||
public constructor(
|
||||
private readonly scope: ObservableScope,
|
||||
private readonly connectionFactory: ConnectionFactory,
|
||||
private readonly inputTransports$: Behavior<LivekitTransport[]>,
|
||||
logger: Logger,
|
||||
) {
|
||||
this.logger = logger.getChild("ConnectionManager");
|
||||
scope.onEnd(() => this.running$.next(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of Behaviors each containing a LIST of LivekitTransport.
|
||||
* Each of these behaviors can be interpreted as subscribed list of transports.
|
||||
*
|
||||
* Using `registerTransports` independent external modules can control what connections
|
||||
* are created by the ConnectionManager.
|
||||
*
|
||||
* The connection manager will remove all duplicate transports in each subscibed list.
|
||||
*
|
||||
* See `unregisterAllTransports` and `unregisterTransport` for details on how to unsubscribe.
|
||||
*/
|
||||
private readonly transportsSubscriptions$ = new BehaviorSubject<
|
||||
Behavior<LivekitTransport[]>[]
|
||||
>([]);
|
||||
|
||||
/**
|
||||
* All transports currently managed by the ConnectionManager.
|
||||
*
|
||||
@@ -119,15 +121,10 @@ export class ConnectionManager {
|
||||
* externally this is modified via `registerTransports()`.
|
||||
*/
|
||||
private readonly transports$ = this.scope.behavior(
|
||||
this.transportsSubscriptions$.pipe(
|
||||
switchMap((subscriptions) =>
|
||||
combineLatest(subscriptions).pipe(
|
||||
map((transportsNested) => transportsNested.flat()),
|
||||
map(removeDuplicateTransports),
|
||||
),
|
||||
),
|
||||
combineLatest([this.running$, this.inputTransports$]).pipe(
|
||||
map(([running, transports]) => (running ? transports : [])),
|
||||
map(removeDuplicateTransports),
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -163,60 +160,6 @@ export class ConnectionManager {
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Add an a Behavior containing a list of transports to this ConnectionManager.
|
||||
*
|
||||
* The intended usage is:
|
||||
* - create a ConnectionManager
|
||||
* - register one `transports$` behavior using registerTransports
|
||||
* - add new connections to the `ConnectionManager` by updating the `transports$` behavior
|
||||
* - remove a single connection by removing the transport.
|
||||
* - remove this subscription by calling `unregisterTransports` and passing
|
||||
* the same `transports$` behavior reference.
|
||||
* @param transports$ The Behavior containing a list of transports to subscribe to.
|
||||
*/
|
||||
public registerTransports(transports$: Behavior<LivekitTransport[]>): void {
|
||||
if (!this.transportsSubscriptions$.value.some((t$) => t$ === transports$)) {
|
||||
this.transportsSubscriptions$.next(
|
||||
this.transportsSubscriptions$.value.concat(transports$),
|
||||
);
|
||||
}
|
||||
// // After updating the subscriptions our connection list is also updated.
|
||||
// return transports$.value
|
||||
// .map((transport) => {
|
||||
// const isConnectionForTransport = (connection: Connection): boolean =>
|
||||
// areLivekitTransportsEqual(connection.transport, transport);
|
||||
// return this.connections$.value.find(isConnectionForTransport);
|
||||
// })
|
||||
// .filter((c) => c !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from the given transports.
|
||||
* @param transports$ The behavior to unsubscribe from
|
||||
* @returns
|
||||
*/
|
||||
public unregisterTransports(
|
||||
transports$: Behavior<LivekitTransport[]>,
|
||||
): boolean {
|
||||
const subscriptions = this.transportsSubscriptions$.value;
|
||||
const subscriptionsUnregistered = subscriptions.filter(
|
||||
(t$) => t$ !== transports$,
|
||||
);
|
||||
const canUnregister =
|
||||
subscriptions.length !== subscriptionsUnregistered.length;
|
||||
if (canUnregister)
|
||||
this.transportsSubscriptions$.next(subscriptionsUnregistered);
|
||||
return canUnregister;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all transports.
|
||||
*/
|
||||
public unregisterAllTransports(): void {
|
||||
this.transportsSubscriptions$.next([]);
|
||||
}
|
||||
|
||||
public connectionManagerData$: Behavior<ConnectionManagerData> =
|
||||
this.scope.behavior(
|
||||
this.connections$.pipe(
|
||||
|
||||
@@ -6,18 +6,12 @@ Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type RoomMember, RoomStateEvent } from "matrix-js-sdk";
|
||||
import {
|
||||
combineLatest,
|
||||
fromEvent,
|
||||
map,
|
||||
type Observable,
|
||||
startWith,
|
||||
} from "rxjs";
|
||||
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 NodeStyleEventEmitter } from "rxjs/internal/observable/fromEvent";
|
||||
|
||||
import { type ObservableScope } from "../ObservableScope";
|
||||
import {
|
||||
@@ -36,20 +30,21 @@ import { type Behavior } from "../Behavior";
|
||||
// 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: Pick<MatrixRoom, "getMember"> & HasEventTargetAddRemove<unknown>,
|
||||
matrixRoom: Pick<MatrixRoom, "getMember"> & NodeStyleEventEmitter,
|
||||
memberships$: Observable<CallMembership[]>,
|
||||
userId: string,
|
||||
deviceId: string,
|
||||
): Behavior<Map<string, string>> =>
|
||||
scope.behavior(
|
||||
combineLatest([
|
||||
// Handle call membership changes
|
||||
memberships$,
|
||||
// Additionally handle display name changes (implicitly reacting to them)
|
||||
fromEvent(matrixRoom, RoomStateEvent.Members).pipe(startWith(null)),
|
||||
// TODO: do we need: pauseWhen(this.pretendToBeDisconnected$),
|
||||
]).pipe(
|
||||
map((memberships, _displaynames) => {
|
||||
combineLatest(
|
||||
[
|
||||
// Handle call membership changes
|
||||
memberships$,
|
||||
// Additionally handle display name changes (implicitly reacting to them)
|
||||
fromEvent(matrixRoom, RoomStateEvent.Members).pipe(startWith(null)),
|
||||
// TODO: do we need: pauseWhen(this.pretendToBeDisconnected$),
|
||||
],
|
||||
(memberships, _displaynames) => {
|
||||
const displaynameMap = new Map<string, string>([
|
||||
[
|
||||
`${userId}:${deviceId}`,
|
||||
@@ -76,7 +71,7 @@ export const memberDisplaynames$ = (
|
||||
);
|
||||
}
|
||||
return displaynameMap;
|
||||
}),
|
||||
},
|
||||
),
|
||||
new Map<string, string>(),
|
||||
);
|
||||
|
||||
@@ -7,13 +7,12 @@ Please see LICENSE in the repository root for full details.
|
||||
|
||||
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 NodeStyleEventEmitter } from "rxjs/src/internal/observable/fromEvent.ts";
|
||||
import { type NodeStyleEventEmitter } from "rxjs/internal/observable/fromEvent";
|
||||
|
||||
import type { Room as MatrixRoom, RoomMember } from "matrix-js-sdk";
|
||||
// import type { Logger } from "matrix-js-sdk/lib/logger";
|
||||
@@ -65,7 +64,9 @@ export class MatrixLivekitMerger {
|
||||
|
||||
public constructor(
|
||||
private scope: ObservableScope,
|
||||
private memberships$: Observable<CallMembership[]>,
|
||||
private membershipsWithTransport$: Behavior<
|
||||
{ membership: CallMembership; transport?: LivekitTransport }[]
|
||||
>,
|
||||
private connectionManager: ConnectionManager,
|
||||
// TODO this is too much information for that class,
|
||||
// apparently needed to get a room member to later get the Avatar
|
||||
@@ -90,14 +91,13 @@ export class MatrixLivekitMerger {
|
||||
const displaynameMap$ = memberDisplaynames$(
|
||||
this.scope,
|
||||
this.matrixRoom,
|
||||
this.memberships$,
|
||||
this.membershipsWithTransport$.pipe(
|
||||
map((v) => v.map((v) => v.membership)),
|
||||
),
|
||||
this.userId,
|
||||
this.deviceId,
|
||||
);
|
||||
const membershipsWithTransport$ =
|
||||
this.mapMembershipsToMembershipWithTransport$();
|
||||
|
||||
this.startFeedingConnectionManager(membershipsWithTransport$);
|
||||
const membershipsWithTransport$ = this.membershipsWithTransport$;
|
||||
|
||||
return combineLatest([
|
||||
membershipsWithTransport$,
|
||||
@@ -138,48 +138,6 @@ export class MatrixLivekitMerger {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private startFeedingConnectionManager(
|
||||
membershipsWithTransport$: Behavior<
|
||||
{ membership: CallMembership; transport?: LivekitTransport }[]
|
||||
>,
|
||||
): void {
|
||||
const transports$ = this.scope.behavior(
|
||||
membershipsWithTransport$.pipe(
|
||||
map((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
|
||||
),
|
||||
);
|
||||
// duplicated transports will be elimiated by the connection manager
|
||||
this.connectionManager.registerTransports(transports$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the transports used by ourselves, plus all other MatrixRTC session
|
||||
* members. For completeness this also lists the preferred transport and
|
||||
* whether we are in multi-SFU mode or sticky events mode (because
|
||||
* advertisedTransport$ wants to read them at the same time, and bundling data
|
||||
* together when it might change together is what you have to do in RxJS to
|
||||
* avoid reading inconsistent state or observing too many changes.)
|
||||
*/
|
||||
private mapMembershipsToMembershipWithTransport$(): Behavior<
|
||||
{ membership: CallMembership; transport?: LivekitTransport }[]
|
||||
> {
|
||||
return this.scope.behavior(
|
||||
this.memberships$.pipe(
|
||||
map((memberships) => {
|
||||
return memberships.map((membership) => {
|
||||
const oldestMembership = memberships[0] ?? membership;
|
||||
const transport = membership.getTransport(oldestMembership);
|
||||
return {
|
||||
membership,
|
||||
transport: isLivekitTransport(transport) ? transport : undefined,
|
||||
};
|
||||
});
|
||||
}),
|
||||
),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add back in the callviewmodel pauseWhen(this.pretendToBeDisconnected$)
|
||||
|
||||
Reference in New Issue
Block a user