diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index a520895160f..b02955511e9 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -1249,6 +1249,7 @@ describe("MatrixClient event timelines", function () { }, event: true, }); + THREAD_REPLY2.localTimestamp += 1000; // Test data for the first thread, with the second reply const THREAD_ROOT_UPDATED = { @@ -1305,6 +1306,7 @@ describe("MatrixClient event timelines", function () { // Test adding a second event to the first thread const thread = room.getThread(THREAD_ROOT.event_id!)!; + thread.initialEventsFetched = true; const prom = emitPromise(room, ThreadEvent.NewReply); respondToEvent(THREAD_ROOT_UPDATED); respondToEvent(THREAD_ROOT_UPDATED); @@ -1624,10 +1626,35 @@ describe("MatrixClient event timelines", function () { }, }, }); + httpBackend + .when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!)) + .respond(200, function () { + return THREAD_ROOT; + }); + httpBackend + .when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!)) + .respond(200, function () { + return THREAD_ROOT; + }); + httpBackend + .when( + "GET", + "/_matrix/client/v1/rooms/!foo%3Abar/relations/" + + encodeURIComponent(THREAD_ROOT.event_id!) + + "/" + + encodeURIComponent(THREAD_RELATION_TYPE.name) + + buildRelationPaginationQuery({ dir: Direction.Backward, limit: 1 }), + ) + .respond(200, function () { + return { + chunk: [THREAD_REPLY], + }; + }); await Promise.all([httpBackend.flushAllExpected(), utils.syncPromise(client)]); const room = client.getRoom(roomId)!; const thread = room.getThread(THREAD_ROOT.event_id!)!; + expect(thread.initialEventsFetched).toBeTruthy(); const timelineSet = thread.timelineSet; httpBackend @@ -1645,6 +1672,28 @@ describe("MatrixClient event timelines", function () { .respond(200, function () { return THREAD_ROOT; }); + httpBackend + .when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!)) + .respond(200, function () { + return THREAD_ROOT; + }); + httpBackend + .when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!)) + .respond(200, function () { + return THREAD_ROOT; + }); + httpBackend + .when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id!)) + .respond(200, function () { + return { + start: "start_token", + events_before: [], + event: THREAD_ROOT, + events_after: [], + end: "end_token", + state: [], + }; + }); httpBackend .when( "GET", @@ -1652,13 +1701,28 @@ describe("MatrixClient event timelines", function () { encodeURIComponent(THREAD_ROOT.event_id!) + "/" + encodeURIComponent(THREAD_RELATION_TYPE.name) + - buildRelationPaginationQuery({ dir: Direction.Backward, limit: 1 }), + buildRelationPaginationQuery({ dir: Direction.Backward, from: "start_token" }), ) .respond(200, function () { return { - chunk: [THREAD_ROOT], + chunk: [], }; }); + httpBackend + .when( + "GET", + "/_matrix/client/v1/rooms/!foo%3Abar/relations/" + + encodeURIComponent(THREAD_ROOT.event_id!) + + "/" + + encodeURIComponent(THREAD_RELATION_TYPE.name) + + buildRelationPaginationQuery({ dir: Direction.Forward, from: "end_token" }), + ) + .respond(200, function () { + return { + chunk: [THREAD_REPLY], + }; + }); + const timeline = await flushHttp(client.getEventTimeline(timelineSet, THREAD_ROOT.event_id!)); httpBackend.when("GET", "/sync").respond(200, { diff --git a/spec/unit/room.spec.ts b/spec/unit/room.spec.ts index 5036cabaaf4..81f6602e784 100644 --- a/spec/unit/room.spec.ts +++ b/spec/unit/room.spec.ts @@ -29,6 +29,8 @@ import { EventTimelineSet, EventType, IContent, + IEvent, + IRelationsRequestOpts, IStateEventWithRoomId, JoinRule, MatrixEvent, @@ -2489,11 +2491,24 @@ describe("Room", function () { }, }); + room.client.fetchRelations = ( + roomId: string, + eventId: string, + relationType?: RelationType | string | null, + eventType?: EventType | string | null, + opts: IRelationsRequestOpts = { dir: Direction.Backward }, + ) => + Promise.resolve({ + chunk: [threadResponse.event] as IEvent[], + next_batch: "start_token", + }); + let prom = emitPromise(room, ThreadEvent.New); room.addLiveEvents([randomMessage, threadRoot, threadResponse]); const thread: Thread = await prom; await emitPromise(room, ThreadEvent.Update); + expect(thread.initialEventsFetched).toBeTruthy(); expect(thread.replyToEvent!.event).toEqual(threadResponse.event); expect(thread.replyToEvent!.getContent().body).toBe(threadResponse.getContent().body); @@ -2704,13 +2719,27 @@ describe("Room", function () { }, }); + room.client.fetchRelations = ( + roomId: string, + eventId: string, + relationType?: RelationType | string | null, + eventType?: EventType | string | null, + opts: IRelationsRequestOpts = { dir: Direction.Backward }, + ) => + Promise.resolve({ + chunk: [threadResponse1.event] as IEvent[], + next_batch: "start_token", + }); + let prom = emitPromise(room, ThreadEvent.New); - room.addLiveEvents([threadRoot, threadResponse1, threadResponse2]); - const thread = await prom; + room.addLiveEvents([threadRoot, threadResponse1]); + const thread: Thread = await prom; await emitPromise(room, ThreadEvent.Update); + expect(thread.initialEventsFetched).toBeTruthy(); + room.addLiveEvents([threadResponse2]); expect(thread).toHaveLength(2); - expect(thread.replyToEvent.getId()).toBe(threadResponse2.getId()); + expect(thread.replyToEvent!.getId()).toBe(threadResponse2.getId()); room.client.fetchRoomEvent = (eventId: string) => Promise.resolve({ @@ -2733,7 +2762,7 @@ describe("Room", function () { await prom; await emitPromise(room, ThreadEvent.Update); expect(thread).toHaveLength(1); - expect(thread.replyToEvent.getId()).toBe(threadResponse1.getId()); + expect(thread.replyToEvent!.getId()).toBe(threadResponse1.getId()); room.client.fetchRoomEvent = (eventId: string) => Promise.resolve({ @@ -2757,7 +2786,7 @@ describe("Room", function () { await prom; await prom2; expect(thread).toHaveLength(0); - expect(thread.replyToEvent.getId()).toBe(threadRoot.getId()); + expect(thread.replyToEvent!.getId()).toBe(threadRoot.getId()); }); }); diff --git a/src/models/thread.ts b/src/models/thread.ts index eaa62e27951..bca2d92b427 100644 --- a/src/models/thread.ts +++ b/src/models/thread.ts @@ -262,7 +262,7 @@ export class Thread extends ReadReceipt { this.addEventToTimeline(event, toStartOfTimeline); this.client.decryptEventIfNeeded(event, {}); - } else if (!toStartOfTimeline && isNewestReply) { + } else if (!toStartOfTimeline && this.initialEventsFetched && isNewestReply) { this.addEventToTimeline(event, false); this.fetchEditsWhereNeeded(event); } else if (event.isRelation(RelationType.Annotation) || event.isRelation(RelationType.Replace)) {