diff --git a/src/matrixrtc/MatrixRTCSession.ts b/src/matrixrtc/MatrixRTCSession.ts index d20773d2d7..6ecd1a6b40 100644 --- a/src/matrixrtc/MatrixRTCSession.ts +++ b/src/matrixrtc/MatrixRTCSession.ts @@ -28,7 +28,7 @@ import { MembershipManager } from "./NewMembershipManager.ts"; import { EncryptionManager, type IEncryptionManager } from "./EncryptionManager.ts"; import { LegacyMembershipManager } from "./LegacyMembershipManager.ts"; import { logDurationSync } from "../utils.ts"; -import { type Statistics } from "./types.ts"; +import { type Statistics, type CallNotifyType, isMyMembership } from "./types.ts"; import { RoomKeyTransport } from "./RoomKeyTransport.ts"; import type { IMembershipManager } from "./IMembershipManager.ts"; import { @@ -66,6 +66,14 @@ export type MatrixRTCSessionEventHandlerMap = { [MatrixRTCSessionEvent.MembershipManagerError]: (error: unknown) => void; }; +export interface SessionConfig { + /** + * What kind of notification to send when starting the session. + * @default undefined (no notification) + */ + notifyType?: CallNotifyType; +} + export interface MembershipConfig { /** * Use the new Manager. @@ -162,7 +170,7 @@ export interface EncryptionConfig { */ useKeyDelay?: number; } -export type JoinSessionConfig = MembershipConfig & EncryptionConfig; +export type JoinSessionConfig = SessionConfig & MembershipConfig & EncryptionConfig; /** * A MatrixRTCSession manages the membership & properties of a MatrixRTC session. @@ -176,7 +184,15 @@ export class MatrixRTCSession extends TypedEventEmitter< private encryptionManager?: IEncryptionManager; // The session Id of the call, this is the call_id of the call Member event. private _callId: string | undefined; + private joinConfig?: SessionConfig; private logger: Logger; + + /** + * Whether we're trying to join the session but still waiting for room state + * to reflect our own membership. + */ + private joining = false; + /** * This timeout is responsible to track any expiration. We need to know when we have to start * to ignore other call members. There is no callback for this. This timeout will always be configured to @@ -424,6 +440,11 @@ export class MatrixRTCSession extends TypedEventEmitter< ); } + this.joinConfig = joinConfig; + const userId = this.client.getUserId()!; + const deviceId = this.client.getDeviceId()!; + this.joining = !this.memberships.some((m) => isMyMembership(m, userId, deviceId)); + // Join! this.membershipManager!.join(fociPreferred, fociActive, (e) => { this.logger.error("MembershipManager encountered an unrecoverable error: ", e); @@ -453,11 +474,11 @@ export class MatrixRTCSession extends TypedEventEmitter< this.logger.info(`Leaving call session in room ${this.roomSubset.roomId}`); + this.joining = false; this.encryptionManager!.leave(); - const leavePromise = this.membershipManager!.leave(timeout); - this.emit(MatrixRTCSessionEvent.JoinStateChanged, false); + this.emit(MatrixRTCSessionEvent.JoinStateChanged, false); return await leavePromise; } @@ -540,6 +561,22 @@ export class MatrixRTCSession extends TypedEventEmitter< } } + /** + * Sends a notification corresponding to the configured notify type. + */ + private sendCallNotify(): void { + if (this.joinConfig?.notifyType !== undefined) { + this.client + .sendEvent(this.roomSubset.roomId, EventType.CallNotify, { + "application": "m.call", + "m.mentions": { user_ids: [], room: true }, + "notify_type": this.joinConfig.notifyType, + "call_id": this.callId!, + }) + .catch((e) => this.logger.error("Failed to send call notification", e)); + } + } + /** * Call this when the Matrix room members have changed. */ @@ -578,6 +615,15 @@ export class MatrixRTCSession extends TypedEventEmitter< }); void this.membershipManager?.onRTCSessionMemberUpdate(this.memberships); + + const userId = this.client.getUserId()!; + const deviceId = this.client.getDeviceId()!; + if (this.joining && this.memberships.some((m) => isMyMembership(m, userId, deviceId))) { + this.joining = false; + // If we're the first member in the call, we're responsible for + // sending the notification event + if (oldMemberships.length === 0) this.sendCallNotify(); + } } // This also needs to be done if `changed` = false // A member might have updated their fingerprint (created_ts)