@@ -50,6 +50,7 @@ import {
5050} from "./RoomAndToDeviceKeyTransport.ts" ;
5151import { TypedReEmitter } from "../ReEmitter.ts" ;
5252import { ToDeviceKeyTransport } from "./ToDeviceKeyTransport.ts" ;
53+ import { MatrixEvent } from "src/matrix.ts" ;
5354
5455/**
5556 * Events emitted by MatrixRTCSession
@@ -308,10 +309,10 @@ export class MatrixRTCSession extends TypedEventEmitter<
308309 *
309310 * @deprecated Use `MatrixRTCSession.sessionMembershipsForSlot` instead.
310311 */
311- public static callMembershipsForRoom (
312- room : Pick < Room , "getLiveTimeline" | "roomId" | "hasMembershipState" > ,
313- ) : CallMembership [ ] {
314- return MatrixRTCSession . sessionMembershipsForSlot ( room , {
312+ public static async callMembershipsForRoom (
313+ room : Pick < Room , "getLiveTimeline" | "roomId" | "hasMembershipState" | "findEventById" | "client" > ,
314+ ) : Promise < CallMembership [ ] > {
315+ return await MatrixRTCSession . sessionMembershipsForSlot ( room , {
315316 id : "" ,
316317 application : "m.call" ,
317318 } ) ;
@@ -320,72 +321,67 @@ export class MatrixRTCSession extends TypedEventEmitter<
320321 /**
321322 * @deprecated use `MatrixRTCSession.slotMembershipsForRoom` instead.
322323 */
323- public static sessionMembershipsForRoom (
324- room : Pick < Room , "getLiveTimeline" | "roomId" | "hasMembershipState" > ,
324+ public static async sessionMembershipsForRoom (
325+ room : Pick < Room , "getLiveTimeline" | "roomId" | "hasMembershipState" | "findEventById" | "client" > ,
325326 sessionDescription : SlotDescription ,
326- ) : CallMembership [ ] {
327- return this . sessionMembershipsForSlot ( room , sessionDescription ) ;
327+ ) : Promise < CallMembership [ ] > {
328+ return await this . sessionMembershipsForSlot ( room , sessionDescription ) ;
328329 }
329330
330331 /**
331332 * Returns all the call memberships for a room that match the provided `sessionDescription`,
332333 * oldest first.
333334 */
334- public static sessionMembershipsForSlot (
335- room : Pick < Room , "getLiveTimeline" | "roomId" | "hasMembershipState" > ,
335+ public static async sessionMembershipsForSlot (
336+ room : Pick < Room , "getLiveTimeline" | "roomId" | "hasMembershipState" | "findEventById" | "client" > ,
336337 slotDescription : SlotDescription ,
337- ) : CallMembership [ ] {
338+ existingMemberships ?: CallMembership [ ] ,
339+ ) : Promise < CallMembership [ ] > {
338340 const logger = rootLogger . getChild ( `[MatrixRTCSession ${ room . roomId } ]` ) ;
339341 const roomState = room . getLiveTimeline ( ) . getState ( EventTimeline . FORWARDS ) ;
340342 if ( ! roomState ) {
341343 logger . warn ( "Couldn't get state for room " + room . roomId ) ;
342344 throw new Error ( "Could't get state for room " + room . roomId ) ;
343345 }
344346 const callMemberEvents = roomState . getStateEvents ( EventType . GroupCallMemberPrefix ) ;
345-
347+ // TODO optimise this to reuse existing memberships instead of always creating new ones.
346348 const callMemberships : CallMembership [ ] = [ ] ;
349+
347350 for ( const memberEvent of callMemberEvents ) {
348- const content = memberEvent . getContent ( ) ;
349- const eventKeysCount = Object . keys ( content ) . length ;
350- // Dont even bother about empty events (saves us from costly type/"key in" checks in bigger rooms)
351- if ( eventKeysCount === 0 ) continue ;
352-
353- const membershipContents : any [ ] = [ ] ;
354-
355- // We first decide if its a MSC4143 event (per device state key)
356- if ( eventKeysCount > 1 && "focus_active" in content ) {
357- // We have a MSC4143 event membership event
358- membershipContents . push ( content ) ;
359- } else if ( eventKeysCount === 1 && "memberships" in content ) {
360- logger . warn ( `Legacy event found. Those are ignored, they do not contribute to the MatrixRTC session` ) ;
361- }
351+ let membership = existingMemberships ?. find ( ( m ) => m . eventId === memberEvent . getId ( ) ) ;
352+ if ( ! membership ) {
353+ const content = memberEvent . getContent ( ) ;
362354
363- if ( membershipContents . length === 0 ) continue ;
355+ const relatedEventId = memberEvent . relationEventId ;
356+ const relatedEvent = relatedEventId
357+ ? room . findEventById ( relatedEventId )
358+ : new MatrixEvent ( await room . client . fetchRoomEvent ( room . roomId , relatedEventId ! ) ) ;
364359
365- for ( const membershipData of membershipContents ) {
366360 try {
367- const membership = new CallMembership ( memberEvent , membershipData ) ;
368-
369- if ( ! deepCompare ( membership . slotDescription , slotDescription ) ) {
370- logger . info (
371- `Ignoring membership of user ${ membership . sender } for a different session: ${ JSON . stringify ( membership . slotDescription ) } ` ,
372- ) ;
373- continue ;
374- }
375-
376- if ( membership . isExpired ( ) ) {
377- logger . info ( `Ignoring expired device membership ${ membership . sender } /${ membership . deviceId } ` ) ;
378- continue ;
379- }
380- if ( ! room . hasMembershipState ( membership . sender ?? "" , KnownMembership . Join ) ) {
381- logger . info ( `Ignoring membership of user ${ membership . sender } who is not in the room.` ) ;
382- continue ;
383- }
384- callMemberships . push ( membership ) ;
361+ membership = new CallMembership ( memberEvent , content , relatedEvent ) ;
385362 } catch ( e ) {
386363 logger . warn ( "Couldn't construct call membership: " , e ) ;
364+ continue ;
365+ }
366+ // static check for newly created memberships
367+ if ( ! deepCompare ( membership . slotDescription , slotDescription ) ) {
368+ logger . info (
369+ `Ignoring membership of user ${ membership . sender } for a different session: ${ JSON . stringify ( membership . slotDescription ) } ` ,
370+ ) ;
371+ continue ;
387372 }
388373 }
374+
375+ // Dynamic checks for all (including existing) memberships
376+ if ( membership . isExpired ( ) ) {
377+ logger . info ( `Ignoring expired device membership ${ membership . sender } /${ membership . deviceId } ` ) ;
378+ continue ;
379+ }
380+ if ( ! room . hasMembershipState ( membership . sender ?? "" , KnownMembership . Join ) ) {
381+ logger . info ( `Ignoring membership of user ${ membership . sender } who is not in the room.` ) ;
382+ continue ;
383+ }
384+ callMemberships . push ( membership ) ;
389385 }
390386
391387 callMemberships . sort ( ( a , b ) => a . createdTs ( ) - b . createdTs ( ) ) ;
@@ -409,15 +405,22 @@ export class MatrixRTCSession extends TypedEventEmitter<
409405 *
410406 * @deprecated Use `MatrixRTCSession.sessionForSlot` with sessionDescription `{ id: "", application: "m.call" }` instead.
411407 */
412- public static roomSessionForRoom ( client : MatrixClient , room : Room ) : MatrixRTCSession {
413- const callMemberships = MatrixRTCSession . sessionMembershipsForSlot ( room , { id : "" , application : "m.call" } ) ;
408+ public static async roomSessionForRoom ( client : MatrixClient , room : Room ) : Promise < MatrixRTCSession > {
409+ const callMemberships = await MatrixRTCSession . sessionMembershipsForSlot ( room , {
410+ id : "" ,
411+ application : "m.call" ,
412+ } ) ;
414413 return new MatrixRTCSession ( client , room , callMemberships , { id : "" , application : "m.call" } ) ;
415414 }
416415
417416 /**
418417 * @deprecated Use `MatrixRTCSession.sessionForSlot` instead.
419418 */
420- public static sessionForRoom ( client : MatrixClient , room : Room , slotDescription : SlotDescription ) : MatrixRTCSession {
419+ public static async sessionForRoom (
420+ client : MatrixClient ,
421+ room : Room ,
422+ slotDescription : SlotDescription ,
423+ ) : Promise < MatrixRTCSession > {
421424 return this . sessionForSlot ( client , room , slotDescription ) ;
422425 }
423426
@@ -426,8 +429,12 @@ export class MatrixRTCSession extends TypedEventEmitter<
426429 * This returned session can be used to find out if there are active sessions
427430 * for the requested room and `slotDescription`.
428431 */
429- public static sessionForSlot ( client : MatrixClient , room : Room , slotDescription : SlotDescription ) : MatrixRTCSession {
430- const callMemberships = MatrixRTCSession . sessionMembershipsForSlot ( room , slotDescription ) ;
432+ public static async sessionForSlot (
433+ client : MatrixClient ,
434+ room : Room ,
435+ slotDescription : SlotDescription ,
436+ ) : Promise < MatrixRTCSession > {
437+ const callMemberships = await MatrixRTCSession . sessionMembershipsForSlot ( room , slotDescription ) ;
431438
432439 return new MatrixRTCSession ( client , room , callMemberships , slotDescription ) ;
433440 }
@@ -799,46 +806,50 @@ export class MatrixRTCSession extends TypedEventEmitter<
799806 */
800807 private recalculateSessionMembers = ( ) : void => {
801808 const oldMemberships = this . memberships ;
802- this . memberships = MatrixRTCSession . sessionMembershipsForSlot ( this . room , this . slotDescription ) ;
803-
804- this . _slotId = this . _slotId ?? this . memberships [ 0 ] ?. slotId ;
805-
806- const changed =
807- oldMemberships . length != this . memberships . length ||
808- oldMemberships . some ( ( m , i ) => ! CallMembership . equal ( m , this . memberships [ i ] ) ) ;
809-
810- if ( changed ) {
811- this . logger . info (
812- `Memberships for call in room ${ this . roomSubset . roomId } have changed: emitting (${ this . memberships . length } members)` ,
813- ) ;
814- logDurationSync ( this . logger , "emit MatrixRTCSessionEvent.MembershipsChanged" , ( ) => {
815- this . emit ( MatrixRTCSessionEvent . MembershipsChanged , oldMemberships , this . memberships ) ;
816- } ) ;
817-
818- void this . membershipManager ?. onRTCSessionMemberUpdate ( this . memberships ) ;
819- // The `ownMembership` will be set when calling `onRTCSessionMemberUpdate`.
820- const ownMembership = this . membershipManager ?. ownMembership ;
821- if ( this . pendingNotificationToSend && ownMembership && oldMemberships . length === 0 ) {
822- // If we're the first member in the call, we're responsible for
823- // sending the notification event
824- if ( ownMembership . eventId && this . joinConfig ?. notificationType ) {
825- this . sendCallNotify (
826- ownMembership . eventId ,
827- this . joinConfig . notificationType ,
828- ownMembership . callIntent ,
809+ void MatrixRTCSession . sessionMembershipsForSlot ( this . room , this . slotDescription , oldMemberships ) . then (
810+ ( newMemberships ) => {
811+ this . memberships = newMemberships ;
812+ this . _slotId = this . _slotId ?? this . memberships [ 0 ] ?. slotId ;
813+
814+ const changed =
815+ oldMemberships . length != this . memberships . length ||
816+ // If they have the same length, this is enough to check "changed"
817+ oldMemberships . some ( ( m , i ) => ! CallMembership . equal ( m , this . memberships [ i ] ) ) ;
818+
819+ if ( changed ) {
820+ this . logger . info (
821+ `Memberships for call in room ${ this . roomSubset . roomId } have changed: emitting (${ this . memberships . length } members)` ,
829822 ) ;
830- } else {
831- this . logger . warn ( "Own membership eventId is undefined, cannot send call notification" ) ;
823+ logDurationSync ( this . logger , "emit MatrixRTCSessionEvent.MembershipsChanged" , ( ) => {
824+ this . emit ( MatrixRTCSessionEvent . MembershipsChanged , oldMemberships , this . memberships ) ;
825+ } ) ;
826+
827+ void this . membershipManager ?. onRTCSessionMemberUpdate ( this . memberships ) ;
828+ // The `ownMembership` will be set when calling `onRTCSessionMemberUpdate`.
829+ const ownMembership = this . membershipManager ?. ownMembership ;
830+ if ( this . pendingNotificationToSend && ownMembership && oldMemberships . length === 0 ) {
831+ // If we're the first member in the call, we're responsible for
832+ // sending the notification event
833+ if ( ownMembership . eventId && this . joinConfig ?. notificationType ) {
834+ this . sendCallNotify (
835+ ownMembership . eventId ,
836+ this . joinConfig . notificationType ,
837+ ownMembership . callIntent ,
838+ ) ;
839+ } else {
840+ this . logger . warn ( "Own membership eventId is undefined, cannot send call notification" ) ;
841+ }
842+ }
843+ // If anyone else joins the session it is no longer our responsibility to send the notification.
844+ // (If we were the joiner we already did sent the notification in the block above.)
845+ if ( this . memberships . length > 0 ) this . pendingNotificationToSend = undefined ;
832846 }
833- }
834- // If anyone else joins the session it is no longer our responsibility to send the notification.
835- // (If we were the joiner we already did sent the notification in the block above.)
836- if ( this . memberships . length > 0 ) this . pendingNotificationToSend = undefined ;
837- }
838- // This also needs to be done if `changed` = false
839- // A member might have updated their fingerprint (created_ts)
840- void this . encryptionManager ?. onMembershipsUpdate ( oldMemberships ) ;
847+ // This also needs to be done if `changed` = false
848+ // A member might have updated their fingerprint (created_ts)
849+ void this . encryptionManager ?. onMembershipsUpdate ( oldMemberships ) ;
841850
842- this . setExpiryTimer ( ) ;
851+ this . setExpiryTimer ( ) ;
852+ } ,
853+ ) ;
843854 } ;
844855}
0 commit comments