Skip to content

Commit 1e2f701

Browse files
t3chguytoger5
authored andcommitted
Fix edge cases around 2nd order relations and threads (#3437)
* Fix tests oversimplifying threads fixtures * Check for unsigned thread_id in MatrixEvent::threadRootId * Fix threads order being racy * Make Sonar happier * Iterate
1 parent 6f79cd2 commit 1e2f701

File tree

4 files changed

+45
-35
lines changed

4 files changed

+45
-35
lines changed

spec/integ/matrix-client-event-timeline.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,7 @@ describe("MatrixClient event timelines", function () {
12741274
THREAD_ROOT.event_id,
12751275
THREAD_REPLY.event_id,
12761276
THREAD_REPLY2.getId(),
1277+
THREAD_ROOT_REACTION.getId(),
12771278
THREAD_REPLY3.getId(),
12781279
]);
12791280
});
@@ -1322,7 +1323,7 @@ describe("MatrixClient event timelines", function () {
13221323
request.respond(200, function () {
13231324
return {
13241325
original_event: root,
1325-
chunk: [replies],
1326+
chunk: replies,
13261327
// no next batch as this is the oldest end of the timeline
13271328
};
13281329
});
@@ -1479,7 +1480,7 @@ describe("MatrixClient event timelines", function () {
14791480
user: userId,
14801481
type: "m.room.message",
14811482
content: {
1482-
"body": "thread reply",
1483+
"body": "thread2 reply",
14831484
"msgtype": "m.text",
14841485
"m.relates_to": {
14851486
// We can't use the const here because we change server support mode for test
@@ -1499,7 +1500,7 @@ describe("MatrixClient event timelines", function () {
14991500
user: userId,
15001501
type: "m.room.message",
15011502
content: {
1502-
"body": "thread reply",
1503+
"body": "thread reply2",
15031504
"msgtype": "m.text",
15041505
"m.relates_to": {
15051506
// We can't use the const here because we change server support mode for test
@@ -1567,7 +1568,7 @@ describe("MatrixClient event timelines", function () {
15671568
// Test adding a second event to the first thread
15681569
const thread = room.getThread(THREAD_ROOT.event_id!)!;
15691570
thread.initialEventsFetched = true;
1570-
const prom = emitPromise(room, ThreadEvent.NewReply);
1571+
const prom = emitPromise(room, ThreadEvent.Update);
15711572
respondToEvent(THREAD_ROOT_UPDATED);
15721573
respondToEvent(THREAD_ROOT_UPDATED);
15731574
respondToEvent(THREAD_ROOT_UPDATED);

spec/unit/event-timeline-set.spec.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,6 @@ describe("EventTimelineSet", () => {
142142
});
143143

144144
describe("addEventToTimeline", () => {
145-
let thread: Thread;
146-
147-
beforeEach(() => {
148-
(client.supportsThreads as jest.Mock).mockReturnValue(true);
149-
thread = new Thread("!thread_id:server", messageEvent, { room, client });
150-
});
151-
152145
it("Adds event to timeline", () => {
153146
const liveTimeline = eventTimelineSet.getLiveTimeline();
154147
expect(liveTimeline.getEvents().length).toStrictEqual(0);
@@ -167,6 +160,15 @@ describe("EventTimelineSet", () => {
167160
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, true, false);
168161
}).not.toThrow();
169162
});
163+
});
164+
165+
describe("addEventToTimeline (thread timeline)", () => {
166+
let thread: Thread;
167+
168+
beforeEach(() => {
169+
(client.supportsThreads as jest.Mock).mockReturnValue(true);
170+
thread = new Thread("!thread_id:server", messageEvent, { room, client });
171+
});
170172

171173
it("should not add an event to a timeline that does not belong to the timelineSet", () => {
172174
const eventTimelineSet2 = new EventTimelineSet(room);
@@ -197,7 +199,14 @@ describe("EventTimelineSet", () => {
197199
const liveTimeline = eventTimelineSetForThread.getLiveTimeline();
198200
expect(liveTimeline.getEvents().length).toStrictEqual(0);
199201

200-
eventTimelineSetForThread.addEventToTimeline(messageEvent, liveTimeline, {
202+
const normalMessage = utils.mkMessage({
203+
room: roomId,
204+
user: userA,
205+
msg: "Hello!",
206+
event: true,
207+
});
208+
209+
eventTimelineSetForThread.addEventToTimeline(normalMessage, liveTimeline, {
201210
toStartOfTimeline: true,
202211
});
203212
expect(liveTimeline.getEvents().length).toStrictEqual(0);
@@ -336,7 +345,9 @@ describe("EventTimelineSet", () => {
336345
});
337346

338347
it("should return true if the timeline set is not for a thread and the event is a thread root", () => {
348+
const thread = new Thread(messageEvent.getId()!, messageEvent, { room, client });
339349
const eventTimelineSet = new EventTimelineSet(room, {}, client);
350+
messageEvent.setThread(thread);
340351
expect(eventTimelineSet.canContain(messageEvent)).toBeTruthy();
341352
});
342353

src/models/event.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -579,9 +579,18 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
579579
const relatesTo = this.getWireContent()?.["m.relates_to"];
580580
if (relatesTo?.rel_type === THREAD_RELATION_TYPE.name) {
581581
return relatesTo.event_id;
582-
} else {
583-
return this.getThread()?.id || this.threadId;
584582
}
583+
if (this.thread) {
584+
return this.thread.id;
585+
}
586+
if (this.threadId !== undefined) {
587+
return this.threadId;
588+
}
589+
const unsigned = this.getUnsigned();
590+
if (typeof unsigned[UNSIGNED_THREAD_ID_FIELD.name] === "string") {
591+
return unsigned[UNSIGNED_THREAD_ID_FIELD.name];
592+
}
593+
return undefined;
585594
}
586595

587596
/**
@@ -593,7 +602,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
593602
// Bundled relationships only returned when the sync response is limited
594603
// hence us having to check both bundled relation and inspect the thread
595604
// model
596-
return !!threadDetails || this.getThread()?.id === this.getId();
605+
return !!threadDetails || this.threadRootId === this.getId();
597606
}
598607

599608
public get replyEventId(): string | undefined {

src/models/room.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,7 +1957,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
19571957
}
19581958
}
19591959

1960-
this.on(ThreadEvent.NewReply, this.onThreadNewReply);
1960+
this.on(ThreadEvent.Update, this.onThreadUpdate);
19611961
this.on(ThreadEvent.Delete, this.onThreadDelete);
19621962
this.threadsReady = true;
19631963
}
@@ -2055,7 +2055,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
20552055
}
20562056
}
20572057

2058-
private onThreadNewReply(thread: Thread): void {
2058+
private onThreadUpdate(thread: Thread): void {
20592059
this.updateThreadRootEvents(thread, false, true);
20602060
}
20612061

@@ -2113,12 +2113,13 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
21132113
};
21142114
}
21152115

2116-
// A thread relation is always only shown in a thread
2117-
if (event.isRelation(THREAD_RELATION_TYPE.name)) {
2116+
// A thread relation (1st and 2nd order) is always only shown in a thread
2117+
const threadRootId = event.threadRootId;
2118+
if (threadRootId != undefined) {
21182119
return {
21192120
shouldLiveInRoom: false,
21202121
shouldLiveInThread: true,
2121-
threadId: event.threadRootId,
2122+
threadId: threadRootId,
21222123
};
21232124
}
21242125

@@ -2149,15 +2150,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
21492150
};
21502151
}
21512152

2152-
const unsigned = event.getUnsigned();
2153-
if (typeof unsigned[UNSIGNED_THREAD_ID_FIELD.name] === "string") {
2154-
return {
2155-
shouldLiveInRoom: false,
2156-
shouldLiveInThread: true,
2157-
threadId: unsigned[UNSIGNED_THREAD_ID_FIELD.name],
2158-
};
2159-
}
2160-
21612153
// We've exhausted all scenarios,
21622154
// we cannot assume that it lives in the main timeline as this may be a relation for an unknown thread
21632155
// adding the event in the wrong timeline causes stuck notifications and can break ability to send read receipts
@@ -2890,12 +2882,9 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
28902882
private findThreadRoots(events: MatrixEvent[]): Set<string> {
28912883
const threadRoots = new Set<string>();
28922884
for (const event of events) {
2893-
if (event.isRelation(THREAD_RELATION_TYPE.name)) {
2894-
threadRoots.add(event.relationEventId ?? "");
2895-
}
2896-
const unsigned = event.getUnsigned();
2897-
if (typeof unsigned[UNSIGNED_THREAD_ID_FIELD.name] === "string") {
2898-
threadRoots.add(unsigned[UNSIGNED_THREAD_ID_FIELD.name]!);
2885+
const threadRootId = event.threadRootId;
2886+
if (threadRootId != undefined) {
2887+
threadRoots.add(threadRootId);
28992888
}
29002889
}
29012890
return threadRoots;

0 commit comments

Comments
 (0)