@@ -480,72 +480,93 @@ export class StopGapWidget extends EventEmitter {
480480 await this . messaging ?. feedToDevice ( ev . getEffectiveEvent ( ) as IRoomEvent , ev . isEncrypted ( ) ) ;
481481 } ;
482482
483- private feedEvent ( ev : MatrixEvent ) : void {
484- if ( ! this . messaging ) return ;
485-
486- // Check to see if this event would be before or after our "read up to" marker. If it's
487- // before, or we can't decide, then we assume the widget will have already seen the event.
488- // If the event is after, or we don't have a marker for the room, then we'll send it through.
489- //
490- // This approach of "read up to" prevents widgets receiving decryption spam from startup or
491- // receiving out-of-order events from backfill and such.
492- //
493- // Skip marker timeline check for events with relations to unknown parent because these
494- // events are not added to the timeline here and will be ignored otherwise:
495- // https://github.com/matrix-org/matrix-js-sdk/blob/d3dfcd924201d71b434af3d77343b5229b6ed75e/src/models/room.ts#L2207-L2213
496- let isRelationToUnknown : boolean | undefined = undefined ;
497- const upToEventId = this . readUpToMap [ ev . getRoomId ( ) ! ] ;
498- if ( upToEventId ) {
499- // Small optimization for exact match (prevent search)
500- if ( upToEventId === ev . getId ( ) ) {
501- return ;
502- }
483+ /**
484+ * Determines whether the event has a relation to an unknown parent.
485+ */
486+ private relatesToUnknown ( ev : MatrixEvent ) : boolean {
487+ // Replies to unknown events don't count
488+ if ( ! ev . relationEventId || ev . replyEventId ) return false ;
489+ const room = this . client . getRoom ( ev . getRoomId ( ) ) ;
490+ return room === null || ! room . findEventById ( ev . relationEventId ) ;
491+ }
503492
504- // should be true to forward the event to the widget
505- let shouldForward = false ;
506-
507- const room = this . client . getRoom ( ev . getRoomId ( ) ! ) ;
508- if ( ! room ) return ;
509- // Timelines are most recent last, so reverse the order and limit ourselves to 100 events
510- // to avoid overusing the CPU.
511- const timeline = room . getLiveTimeline ( ) ;
512- const events = arrayFastClone ( timeline . getEvents ( ) ) . reverse ( ) . slice ( 0 , 100 ) ;
513-
514- for ( const timelineEvent of events ) {
515- if ( timelineEvent . getId ( ) === upToEventId ) {
516- break ;
517- } else if ( timelineEvent . getId ( ) === ev . getId ( ) ) {
518- shouldForward = true ;
519- break ;
520- }
521- }
493+ /**
494+ * Determines whether the event comes from a room that we've been invited to
495+ * (in which case we likely don't have the full timeline).
496+ */
497+ private isFromInvite ( ev : MatrixEvent ) : boolean {
498+ const room = this . client . getRoom ( ev . getRoomId ( ) ) ;
499+ return room ?. getMyMembership ( ) === KnownMembership . Invite ;
500+ }
522501
523- if ( ! shouldForward ) {
524- // checks that the event has a relation to unknown event
525- isRelationToUnknown =
526- ! ev . replyEventId && ! ! ev . relationEventId && ! room . findEventById ( ev . relationEventId ) ;
527- if ( ! isRelationToUnknown ) {
528- // Ignore the event: it is before our interest.
529- return ;
530- }
531- }
502+ /**
503+ * Advances the "read up to" marker for a room to a certain event. No-ops if
504+ * the event is before the marker.
505+ * @returns Whether the "read up to" marker was advanced.
506+ */
507+ private advanceReadUpToMarker ( ev : MatrixEvent ) : boolean {
508+ const evId = ev . getId ( ) ;
509+ if ( evId === undefined ) return false ;
510+ const roomId = ev . getRoomId ( ) ;
511+ if ( roomId === undefined ) return false ;
512+ const room = this . client . getRoom ( roomId ) ;
513+ if ( room === null ) return false ;
514+
515+ const upToEventId = this . readUpToMap [ ev . getRoomId ( ) ! ] ;
516+ if ( ! upToEventId ) {
517+ // There's no marker yet; start it at this event
518+ this . readUpToMap [ roomId ] = evId ;
519+ return true ;
532520 }
533521
534- // Skip marker assignment if membership is 'invite', otherwise 'm.room.member' from
535- // invitation room will assign it and new state events will be not forwarded to the widget
536- // because of empty timeline for invitation room and assigned marker.
537- const evRoomId = ev . getRoomId ( ) ;
538- const evId = ev . getId ( ) ;
539- if ( evRoomId && evId ) {
540- const room = this . client . getRoom ( evRoomId ) ;
541- if ( room && room . getMyMembership ( ) === KnownMembership . Join && ! isRelationToUnknown ) {
542- this . readUpToMap [ evRoomId ] = evId ;
522+ // Small optimization for exact match (skip the search)
523+ if ( upToEventId === evId ) return false ;
524+
525+ // Timelines are most recent last, so reverse the order and limit ourselves to 100 events
526+ // to avoid overusing the CPU.
527+ const timeline = room . getLiveTimeline ( ) ;
528+ const events = arrayFastClone ( timeline . getEvents ( ) ) . reverse ( ) . slice ( 0 , 100 ) ;
529+
530+ for ( const timelineEvent of events ) {
531+ if ( timelineEvent . getId ( ) === upToEventId ) {
532+ // The event must be somewhere before the "read up to" marker
533+ return false ;
534+ } else if ( timelineEvent . getId ( ) === ev . getId ( ) ) {
535+ // The event is after the marker; advance it
536+ this . readUpToMap [ roomId ] = evId ;
537+ return true ;
543538 }
544539 }
545540
546- const raw = ev . getEffectiveEvent ( ) ;
547- this . messaging . feedEvent ( raw as IRoomEvent , this . eventListenerRoomId ! ) . catch ( ( e ) => {
548- logger . error ( "Error sending event to widget: " , e ) ;
549- } ) ;
541+ // We can't say for sure whether the widget has seen the event; let's
542+ // just assume that it has
543+ return false ;
544+ }
545+
546+ private feedEvent ( ev : MatrixEvent ) : void {
547+ if ( this . messaging === null ) return ;
548+ if (
549+ // Skip marker timeline check for events with relations to unknown parent because these
550+ // events are not added to the timeline here and will be ignored otherwise:
551+ // https://github.com/matrix-org/matrix-js-sdk/blob/d3dfcd924201d71b434af3d77343b5229b6ed75e/src/models/room.ts#L2207-L2213
552+ this . relatesToUnknown ( ev ) ||
553+ // Skip marker timeline check for rooms where membership is
554+ // 'invite', otherwise the membership event from the invitation room
555+ // will advance the marker and new state events will not be
556+ // forwarded to the widget.
557+ this . isFromInvite ( ev ) ||
558+ // Check whether this event would be before or after our "read up to" marker. If it's
559+ // before, or we can't decide, then we assume the widget will have already seen the event.
560+ // If the event is after, or we don't have a marker for the room, then the marker will advance and we'll
561+ // send it through.
562+ // This approach of "read up to" prevents widgets receiving decryption spam from startup or
563+ // receiving ancient events from backfill and such.
564+ this . advanceReadUpToMarker ( ev )
565+ ) {
566+ const raw = ev . getEffectiveEvent ( ) ;
567+ this . messaging . feedEvent ( raw as IRoomEvent , this . eventListenerRoomId ! ) . catch ( ( e ) => {
568+ logger . error ( "Error sending event to widget: " , e ) ;
569+ } ) ;
570+ }
550571 }
551572}
0 commit comments