From d37a76fe64f640c43235f3485c72532e98503341 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Fri, 19 May 2023 12:34:26 +0100 Subject: [PATCH] Attempt a potential workaround for stuck notifs (#3384) * Attempt a potential workaround for stuck notifs * Remove TODOs * Fix backwards logic about server support for MSC3981 in fetchEditsWhereNeeded * Check for lack of MSC3981 server support before calling insertEventIntoTimeline * If no parent event is found, insert purely based on timestamp * Mark temporary methods as internal (cherry picked from commit cd26ba67d4ac8688d10058a8e5de571de8c5268c) --- src/models/event-timeline-set.ts | 88 ++++++++++++++++++++++++++++++++ src/models/event-timeline.ts | 39 ++++++++++++++ src/models/thread.ts | 39 +++++++++++++- 3 files changed, 164 insertions(+), 2 deletions(-) diff --git a/src/models/event-timeline-set.ts b/src/models/event-timeline-set.ts index 5cb04997e8b..dfe9694c2ed 100644 --- a/src/models/event-timeline-set.ts +++ b/src/models/event-timeline-set.ts @@ -756,6 +756,94 @@ export class EventTimelineSet extends TypedEventEmitter event.getTs()) { + // We found an event later than ours, so insert before that. + break; + } + } + // If we got to the end of the loop, insertIndex points at the end of + // the list. + + const eventId = event.getId()!; + timeline.insertEvent(event, insertIndex, roomState); + this._eventIdToTimeline.set(eventId, timeline); + + this.relations.aggregateParentEvent(event); + this.relations.aggregateChildEvent(event, this); + + const data: IRoomTimelineData = { + timeline: timeline, + liveEvent: timeline == this.liveTimeline, + }; + this.emit(RoomEvent.Timeline, event, this.room, false, false, data); + } + /** * Replaces event with ID oldEventId with one with newEventId, if oldEventId is * recognised. Otherwise, add to the live timeline. Used to handle remote echos. diff --git a/src/models/event-timeline.ts b/src/models/event-timeline.ts index d1ba3210365..67be25fbc9c 100644 --- a/src/models/event-timeline.ts +++ b/src/models/event-timeline.ts @@ -427,6 +427,45 @@ export class EventTimeline { } } + /** + * Insert a new event into the timeline, and update the state. + * + * TEMPORARY: until we have recursive relations, we need this function + * to exist to allow us to insert events in timeline order, which is our + * best guess for Sync Order. + * This is a copy of addEvent above, modified to allow inserting an event at + * a specific index. + * + * @internal + */ + public insertEvent(event: MatrixEvent, insertIndex: number, roomState: RoomState): void { + const timelineSet = this.getTimelineSet(); + + if (timelineSet.room) { + EventTimeline.setEventMetadata(event, roomState, false); + + // modify state but only on unfiltered timelineSets + if (event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) { + roomState.setStateEvents([event], {}); + // it is possible that the act of setting the state event means we + // can set more metadata (specifically sender/target props), so try + // it again if the prop wasn't previously set. It may also mean that + // the sender/target is updated (if the event set was a room member event) + // so we want to use the *updated* member (new avatar/name) instead. + // + // However, we do NOT want to do this on member events if we're going + // back in time, else we'll set the .sender value for BEFORE the given + // member event, whereas we want to set the .sender value for the ACTUAL + // member event itself. + if (!event.sender || event.getType() === EventType.RoomMember) { + EventTimeline.setEventMetadata(event, roomState!, false); + } + } + } + + this.events.splice(insertIndex, 0, event); // insert element + } + /** * Remove an event from the timeline * diff --git a/src/models/thread.ts b/src/models/thread.ts index c31499b2b34..f2420842dfc 100644 --- a/src/models/thread.ts +++ b/src/models/thread.ts @@ -236,6 +236,33 @@ export class Thread extends ReadReceipt { } } + /** + * TEMPORARY. Only call this when MSC3981 is not available, and we have some + * late-arriving events to insert, because we recursively found them as part + * of populating a thread. When we have MSC3981 we won't need it, because + * they will all be supplied by the homeserver in one request, and they will + * already be in the right order in that response. + * This is a copy of addEventToTimeline above, modified to call + * insertEventIntoTimeline so this event is inserted into our best guess of + * the right place based on timestamp. (We should be using Sync Order but we + * don't have it.) + * + * @internal + */ + public insertEventIntoTimeline(event: MatrixEvent): void { + const eventId = event.getId(); + if (!eventId) { + return; + } + if (this.findEventById(eventId)) { + return; + } + this.timelineSet.insertEventIntoTimeline(event, this.liveTimeline, this.roomState); + + // As far as we know, timeline should always be the same as events + this.timeline = this.events; + } + public addEvents(events: MatrixEvent[], toStartOfTimeline: boolean): void { events.forEach((ev) => this.addEvent(ev, toStartOfTimeline, false)); this.updateThreadMetadata(); @@ -281,7 +308,14 @@ export class Thread extends ReadReceipt { */ this.replayEvents?.push(event); } else { - this.addEventToTimeline(event, toStartOfTimeline); + const recursionSupport = + this.client.canSupport.get(Feature.RelationsRecursion) ?? ServerSupport.Unsupported; + + if (recursionSupport === ServerSupport.Unsupported) { + this.insertEventIntoTimeline(event); + } else { + this.addEventToTimeline(event, toStartOfTimeline); + } } // Apply annotations and replace relations to the relations of the timeline only this.timelineSet.relations?.aggregateParentEvent(event); @@ -460,7 +494,7 @@ export class Thread extends ReadReceipt { // XXX: Workaround for https://github.com/matrix-org/matrix-spec-proposals/pull/2676/files#r827240084 private async fetchEditsWhereNeeded(...events: MatrixEvent[]): Promise { const recursionSupport = this.client.canSupport.get(Feature.RelationsRecursion) ?? ServerSupport.Unsupported; - if (recursionSupport !== ServerSupport.Unsupported) { + if (recursionSupport === ServerSupport.Unsupported) { return Promise.all( events .filter((e) => e.isEncrypted()) @@ -473,6 +507,7 @@ export class Thread extends ReadReceipt { .then((relations) => { if (relations.events.length) { event.makeReplaced(relations.events[0]); + this.insertEventIntoTimeline(event); } }) .catch((e) => {