Skip to content

Commit c4c7f94

Browse files
authored
Make a MatrixRTCSession emit once the RTCNotification is sent (#4976)
* MatrixRTCSession emits once the rtc notification is sent. Signed-off-by: Timo K <[email protected]> * update correct type description Signed-off-by: Timo K <[email protected]> * Add test Signed-off-by: Timo K <[email protected]> * fix imports Signed-off-by: Timo K <[email protected]> --------- Signed-off-by: Timo K <[email protected]>
1 parent 1fac06e commit c4c7f94

File tree

2 files changed

+80
-13
lines changed

2 files changed

+80
-13
lines changed

spec/unit/matrixrtc/MatrixRTCSession.spec.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,19 @@ describe("MatrixRTCSession", () => {
309309
expect(sess!.isJoined()).toEqual(true);
310310
});
311311

312-
it("sends a notification when starting a call", async () => {
312+
it("sends a notification when starting a call and emit DidSendCallNotification", async () => {
313313
// Simulate a join, including the update to the room state
314+
// Ensure sendEvent returns event IDs so the DidSendCallNotification payload includes them
315+
sendEventMock
316+
.mockResolvedValueOnce({ event_id: "legacy-evt" })
317+
.mockResolvedValueOnce({ event_id: "new-evt" });
318+
const didSendEventFn = jest.fn();
319+
sess!.once(MatrixRTCSessionEvent.DidSendCallNotification, didSendEventFn);
320+
// Create an additional listener to create a promise that resolves after the emission.
321+
const didSendNotification = new Promise((resolve) => {
322+
sess!.once(MatrixRTCSessionEvent.DidSendCallNotification, resolve);
323+
});
324+
314325
sess!.joinRoomSession([mockFocus], mockFocus, { notificationType: "ring" });
315326
await Promise.race([sentStateEvent, new Promise((resolve) => setTimeout(resolve, 5000))]);
316327
mockRoomState(mockRoom, [{ ...membershipTemplate, user_id: client.getUserId()! }]);
@@ -335,6 +346,28 @@ describe("MatrixRTCSession", () => {
335346
"notify_type": "ring",
336347
"call_id": "",
337348
});
349+
await didSendNotification;
350+
// And ensure we emitted the DidSendCallNotification event with both payloads
351+
expect(didSendEventFn).toHaveBeenCalledWith(
352+
{
353+
"event_id": "new-evt",
354+
"lifetime": 30000,
355+
"m.mentions": { room: true, user_ids: [] },
356+
"m.relates_to": {
357+
event_id: expect.any(String),
358+
rel_type: "org.matrix.msc4075.rtc.notification.parent",
359+
},
360+
"notification_type": "ring",
361+
"sender_ts": expect.any(Number),
362+
},
363+
{
364+
"application": "m.call",
365+
"call_id": "",
366+
"event_id": "legacy-evt",
367+
"m.mentions": { room: true, user_ids: [] },
368+
"notify_type": "ring",
369+
},
370+
);
338371
});
339372

340373
it("doesn't send a notification when joining an existing call", async () => {

src/matrixrtc/MatrixRTCSession.ts

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,21 @@ import { EventTimeline } from "../models/event-timeline.ts";
2020
import { type Room } from "../models/room.ts";
2121
import { type MatrixClient } from "../client.ts";
2222
import { EventType, RelationType } from "../@types/event.ts";
23+
import { KnownMembership } from "../@types/membership.ts";
24+
import { type ISendEventResponse } from "../@types/requests.ts";
2325
import { CallMembership } from "./CallMembership.ts";
2426
import { RoomStateEvent } from "../models/room-state.ts";
2527
import { type Focus } from "./focus.ts";
26-
import { KnownMembership } from "../@types/membership.ts";
2728
import { MembershipManager } from "./MembershipManager.ts";
2829
import { EncryptionManager, type IEncryptionManager } from "./EncryptionManager.ts";
2930
import { deepCompare, logDurationSync } from "../utils.ts";
30-
import { type Statistics, type RTCNotificationType, type Status } from "./types.ts";
31+
import {
32+
type Statistics,
33+
type RTCNotificationType,
34+
type Status,
35+
type IRTCNotificationContent,
36+
type ICallNotifyContent,
37+
} from "./types.ts";
3138
import { RoomKeyTransport } from "./RoomKeyTransport.ts";
3239
import {
3340
MembershipManagerEvent,
@@ -43,6 +50,9 @@ import {
4350
import { TypedReEmitter } from "../ReEmitter.ts";
4451
import { ToDeviceKeyTransport } from "./ToDeviceKeyTransport.ts";
4552

53+
/**
54+
* Events emitted by MatrixRTCSession
55+
*/
4656
export enum MatrixRTCSessionEvent {
4757
// A member joined, left, or updated a property of their membership.
4858
MembershipsChanged = "memberships_changed",
@@ -54,6 +64,8 @@ export enum MatrixRTCSessionEvent {
5464
EncryptionKeyChanged = "encryption_key_changed",
5565
/** The membership manager had to shut down caused by an unrecoverable error */
5666
MembershipManagerError = "membership_manager_error",
67+
/** The RTCSession did send a call notification caused by joining the call as the first member */
68+
DidSendCallNotification = "did_send_call_notification",
5769
}
5870

5971
export type MatrixRTCSessionEventHandlerMap = {
@@ -68,6 +80,10 @@ export type MatrixRTCSessionEventHandlerMap = {
6880
participantId: string,
6981
) => void;
7082
[MatrixRTCSessionEvent.MembershipManagerError]: (error: unknown) => void;
83+
[MatrixRTCSessionEvent.DidSendCallNotification]: (
84+
notificationContentNew: { event_id: string } & IRTCNotificationContent,
85+
notificationContentLegacy: { event_id: string } & ICallNotifyContent,
86+
) => void;
7187
};
7288

7389
export interface SessionConfig {
@@ -652,19 +668,24 @@ export class MatrixRTCSession extends TypedEventEmitter<
652668
* Sends a notification corresponding to the configured notify type.
653669
*/
654670
private sendCallNotify(parentEventId: string, notificationType: RTCNotificationType): void {
655-
// Send legacy event:
656-
this.client
657-
.sendEvent(this.roomSubset.roomId, EventType.CallNotify, {
671+
const sendLegacyNotificationEvent = async (): Promise<{
672+
response: ISendEventResponse;
673+
content: ICallNotifyContent;
674+
}> => {
675+
const content: ICallNotifyContent = {
658676
"application": "m.call",
659677
"m.mentions": { user_ids: [], room: true },
660678
"notify_type": notificationType === "notification" ? "notify" : notificationType,
661679
"call_id": this.callId!,
662-
})
663-
.catch((e) => this.logger.error("Failed to send call notification", e));
664-
665-
// Send new event:
666-
this.client
667-
.sendEvent(this.roomSubset.roomId, EventType.RTCNotification, {
680+
};
681+
const response = await this.client.sendEvent(this.roomSubset.roomId, EventType.CallNotify, content);
682+
return { response, content };
683+
};
684+
const sendNewNotificationEvent = async (): Promise<{
685+
response: ISendEventResponse;
686+
content: IRTCNotificationContent;
687+
}> => {
688+
const content: IRTCNotificationContent = {
668689
"m.mentions": { user_ids: [], room: true },
669690
"notification_type": notificationType,
670691
"m.relates_to": {
@@ -673,8 +694,21 @@ export class MatrixRTCSession extends TypedEventEmitter<
673694
},
674695
"sender_ts": Date.now(),
675696
"lifetime": 30_000, // 30 seconds
697+
};
698+
const response = await this.client.sendEvent(this.roomSubset.roomId, EventType.RTCNotification, content);
699+
return { response, content };
700+
};
701+
702+
void Promise.all([sendLegacyNotificationEvent(), sendNewNotificationEvent()])
703+
.then(([legacy, newNotification]) => {
704+
// Join event_id and origin event content
705+
const legacyResult = { ...legacy.response, ...legacy.content };
706+
const newResult = { ...newNotification.response, ...newNotification.content };
707+
this.emit(MatrixRTCSessionEvent.DidSendCallNotification, newResult, legacyResult);
676708
})
677-
.catch((e) => this.logger.error("Failed to send call notification", e));
709+
.catch(([errorLegacy, errorNew]) =>
710+
this.logger.error("Failed to send call notification", errorLegacy, errorNew),
711+
);
678712
}
679713

680714
/**

0 commit comments

Comments
 (0)