1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-09 10:22:46 +03:00

Emit relations created when target event added later

This changes the "relations created" event to ensure it is properly emitted even
if the target event is added to the timeline after the relation event. There was
perhaps always a risk of this happening in the past, but is seems more likely to
bite now with delayed decryption.

Part of https://github.com/vector-im/element-web/issues/17461
This commit is contained in:
J. Ryan Stinnett
2021-05-26 16:35:49 +01:00
parent acb9bc8cc5
commit 270be2df7a
3 changed files with 87 additions and 16 deletions

View File

@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { EventTimelineSet } from "../../src/models/event-timeline-set";
import { MatrixEvent } from "../../src/models/event"; import { MatrixEvent } from "../../src/models/event";
import { Relations } from "../../src/models/relations"; import { Relations } from "../../src/models/relations";
@@ -70,4 +71,63 @@ describe("Relations", function() {
expect(events.size).toEqual(1); expect(events.size).toEqual(1);
} }
}); });
it("should emit created regardless of ordering", async function () {
const targetEvent = new MatrixEvent({
"sender": "@bob:example.com",
"type": "m.room.message",
"event_id": "$2s4yYpEkVQrPglSCSqB_m6E8vDhWsg0yFNyOJdVIb_o",
"room_id": "!pzVjCQSoQPpXQeHpmK:example.com",
"content": {},
});
const relationEvent = new MatrixEvent({
"sender": "@bob:example.com",
"type": "m.reaction",
"event_id": "$cZ1biX33ENJqIm00ks0W_hgiO_6CHrsAc3ZQrnLeNTw",
"room_id": "!pzVjCQSoQPpXQeHpmK:example.com",
"content": {
"m.relates_to": {
"event_id": "$2s4yYpEkVQrPglSCSqB_m6E8vDhWsg0yFNyOJdVIb_o",
"key": "👍️",
"rel_type": "m.annotation",
},
},
});
// Stub the room
const room = {
getPendingEvent() { return null; },
getUnfilteredTimelineSet() { return null; },
};
// Add the target event first, then the relation event
{
const relationsCreated = new Promise(resolve => {
targetEvent.once("Event.relationsCreated", resolve);
})
const timelineSet = new EventTimelineSet(room, {
unstableClientRelationAggregation: true,
});
timelineSet.addLiveEvent(targetEvent);
timelineSet.addLiveEvent(relationEvent);
await relationsCreated;
}
// Add the relation event first, then the target event
{
const relationsCreated = new Promise(resolve => {
targetEvent.once("Event.relationsCreated", resolve);
})
const timelineSet = new EventTimelineSet(room, {
unstableClientRelationAggregation: true,
});
timelineSet.addLiveEvent(relationEvent);
timelineSet.addLiveEvent(targetEvent);
await relationsCreated;
}
});
}); });

View File

@@ -740,16 +740,11 @@ EventTimelineSet.prototype.setRelationsTarget = function(event) {
if (!relationsForEvent) { if (!relationsForEvent) {
return; return;
} }
// don't need it for non m.replace relations for now
const relationsWithRelType = relationsForEvent["m.replace"];
if (!relationsWithRelType) {
return;
}
// only doing replacements for messages for now (e.g. edits)
const relationsWithEventType = relationsWithRelType["m.room.message"];
if (relationsWithEventType) { for (const relationsWithRelType of Object.values(relationsForEvent)) {
relationsWithEventType.setTargetEvent(event); for (const relationsWithEventType of Object.values(relationsWithRelType)) {
relationsWithEventType.setTargetEvent(event);
}
} }
}; };
@@ -797,7 +792,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
} }
let relationsWithEventType = relationsWithRelType[eventType]; let relationsWithEventType = relationsWithRelType[eventType];
let isNewRelations = false;
let relatesToEvent; let relatesToEvent;
if (!relationsWithEventType) { if (!relationsWithEventType) {
relationsWithEventType = relationsWithRelType[eventType] = new Relations( relationsWithEventType = relationsWithRelType[eventType] = new Relations(
@@ -805,7 +799,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
eventType, eventType,
this.room, this.room,
); );
isNewRelations = true;
relatesToEvent = this.findEventById(relatesToEventId) || this.room.getPendingEvent(relatesToEventId); relatesToEvent = this.findEventById(relatesToEventId) || this.room.getPendingEvent(relatesToEventId);
if (relatesToEvent) { if (relatesToEvent) {
relationsWithEventType.setTargetEvent(relatesToEvent); relationsWithEventType.setTargetEvent(relatesToEvent);
@@ -813,11 +806,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
} }
relationsWithEventType.addEvent(event); relationsWithEventType.addEvent(event);
// only emit once event has been added to relations
if (isNewRelations && relatesToEvent) {
relatesToEvent.emit("Event.relationsCreated", relationType, eventType);
}
}; };
/** /**

View File

@@ -48,6 +48,7 @@ export class Relations extends EventEmitter {
this._sortedAnnotationsByKey = []; this._sortedAnnotationsByKey = [];
this._targetEvent = null; this._targetEvent = null;
this._room = room; this._room = room;
this._creationEmitted = false;
} }
/** /**
@@ -94,6 +95,8 @@ export class Relations extends EventEmitter {
event.on("Event.beforeRedaction", this._onBeforeRedaction); event.on("Event.beforeRedaction", this._onBeforeRedaction);
this.emit("Relations.add", event); this.emit("Relations.add", event);
this._maybeEmitCreated();
} }
/** /**
@@ -345,6 +348,7 @@ export class Relations extends EventEmitter {
return; return;
} }
this._targetEvent = event; this._targetEvent = event;
if (this.relationType === "m.replace") { if (this.relationType === "m.replace") {
const replacement = await this.getLastReplacement(); const replacement = await this.getLastReplacement();
// this is the initial update, so only call it if we already have something // this is the initial update, so only call it if we already have something
@@ -353,5 +357,24 @@ export class Relations extends EventEmitter {
this._targetEvent.makeReplaced(replacement); this._targetEvent.makeReplaced(replacement);
} }
} }
this._maybeEmitCreated();
}
_maybeEmitCreated() {
if (this._creationEmitted) {
return;
}
// Only emit we're "created" once we have a target event instance _and_
// at least one related event.
if (!this._targetEvent || !this._relations.size) {
return;
}
this._creationEmitted = true;
this._targetEvent.emit(
"Event.relationsCreated",
this.relationType,
this.eventType,
);
} }
} }