From db6a8ae0101a7f8c4886d91b14ead8c836957dd2 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 6 Jun 2023 11:35:06 +0200 Subject: [PATCH 1/7] Set UNSIGNED_THREAD_ID on messages retrieved in thread timelines --- src/client.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/client.ts b/src/client.ts index caca1a89a82..3eaea7efdf1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -151,6 +151,7 @@ import { UNSTABLE_MSC3088_PURPOSE, UNSTABLE_MSC3089_TREE_SUBTYPE, MSC3912_RELATION_BASED_REDACTIONS_PROP, + UNSIGNED_THREAD_ID_FIELD, } from "./@types/event"; import { IdServerUnbindResult, IImageInfo, Preset, Visibility } from "./@types/partials"; import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper"; @@ -5774,6 +5775,10 @@ export class MatrixClient extends TypedEventEmitter Date: Tue, 6 Jun 2023 11:35:59 +0200 Subject: [PATCH 2/7] If recursive relations are available, request the entire thread for thread timelines --- src/client.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/client.ts b/src/client.ts index 3eaea7efdf1..fb8ffbd5fbe 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5751,18 +5751,19 @@ export class MatrixClient extends TypedEventEmitter Date: Tue, 6 Jun 2023 11:45:31 +0200 Subject: [PATCH 3/7] chore: fixed lint issues --- src/client.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/client.ts b/src/client.ts index fb8ffbd5fbe..9bd8d7e7421 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5778,7 +5778,7 @@ export class MatrixClient extends TypedEventEmitter = res.end; while (nextBatch) { @@ -5908,13 +5906,11 @@ export class MatrixClient extends TypedEventEmitter Date: Tue, 6 Jun 2023 12:01:41 +0200 Subject: [PATCH 4/7] Adapt tests to new changed behaviour --- .../integ/matrix-client-event-timeline.spec.ts | 18 ++++++++++++++++-- spec/unit/room.spec.ts | 4 ++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index c7083db1968..1a2a6c07d6e 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -29,6 +29,7 @@ import { PendingEventOrdering, RelationType, Room, + UNSIGNED_THREAD_ID_FIELD, } from "../../src/matrix"; import { logger } from "../../src/logger"; import { encodeParams, encodeUri, QueryDict, replaceParam } from "../../src/utils"; @@ -179,6 +180,9 @@ const THREAD_REPLY = utils.mkEvent({ event_id: THREAD_ROOT.event_id, }, }, + unsigned: { + [UNSIGNED_THREAD_ID_FIELD.name]: THREAD_ROOT.event_id, + }, event: false, }); @@ -1091,6 +1095,9 @@ describe("MatrixClient event timelines", function () { event_id: THREAD_ROOT.event_id, }, }, + unsigned: { + [UNSIGNED_THREAD_ID_FIELD.name]: THREAD_ROOT.event_id, + }, event: true, }); THREAD_REPLY2.localTimestamp += 1000; @@ -1109,6 +1116,9 @@ describe("MatrixClient event timelines", function () { event_id: THREAD_ROOT.event_id, }, }, + unsigned: { + [UNSIGNED_THREAD_ID_FIELD.name]: THREAD_ROOT.event_id, + }, event: true, }); THREAD_REPLY3.localTimestamp += 2000; @@ -1182,6 +1192,9 @@ describe("MatrixClient event timelines", function () { event_id: THREAD_ROOT.event_id, }, }, + unsigned: { + [UNSIGNED_THREAD_ID_FIELD.name]: THREAD_ROOT.event_id, + }, event: true, }); THREAD_REPLY2.localTimestamp += 1000; @@ -1214,6 +1227,9 @@ describe("MatrixClient event timelines", function () { event_id: THREAD_ROOT.event_id, }, }, + unsigned: { + [UNSIGNED_THREAD_ID_FIELD.name]: THREAD_ROOT.event_id, + }, event: true, }); THREAD_REPLY3.localTimestamp += 3000; @@ -1254,8 +1270,6 @@ describe("MatrixClient event timelines", function () { "GET", "/_matrix/client/v1/rooms/!foo%3Abar/relations/" + encodeURIComponent(THREAD_ROOT_UPDATED.event_id!) + - "/" + - encodeURIComponent(THREAD_RELATION_TYPE.name) + buildRelationPaginationQuery({ dir: Direction.Backward, limit: 3, diff --git a/spec/unit/room.spec.ts b/spec/unit/room.spec.ts index 2ed44dffd7b..4e2292810eb 100644 --- a/spec/unit/room.spec.ts +++ b/spec/unit/room.spec.ts @@ -42,6 +42,7 @@ import { RelationType, RoomEvent, RoomMember, + UNSIGNED_THREAD_ID_FIELD, } from "../../src"; import { EventTimeline } from "../../src/models/event-timeline"; import { NotificationCountType, Room } from "../../src/models/room"; @@ -133,6 +134,9 @@ describe("Room", function () { "rel_type": "m.thread", }, }, + unsigned: { + [UNSIGNED_THREAD_ID_FIELD.name]: root.getId(), + }, }, room.client, ); From 62c477084bddaeda6a8bee7c8325fc987836d33a Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 6 Jun 2023 13:19:47 +0200 Subject: [PATCH 5/7] Add new tests to increase test coverage --- .../matrix-client-event-timeline.spec.ts | 87 +++++++++++++++++-- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index 1a2a6c07d6e..7e4265bcd6a 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -53,12 +53,16 @@ const withoutRoomId = (e: Partial): Partial => { /** * Our httpBackend only allows matching calls if we have the exact same query, in the exact same order * This method allows building queries with the exact same parameter order as the fetchRelations method in client + * @param client Matrix client to mock the request for * @param params query parameters */ -const buildRelationPaginationQuery = (params: QueryDict): string => { +const buildRelationPaginationQuery = (client: MatrixClient, params: QueryDict): string => { if (Thread.hasServerSideFwdPaginationSupport === FeatureSupport.Experimental) { params = replaceParam("dir", "org.matrix.msc3715.dir", params); } + if (client.canSupport.get(Feature.RelationsRecursion) === ServerSupport.Unstable) { + params = replaceParam("recurse", "org.matrix.msc3981.recurse", params); + } return "?" + encodeParams(params).toString(); }; @@ -626,7 +630,7 @@ describe("MatrixClient event timelines", function () { encodeURIComponent(THREAD_ROOT.event_id!) + "/" + encodeURIComponent(THREAD_RELATION_TYPE.name) + - buildRelationPaginationQuery({ dir: Direction.Backward, limit: 1 }), + buildRelationPaginationQuery(client, { dir: Direction.Backward, limit: 1 }), ) .respond(200, function () { return { @@ -1024,6 +1028,77 @@ describe("MatrixClient event timelines", function () { ]); }); + it("should use recursive relations to paginate thread timelines", async function () { + function respondToThreads( + response = { + chunk: [THREAD_ROOT], + state: [], + next_batch: null, + }, + ): ExpectedHttpRequest { + const request = httpBackend.when( + "GET", + encodeUri("/_matrix/client/v1/rooms/$roomId/threads", { + $roomId: roomId, + }), + ); + request.respond(200, response); + return request; + } + + function respondToThread(root: Partial, replies: Partial[], limit?: number): ExpectedHttpRequest { + const request = httpBackend.when( + "GET", + "/_matrix/client/v1/rooms/!foo%3Abar/relations/" + + encodeURIComponent(root.event_id!) + + buildRelationPaginationQuery(client, { + dir: Direction.Backward, + limit: limit, + recurse: true, + }), + ); + request.respond(200, function () { + return { + original_event: root, + chunk: replies, + // no next batch as this is the oldest end of the timeline + }; + }); + return request; + } + + function respondToEvent(event: Partial = THREAD_ROOT): ExpectedHttpRequest { + const request = httpBackend.when( + "GET", + encodeUri("/_matrix/client/r0/rooms/$roomId/event/$eventId", { + $roomId: roomId, + $eventId: event.event_id!, + }), + ); + request.respond(200, event); + return request; + } + + // Setup + // @ts-ignore + client.clientOpts.threadSupport = true; + Thread.setServerSideSupport(FeatureSupport.Stable); + Thread.setServerSideListSupport(FeatureSupport.Stable); + Thread.setServerSideFwdPaginationSupport(FeatureSupport.Stable); + client.canSupport.set(Feature.RelationsRecursion, ServerSupport.Unstable); + const room = client.getRoom(roomId)!; + await room!.createThreadsTimelineSets(); + respondToThreads(); + respondToThreads(); + respondToEvent(); + respondToEvent(); + respondToEvent(); + respondToEvent(); + respondToEvent(); + respondToThread(THREAD_ROOT, [THREAD_REPLY], 1); + await flushHttp(room.fetchRoomThreads()); + }); + it("should create threads for thread roots discovered", function () { const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; @@ -1270,7 +1345,7 @@ describe("MatrixClient event timelines", function () { "GET", "/_matrix/client/v1/rooms/!foo%3Abar/relations/" + encodeURIComponent(THREAD_ROOT_UPDATED.event_id!) + - buildRelationPaginationQuery({ + buildRelationPaginationQuery(client, { dir: Direction.Backward, limit: 3, recurse: true, @@ -1918,7 +1993,7 @@ describe("MatrixClient event timelines", function () { encodeURIComponent(THREAD_ROOT.event_id!) + "/" + encodeURIComponent(THREAD_RELATION_TYPE.name) + - buildRelationPaginationQuery({ dir: Direction.Backward, limit: 1 }), + buildRelationPaginationQuery(client, { dir: Direction.Backward, limit: 1 }), ) .respond(200, function () { return { @@ -1971,7 +2046,7 @@ describe("MatrixClient event timelines", function () { encodeURIComponent(THREAD_ROOT.event_id!) + "/" + encodeURIComponent(THREAD_RELATION_TYPE.name) + - buildRelationPaginationQuery({ + buildRelationPaginationQuery(client, { dir: Direction.Backward, from: "start_token", }), @@ -1988,7 +2063,7 @@ describe("MatrixClient event timelines", function () { encodeURIComponent(THREAD_ROOT.event_id!) + "/" + encodeURIComponent(THREAD_RELATION_TYPE.name) + - buildRelationPaginationQuery({ dir: Direction.Forward, from: "end_token" }), + buildRelationPaginationQuery(client, { dir: Direction.Forward, from: "end_token" }), ) .respond(200, function () { return { From 0d114efc0451524eddd53a5bcf46ba25ad8ac3e3 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 6 Jun 2023 15:11:56 +0200 Subject: [PATCH 6/7] chore: fixed lint issues --- .../integ/matrix-client-event-timeline.spec.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index 7e4265bcd6a..208e2072450 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -1046,16 +1046,20 @@ describe("MatrixClient event timelines", function () { return request; } - function respondToThread(root: Partial, replies: Partial[], limit?: number): ExpectedHttpRequest { + function respondToThread( + root: Partial, + replies: Partial[], + limit?: number, + ): ExpectedHttpRequest { const request = httpBackend.when( "GET", "/_matrix/client/v1/rooms/!foo%3Abar/relations/" + - encodeURIComponent(root.event_id!) + - buildRelationPaginationQuery(client, { - dir: Direction.Backward, - limit: limit, - recurse: true, - }), + encodeURIComponent(root.event_id!) + + buildRelationPaginationQuery(client, { + dir: Direction.Backward, + limit: limit, + recurse: true, + }), ); request.respond(200, function () { return { From e948f90a8be11f7f16f6ba12573fbd17275ce287 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 6 Jun 2023 15:58:37 +0200 Subject: [PATCH 7/7] increase test coverage --- spec/integ/matrix-client-event-timeline.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index 208e2072450..49e472e76aa 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -1101,6 +1101,12 @@ describe("MatrixClient event timelines", function () { respondToEvent(); respondToThread(THREAD_ROOT, [THREAD_REPLY], 1); await flushHttp(room.fetchRoomThreads()); + const thread = room.getThread(THREAD_ROOT.event_id!)!; + expect(thread).not.toBeNull(); + respondToThread(THREAD_ROOT, [THREAD_REPLY], 1); + expect(thread.timelineSet.thread).toBe(thread); + expect(Thread.hasServerSideSupport).toBe(FeatureSupport.Stable); + await flushHttp(client.getLatestTimeline(thread.timelineSet)); }); it("should create threads for thread roots discovered", function () {