1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-25 05:23:13 +03:00

MatrixRTC: MembershipManager test cases and deprecation of MatrixRTCSession.room (#4713)

* WIP doodles on MembershipManager test cases

* .

* initial membership manager test setup.

* Updates from discussion

* revert renaming comments

* remove unused import

* fix leave delayed event resend test.
It was missing a flush.

* comment out and remove unused variables

* es lint

* use jsdom instead of node test environment

* remove unused variables

* remove unused export

* temp

* review

* fixup tests

* more review

* remove wait for expect dependency

* flatten tests and add comments

* add more leave test cases

* use defer

* remove @jest/environment dependency

* Cleanup awaits and Make mock types more correct.
Make every mock return a Promise if the real implementation does return a pormise.

* remove flush promise dependency

* add linting to matrixrtc tests

* Add fix async lints and use matrix rtc logger for test environment.

* prettier

* change to MatrixRTCSession logger

* make accessing the full room deprecated

* remove deprecated usage of full room

* Clean up the deprecation

---------

Co-authored-by: Hugh Nimmo-Smith <hughns@matrix.org>
This commit is contained in:
Timo
2025-02-27 10:55:09 +01:00
committed by GitHub
parent d81929de4c
commit a3bbc49e02
9 changed files with 758 additions and 151 deletions

View File

@@ -58,6 +58,8 @@ export interface MembershipConfig {
/**
* The timeout (in milliseconds) after we joined the call, that our membership should expire
* unless we have explicitly updated it.
*
* This is what goes into the m.rtc.member event expiry field and is typically set to a number of hours.
*/
membershipExpiryTimeout?: number;
@@ -89,6 +91,7 @@ export interface MembershipConfig {
*/
callMemberEventRetryJitter?: number;
}
export interface EncryptionConfig {
/**
* If true, generate and share a media key for this participant,
@@ -153,7 +156,9 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
/**
* Returns all the call memberships for a room, oldest first
*/
public static callMembershipsForRoom(room: Room): CallMembership[] {
public static callMembershipsForRoom(
room: Pick<Room, "getLiveTimeline" | "roomId" | "hasMembershipState">,
): CallMembership[] {
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
if (!roomState) {
logger.warn("Couldn't get state for room " + room.roomId);
@@ -225,20 +230,51 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
return new MatrixRTCSession(client, room, callMemberships);
}
private constructor(
private readonly client: MatrixClient,
public readonly room: Room,
/**
* WARN: this can in theory only be a subset of the room with the properties required by
* this class.
* Outside of tests this most likely will be a full room, however.
* @deprecated Relying on a full Room object being available here is an anti-pattern. You should be tracking
* the room object in your own code and passing it in when needed.
*/
public get room(): Room {
return this.roomSubset as Room;
}
/**
* This constructs a room session. When using MatrixRTC inside the js-sdk this is expected
* to be used with the MatrixRTCSessionManager exclusively.
*
* In cases where you don't use the js-sdk but build on top of another Matrix stack this class can be used standalone
* to manage a joined MatrixRTC session.
*
* @param client A subset of the {@link MatrixClient} that lets the session interact with the Matrix room.
* @param roomSubset The room this session is attached to. A subset of a js-sdk Room that the session needs.
* @param memberships The list of memberships this session currently has.
*/
public constructor(
private readonly client: Pick<
MatrixClient,
| "getUserId"
| "getDeviceId"
| "sendStateEvent"
| "_unstable_sendDelayedStateEvent"
| "_unstable_updateDelayedEvent"
| "sendEvent"
| "cancelPendingEvent"
>,
private roomSubset: Pick<Room, "getLiveTimeline" | "roomId" | "getVersion" | "hasMembershipState">,
public memberships: CallMembership[],
) {
super();
this._callId = memberships[0]?.callId;
const roomState = this.room.getLiveTimeline().getState(EventTimeline.FORWARDS);
const roomState = this.roomSubset.getLiveTimeline().getState(EventTimeline.FORWARDS);
// TODO: double check if this is actually needed. Should be covered by refreshRoom in MatrixRTCSessionManager
roomState?.on(RoomStateEvent.Members, this.onRoomMemberUpdate);
this.setExpiryTimer();
this.encryptionManager = new EncryptionManager(
this.client,
this.room,
this.roomSubset,
() => this.memberships,
(keyBin: Uint8Array<ArrayBufferLike>, encryptionKeyIndex: number, participantId: string) => {
this.emit(MatrixRTCSessionEvent.EncryptionKeyChanged, keyBin, encryptionKeyIndex, participantId);
@@ -263,7 +299,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
clearTimeout(this.expiryTimeout);
this.expiryTimeout = undefined;
}
const roomState = this.room.getLiveTimeline().getState(EventTimeline.FORWARDS);
const roomState = this.roomSubset.getLiveTimeline().getState(EventTimeline.FORWARDS);
roomState?.off(RoomStateEvent.Members, this.onRoomMemberUpdate);
}
@@ -284,10 +320,10 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
public joinRoomSession(fociPreferred: Focus[], fociActive?: Focus, joinConfig?: JoinSessionConfig): void {
// create MembershipManager
if (this.isJoined()) {
logger.info(`Already joined to session in room ${this.room.roomId}: ignoring join call`);
logger.info(`Already joined to session in room ${this.roomSubset.roomId}: ignoring join call`);
return;
} else {
this.membershipManager = new LegacyMembershipManager(joinConfig, this.room, this.client, () =>
this.membershipManager = new LegacyMembershipManager(joinConfig, this.roomSubset, this.client, () =>
this.getOldestMembership(),
);
}
@@ -311,11 +347,11 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
*/
public async leaveRoomSession(timeout: number | undefined = undefined): Promise<boolean> {
if (!this.isJoined()) {
logger.info(`Not joined to session in room ${this.room.roomId}: ignoring leave call`);
logger.info(`Not joined to session in room ${this.roomSubset.roomId}: ignoring leave call`);
return false;
}
logger.info(`Leaving call session in room ${this.room.roomId}`);
logger.info(`Leaving call session in room ${this.roomSubset.roomId}`);
this.encryptionManager.leave();
@@ -455,7 +491,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
oldMemberships.some((m, i) => !CallMembership.equal(m, this.memberships[i]));
if (changed) {
logger.info(`Memberships for call in room ${this.room.roomId} have changed: emitting`);
logger.info(`Memberships for call in room ${this.roomSubset.roomId} have changed: emitting`);
this.emit(MatrixRTCSessionEvent.MembershipsChanged, oldMemberships, this.memberships);
void this.membershipManager?.onRTCSessionMemberUpdate(this.memberships);