From 3a33c658bbcb8ce8791ec066db899f2571f5c52f Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Wed, 20 Aug 2025 18:48:56 +0200 Subject: [PATCH] Expose the StatusChanged event through the RTCSession (#4974) * Expose the StatusChanged event through the RTCSession Signed-off-by: Timo K * add membershipManagerStatus public get field Signed-off-by: Timo K * add probably left as a getter Signed-off-by: Timo K * add tests for coverage Signed-off-by: Timo K --------- Signed-off-by: Timo K --- spec/unit/matrixrtc/MatrixRTCSession.spec.ts | 28 +++++++++++++++++++- src/matrixrtc/IMembershipManager.ts | 6 +++++ src/matrixrtc/MatrixRTCSession.ts | 21 ++++++++++----- src/matrixrtc/MembershipManager.ts | 4 +++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts index e7e64c9cb..75f58221d 100644 --- a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts @@ -17,7 +17,7 @@ limitations under the License. import { encodeBase64, EventType, MatrixClient, type MatrixError, type MatrixEvent, type Room } from "../../../src"; import { KnownMembership } from "../../../src/@types/membership"; import { MatrixRTCSession, MatrixRTCSessionEvent } from "../../../src/matrixrtc/MatrixRTCSession"; -import { type EncryptionKeysEventContent } from "../../../src/matrixrtc/types"; +import { Status, type EncryptionKeysEventContent } from "../../../src/matrixrtc/types"; import { secureRandomString } from "../../../src/randomstring"; import { makeMockEvent, makeMockRoom, membershipTemplate, makeKey, type MembershipData, mockRoomState } from "./mocks"; import { RTCEncryptionManager } from "../../../src/matrixrtc/RTCEncryptionManager.ts"; @@ -1226,5 +1226,31 @@ describe("MatrixRTCSession", () => { } }); }); + describe("read status", () => { + it("returns the correct probablyLeft status", () => { + const mockRoom = makeMockRoom([membershipTemplate]); + sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + expect(sess!.probablyLeft).toBe(undefined); + + sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); + expect(sess!.probablyLeft).toBe(false); + + // Simulate the membership manager believing the user has left + const accessPrivateFieldsSession = sess as unknown as { + membershipManager: { state: { probablyLeft: boolean } }; + }; + accessPrivateFieldsSession.membershipManager.state.probablyLeft = true; + expect(sess!.probablyLeft).toBe(true); + }); + + it("returns membershipStatus once joinRoomSession got called", () => { + const mockRoom = makeMockRoom([membershipTemplate]); + sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession); + expect(sess!.membershipStatus).toBe(undefined); + + sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); + expect(sess!.membershipStatus).toBe(Status.Connecting); + }); + }); }); }); diff --git a/src/matrixrtc/IMembershipManager.ts b/src/matrixrtc/IMembershipManager.ts index 303bfe203..b1702ef8e 100644 --- a/src/matrixrtc/IMembershipManager.ts +++ b/src/matrixrtc/IMembershipManager.ts @@ -71,6 +71,12 @@ export interface IMembershipManager */ get ownMembership(): CallMembership | undefined; + /** + * If the membership manager has reason to believe that the hs sent a leave event + * and as a consequence the current user is perceived as left for other session participants. + */ + get probablyLeft(): boolean; + /** * Start sending all necessary events to make this user participate in the RTC session. * @param fociPreferred the list of preferred foci to use in the joined RTC membership event. diff --git a/src/matrixrtc/MatrixRTCSession.ts b/src/matrixrtc/MatrixRTCSession.ts index 252b03804..a3f658ad9 100644 --- a/src/matrixrtc/MatrixRTCSession.ts +++ b/src/matrixrtc/MatrixRTCSession.ts @@ -27,7 +27,7 @@ import { KnownMembership } from "../@types/membership.ts"; import { MembershipManager } from "./MembershipManager.ts"; import { EncryptionManager, type IEncryptionManager } from "./EncryptionManager.ts"; import { deepCompare, logDurationSync } from "../utils.ts"; -import { type Statistics, type RTCNotificationType } from "./types.ts"; +import { type Statistics, type RTCNotificationType, type Status } from "./types.ts"; import { RoomKeyTransport } from "./RoomKeyTransport.ts"; import { MembershipManagerEvent, @@ -224,10 +224,8 @@ export type JoinSessionConfig = SessionConfig & MembershipConfig & EncryptionCon * This class doesn't deal with media at all, just membership & properties of a session. */ export class MatrixRTCSession extends TypedEventEmitter< - MatrixRTCSessionEvent | RoomAndToDeviceEvents | MembershipManagerEvent.ProbablyLeft, - MatrixRTCSessionEventHandlerMap & - RoomAndToDeviceEventsHandlerMap & - Pick + MatrixRTCSessionEvent | RoomAndToDeviceEvents | MembershipManagerEvent, + MatrixRTCSessionEventHandlerMap & RoomAndToDeviceEventsHandlerMap & MembershipManagerEventHandlerMap > { private membershipManager?: IMembershipManager; private encryptionManager?: IEncryptionManager; @@ -257,6 +255,14 @@ export class MatrixRTCSession extends TypedEventEmitter< }, }; + public get membershipStatus(): Status | undefined { + return this.membershipManager?.status; + } + + public get probablyLeft(): boolean | undefined { + return this.membershipManager?.probablyLeft; + } + /** * The callId (sessionId) of the call. * @@ -496,7 +502,10 @@ export class MatrixRTCSession extends TypedEventEmitter< this.logger, ); - this.reEmitter.reEmit(this.membershipManager!, [MembershipManagerEvent.ProbablyLeft]); + this.reEmitter.reEmit(this.membershipManager!, [ + MembershipManagerEvent.ProbablyLeft, + MembershipManagerEvent.StatusChanged, + ]); // Create Encryption manager let transport; if (joinConfig?.useExperimentalToDeviceTransport) { diff --git a/src/matrixrtc/MembershipManager.ts b/src/matrixrtc/MembershipManager.ts index 251bf17b1..89b3a0937 100644 --- a/src/matrixrtc/MembershipManager.ts +++ b/src/matrixrtc/MembershipManager.ts @@ -984,6 +984,10 @@ export class MembershipManager this.logger.error("MembershipManager has an unknown state. Actions: ", actions); return Status.Unknown; } + + public get probablyLeft(): boolean { + return this.state.probablyLeft; + } } function createInsertActionUpdate(type: MembershipActionType, offset?: number): ActionUpdate {