diff --git a/spec/integ/matrix-client-event-timeline.spec.js b/spec/integ/matrix-client-event-timeline.spec.js index 454a4d30323..420fee6e588 100644 --- a/spec/integ/matrix-client-event-timeline.spec.js +++ b/spec/integ/matrix-client-event-timeline.spec.js @@ -1,5 +1,5 @@ import * as utils from "../test-utils/test-utils"; -import { EventTimeline } from "../../src/matrix"; +import { EventTimeline, Filter, MatrixEvent } from "../../src/matrix"; import { logger } from "../../src/logger"; import { TestClient } from "../TestClient"; import { Thread, THREAD_RELATION_TYPE } from "../../src/models/thread"; @@ -500,7 +500,8 @@ describe("MatrixClient event timelines", function() { Thread.setServerSideSupport(true); client.stopClient(); // we don't need the client to be syncing at this time const room = client.getRoom(roomId); - const timelineSet = room.getTimelineSets()[0]; + const thread = room.createThread(THREAD_ROOT.event_id, undefined, [], false); + const timelineSet = thread.timelineSet; httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_REPLY.event_id)) .respond(200, function() { @@ -538,6 +539,92 @@ describe("MatrixClient event timelines", function() { expect(timeline.getEvents().find(e => e.getId() === THREAD_ROOT.event_id)).toBeTruthy(); expect(timeline.getEvents().find(e => e.getId() === THREAD_REPLY.event_id)).toBeTruthy(); }); + + it("should return undefined when event is not within a thread but timelineSet is", async () => { + client.clientOpts.experimentalThreadSupport = true; + Thread.setServerSideSupport(true); + client.stopClient(); // we don't need the client to be syncing at this time + const room = client.getRoom(roomId); + const threadRoot = new MatrixEvent(THREAD_ROOT); + const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false); + const timelineSet = thread.timelineSet; + + 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(EVENTS[0].event_id)) + .respond(200, function() { + return { + start: "start_token0", + events_before: [], + event: EVENTS[0], + events_after: [], + end: "end_token0", + state: [], + }; + }); + + const timelinePromise = client.getEventTimeline(timelineSet, EVENTS[0].event_id); + await httpBackend.flushAllExpected(); + + const timeline = await timelinePromise; + expect(timeline).toBeUndefined(); + }); + + it("should return undefined when event is within a thread but timelineSet is not", async () => { + client.clientOpts.experimentalThreadSupport = true; + Thread.setServerSideSupport(true); + client.stopClient(); // we don't need the client to be syncing at this time + const room = client.getRoom(roomId); + const timelineSet = room.getTimelineSets()[0]; + + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_REPLY.event_id)) + .respond(200, function() { + return { + start: "start_token0", + events_before: [], + event: THREAD_REPLY, + events_after: [], + end: "end_token0", + state: [], + }; + }); + + const timelinePromise = client.getEventTimeline(timelineSet, THREAD_REPLY.event_id); + await httpBackend.flushAllExpected(); + + const timeline = await timelinePromise; + expect(timeline).toBeUndefined(); + }); + + it("should should add lazy loading filter when requested", async () => { + client.clientOpts.lazyLoadMembers = true; + client.stopClient(); // we don't need the client to be syncing at this time + const room = client.getRoom(roomId); + const timelineSet = room.getTimelineSets()[0]; + + const req = httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id)); + req.respond(200, function() { + return { + start: "start_token0", + events_before: [], + event: EVENTS[0], + events_after: [], + end: "end_token0", + state: [], + }; + }); + req.check((request) => { + expect(request.opts.qs.filter).toEqual(JSON.stringify(Filter.LAZY_LOADING_MESSAGES_FILTER)); + }); + + await Promise.all([ + client.getEventTimeline(timelineSet, EVENTS[0].event_id), + httpBackend.flushAllExpected(), + ]); + }); }); describe("getLatestTimeline", function() { diff --git a/src/client.ts b/src/client.ts index 304655eac3b..c29578d20ac 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5245,14 +5245,15 @@ export class MatrixClient extends TypedEventEmitterIf the EventTimelineSet object already has the given event in its store, the * corresponding timeline will be returned. Otherwise, a /context request is * made, and used to construct an EventTimeline. + * If the event does not belong to this EventTimelineSet then undefined will be returned. * - * @param {EventTimelineSet} timelineSet The timelineSet to look for the event in + * @param {EventTimelineSet} timelineSet The timelineSet to look for the event in, must be bound to a room * @param {string} eventId The ID of the event to look for * * @return {Promise} Resolves: * {@link module:models/event-timeline~EventTimeline} including the given event */ - public async getEventTimeline(timelineSet: EventTimelineSet, eventId: string): Promise { + public async getEventTimeline(timelineSet: EventTimelineSet, eventId: string): Promise { // don't allow any timeline support unless it's been enabled. if (!this.timelineSupport) { throw new Error("timeline support is disabled. Set the 'timelineSupport'" + @@ -5297,38 +5298,44 @@ export class MatrixClient extends TypedEventEmitter { this.timelineSet = new EventTimelineSet(this.room, { timelineSupport: true, pendingEvents: true, - }); + }, this.client, this); this.reEmitter = new TypedReEmitter(this); this.reEmitter.reEmit(this.timelineSet, [