Skip to content

Commit 7b998dc

Browse files
committed
Refactor feeding of events to widgets
This is a pure refactor with (hopefully) no behavior changes.
1 parent b604a6e commit 7b998dc

File tree

1 file changed

+81
-60
lines changed

1 file changed

+81
-60
lines changed

src/stores/widgets/StopGapWidget.ts

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)