Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Don't send prehistorical events to widgets during decryption at startup #6695

Merged
merged 1 commit into from
Sep 1, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/stores/widgets/StopGapWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { ELEMENT_CLIENT_ID } from "../../identifiers";
import { getUserLanguage } from "../../languageHandler";
import { WidgetVariableCustomisations } from "../../customisations/WidgetVariables";
import { arrayFastClone } from "../../utils/arrays";

// TODO: Destroy all of this code

Expand Down Expand Up @@ -146,6 +147,7 @@ export class StopGapWidget extends EventEmitter {
private scalarToken: string;
private roomId?: string;
private kind: WidgetKind;
private readUpToMap: {[roomId: string]: string} = {}; // room ID to event ID

constructor(private appTileProps: IAppTileProps) {
super();
Expand Down Expand Up @@ -294,6 +296,14 @@ export class StopGapWidget extends EventEmitter {
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
});

// Populate the map of "read up to" events for this widget with the current event in every room.
// This is a bit inefficient, but should be okay. We do this for all rooms in case the widget
// requests timeline capabilities in other rooms down the road. It's just easier to manage here.
for (const room of MatrixClientPeg.get().getRooms()) {
// Timelines are most recent last
this.readUpToMap[room.roomId] = arrayFastClone(room.getLiveTimeline().getEvents()).reverse()[0].getId();
}

// Attach listeners for feeding events - the underlying widget classes handle permissions for us
MatrixClientPeg.get().on('event', this.onEvent);
MatrixClientPeg.get().on('Event.decrypted', this.onEventDecrypted);
Expand Down Expand Up @@ -421,6 +431,43 @@ export class StopGapWidget extends EventEmitter {
private feedEvent(ev: MatrixEvent) {
if (!this.messaging) return;

// Check to see if this event would be before or after our "read up to" marker. If it's
// before, or we can't decide, then we assume the widget will have already seen the event.
// If the event is after, or we don't have a marker for the room, then we'll send it through.
//
// This approach of "read up to" prevents widgets receiving decryption spam from startup or
// receiving out-of-order events from backfill and such.
const upToEventId = this.readUpToMap[ev.getRoomId()];
if (upToEventId) {
// Small optimization for exact match (prevent search)
if (upToEventId === ev.getId()) {
return;
}

let isBeforeMark = true;

// Timelines are most recent last, so reverse the order and limit ourselves to 100 events
// to avoid overusing the CPU.
const timeline = MatrixClientPeg.get().getRoom(ev.getRoomId()).getLiveTimeline();
const events = arrayFastClone(timeline.getEvents()).reverse().slice(0, 100);

for (const timelineEvent of events) {
if (timelineEvent.getId() === upToEventId) {
break;
} else if (timelineEvent.getId() === ev.getId()) {
isBeforeMark = false;
break;
}
}

if (isBeforeMark) {
// Ignore the event: it is before our interest.
return;
}
}

this.readUpToMap[ev.getRoomId()] = ev.getId();

const raw = ev.getEffectiveEvent();
this.messaging.feedEvent(raw).catch(e => {
console.error("Error sending event to widget: ", e);
Expand Down