@@ -15,8 +15,19 @@ limitations under the License.
1515*/
1616
1717import * as utils from "../test-utils/test-utils" ;
18- import { ClientEvent , EventTimeline , Filter , IEvent , MatrixClient , MatrixEvent , Room } from "../../src/matrix" ;
18+ import {
19+ ClientEvent ,
20+ Direction ,
21+ EventTimeline ,
22+ EventTimelineSet ,
23+ Filter ,
24+ IEvent ,
25+ MatrixClient ,
26+ MatrixEvent ,
27+ Room ,
28+ } from "../../src/matrix" ;
1929import { logger } from "../../src/logger" ;
30+ import { encodeUri } from "../../src/utils" ;
2031import { TestClient } from "../TestClient" ;
2132import { FeatureSupport , Thread , THREAD_RELATION_TYPE } from "../../src/models/thread" ;
2233
@@ -133,6 +144,44 @@ const THREAD_REPLY = utils.mkEvent({
133144
134145THREAD_ROOT . unsigned [ "m.relations" ] [ "io.element.thread" ] . latest_event = THREAD_REPLY ;
135146
147+ const STABLE_THREAD_ROOT = utils . mkEvent ( {
148+ room : roomId ,
149+ user : userId ,
150+ type : "m.room.message" ,
151+ content : {
152+ "body" : "thread root" ,
153+ "msgtype" : "m.text" ,
154+ } ,
155+ unsigned : {
156+ "m.relations" : {
157+ "m.thread" : {
158+ //"latest_event": undefined,
159+ "count" : 1 ,
160+ "current_user_participated" : true ,
161+ } ,
162+ } ,
163+ } ,
164+ event : false ,
165+ } ) ;
166+
167+ const STABLE_THREAD_REPLY = utils . mkEvent ( {
168+ room : roomId ,
169+ user : userId ,
170+ type : "m.room.message" ,
171+ content : {
172+ "body" : "thread reply" ,
173+ "msgtype" : "m.text" ,
174+ "m.relates_to" : {
175+ // We can't use the const here because we change server support mode for test
176+ rel_type : "m.thread" ,
177+ event_id : THREAD_ROOT . event_id ,
178+ } ,
179+ } ,
180+ event : false ,
181+ } ) ;
182+
183+ STABLE_THREAD_ROOT . unsigned [ "m.relations" ] [ "m.thread" ] . latest_event = STABLE_THREAD_REPLY ;
184+
136185const SYNC_THREAD_ROOT = withoutRoomId ( THREAD_ROOT ) ;
137186const SYNC_THREAD_REPLY = withoutRoomId ( THREAD_REPLY ) ;
138187SYNC_THREAD_ROOT . unsigned = {
@@ -925,6 +974,231 @@ describe("MatrixClient event timelines", function() {
925974 } ) ;
926975 } ) ;
927976
977+ describe ( "paginateEventTimeline for thread list timeline" , function ( ) {
978+ async function flushHttp < T > ( promise : Promise < T > ) : Promise < T > {
979+ return Promise . all ( [ promise , httpBackend . flushAllExpected ( ) ] ) . then ( ( [ result ] ) => result ) ;
980+ }
981+
982+ describe ( "with server compatibility" , function ( ) {
983+ async function testPagination ( timelineSet : EventTimelineSet , direction : Direction ) {
984+ const RANDOM_TOKEN = "7280349c7bee430f91defe2a38a0a08c" ;
985+ function respondToThreads ( ) {
986+ httpBackend . when ( "GET" , encodeUri ( "/_matrix/client/r0/rooms/$roomId/threads" , {
987+ $roomId : roomId ,
988+ } ) ) . respond ( 200 , {
989+ chunk : [ STABLE_THREAD_ROOT ] ,
990+ state : [ ] ,
991+ next_batch : RANDOM_TOKEN ,
992+ } ) ;
993+ }
994+ function respondToContext ( ) {
995+ httpBackend . when ( "GET" , encodeUri ( "/_matrix/client/r0/rooms/$roomId/context/$eventId" , {
996+ $roomId : roomId ,
997+ $eventId : STABLE_THREAD_ROOT . event_id ! ,
998+ } ) ) . respond ( 200 , {
999+ end : "" ,
1000+ start : "" ,
1001+ state : [ ] ,
1002+ events_before : [ ] ,
1003+ events_after : [ ] ,
1004+ event : STABLE_THREAD_ROOT ,
1005+ } ) ;
1006+ }
1007+
1008+ respondToContext ( ) ;
1009+ await flushHttp ( client . getEventTimeline ( timelineSet , STABLE_THREAD_ROOT . event_id ! ) ) ;
1010+ respondToThreads ( ) ;
1011+ const timeline = await flushHttp ( client . getLatestTimeline ( timelineSet ) ) ;
1012+ expect ( timeline ) . not . toBeNull ( ) ;
1013+
1014+ respondToThreads ( ) ;
1015+ const success = await flushHttp ( client . paginateEventTimeline ( timeline ! , {
1016+ backwards : direction === Direction . Backward ,
1017+ } ) ) ;
1018+ expect ( success ) . toBeTruthy ( ) ;
1019+ expect ( timeline ! . getEvents ( ) . length ) . toEqual ( 1 ) ;
1020+ expect ( timeline ! . getEvents ( ) [ 0 ] . event ) . toEqual ( STABLE_THREAD_ROOT ) ;
1021+ expect ( timeline ! . getPaginationToken ( direction ) ) . toEqual ( RANDOM_TOKEN ) ;
1022+ }
1023+
1024+ it ( "should allow you to paginate all threads backwards" , async function ( ) {
1025+ // @ts -ignore
1026+ client . clientOpts . experimentalThreadSupport = true ;
1027+ Thread . setServerSideSupport ( FeatureSupport . Experimental ) ;
1028+ Thread . setServerSideListSupport ( FeatureSupport . Stable ) ;
1029+
1030+ const room = client . getRoom ( roomId ) ;
1031+ const timelineSets = await ( room ?. createThreadsTimelineSets ( ) ) ;
1032+ expect ( timelineSets ) . not . toBeNull ( ) ;
1033+ const [ allThreads , myThreads ] = timelineSets ! ;
1034+ await testPagination ( allThreads , Direction . Backward ) ;
1035+ await testPagination ( myThreads , Direction . Backward ) ;
1036+ } ) ;
1037+
1038+ it ( "should allow you to paginate all threads forwards" , async function ( ) {
1039+ // @ts -ignore
1040+ client . clientOpts . experimentalThreadSupport = true ;
1041+ Thread . setServerSideSupport ( FeatureSupport . Experimental ) ;
1042+ Thread . setServerSideListSupport ( FeatureSupport . Stable ) ;
1043+
1044+ const room = client . getRoom ( roomId ) ;
1045+ const timelineSets = await ( room ?. createThreadsTimelineSets ( ) ) ;
1046+ expect ( timelineSets ) . not . toBeNull ( ) ;
1047+ const [ allThreads , myThreads ] = timelineSets ! ;
1048+
1049+ await testPagination ( allThreads , Direction . Forward ) ;
1050+ await testPagination ( myThreads , Direction . Forward ) ;
1051+ } ) ;
1052+
1053+ it ( "should allow fetching all threads" , async function ( ) {
1054+ // @ts -ignore
1055+ client . clientOpts . experimentalThreadSupport = true ;
1056+ Thread . setServerSideSupport ( FeatureSupport . Experimental ) ;
1057+ Thread . setServerSideListSupport ( FeatureSupport . Stable ) ;
1058+
1059+ const RANDOM_TOKEN = "7280349c7bee430f91defe2a38a0a08c" ;
1060+ function respondToThreads ( ) {
1061+ httpBackend . when ( "GET" , encodeUri ( "/_matrix/client/r0/rooms/$roomId/threads" , {
1062+ $roomId : roomId ,
1063+ } ) ) . respond ( 200 , {
1064+ chunk : [ STABLE_THREAD_ROOT ] ,
1065+ state : [ ] ,
1066+ next_batch : RANDOM_TOKEN ,
1067+ } ) ;
1068+ }
1069+ const room = client . getRoom ( roomId ) ;
1070+ const timelineSets = await room ?. createThreadsTimelineSets ( ) ;
1071+ expect ( timelineSets ) . not . toBeNull ( ) ;
1072+ respondToThreads ( ) ;
1073+ respondToThreads ( ) ;
1074+ httpBackend . when ( "GET" , "/sync" ) . respond ( 200 , INITIAL_SYNC_DATA ) ;
1075+ await flushHttp ( room . fetchRoomThreads ( ) ) ;
1076+ } ) ;
1077+ } ) ;
1078+
1079+ describe ( "without server compatibility" , function ( ) {
1080+ async function testPagination ( timelineSet : EventTimelineSet , direction : Direction ) {
1081+ const RANDOM_TOKEN = "7280349c7bee430f91defe2a38a0a08c" ;
1082+ function respondToMessagesRequest ( ) {
1083+ httpBackend . when ( "GET" , encodeUri ( "/_matrix/client/r0/rooms/$roomId/messages" , {
1084+ $roomId : roomId ,
1085+ } ) ) . respond ( 200 , {
1086+ chunk : [ THREAD_ROOT ] ,
1087+ state : [ ] ,
1088+ start : `${ Direction . Forward } ${ RANDOM_TOKEN } 2` ,
1089+ end : `${ Direction . Backward } ${ RANDOM_TOKEN } 2` ,
1090+ } ) ;
1091+ }
1092+ function respondToContext ( ) {
1093+ httpBackend . when ( "GET" , encodeUri ( "/_matrix/client/r0/rooms/$roomId/context/$eventId" , {
1094+ $roomId : roomId ,
1095+ $eventId : THREAD_ROOT . event_id ! ,
1096+ } ) ) . respond ( 200 , {
1097+ end : `${ Direction . Forward } ${ RANDOM_TOKEN } 1` ,
1098+ start : `${ Direction . Backward } ${ RANDOM_TOKEN } 1` ,
1099+ state : [ ] ,
1100+ events_before : [ ] ,
1101+ events_after : [ ] ,
1102+ event : THREAD_ROOT ,
1103+ } ) ;
1104+ }
1105+ function respondToSync ( ) {
1106+ httpBackend . when ( "GET" , "/sync" ) . respond ( 200 , INITIAL_SYNC_DATA ) ;
1107+ }
1108+
1109+ respondToContext ( ) ;
1110+ respondToSync ( ) ;
1111+ await flushHttp ( client . getEventTimeline ( timelineSet , THREAD_ROOT . event_id ! ) ) ;
1112+
1113+ respondToMessagesRequest ( ) ;
1114+ const timeline = await flushHttp ( client . getLatestTimeline ( timelineSet ) ) ;
1115+ expect ( timeline ) . not . toBeNull ( ) ;
1116+
1117+ respondToMessagesRequest ( ) ;
1118+ const success = await flushHttp ( client . paginateEventTimeline ( timeline ! , {
1119+ backwards : direction === Direction . Backward ,
1120+ } ) ) ;
1121+
1122+ expect ( success ) . toBeTruthy ( ) ;
1123+ expect ( timeline ! . getEvents ( ) . length ) . toEqual ( 1 ) ;
1124+ expect ( timeline ! . getEvents ( ) [ 0 ] . event ) . toEqual ( THREAD_ROOT ) ;
1125+ expect ( timeline ! . getPaginationToken ( direction ) ) . toEqual ( `${ direction } ${ RANDOM_TOKEN } 2` ) ;
1126+ }
1127+
1128+ it ( "should allow you to paginate all threads" , async function ( ) {
1129+ // @ts -ignore
1130+ client . clientOpts . experimentalThreadSupport = true ;
1131+ Thread . setServerSideSupport ( FeatureSupport . Experimental ) ;
1132+ Thread . setServerSideListSupport ( FeatureSupport . None ) ;
1133+
1134+ function respondToFilter ( ) {
1135+ httpBackend . when ( "POST" , "/filter" ) . respond ( 200 , { filter_id : "fid" } ) ;
1136+ }
1137+ function respondToSync ( ) {
1138+ httpBackend . when ( "GET" , "/sync" ) . respond ( 200 , INITIAL_SYNC_DATA ) ;
1139+ }
1140+
1141+ const room = client . getRoom ( roomId ) ;
1142+
1143+ respondToFilter ( ) ;
1144+ respondToSync ( ) ;
1145+ respondToFilter ( ) ;
1146+ respondToSync ( ) ;
1147+
1148+ const timelineSetsPromise = room ?. createThreadsTimelineSets ( ) ;
1149+ expect ( timelineSetsPromise ) . not . toBeNull ( ) ;
1150+ const timelineSets = await flushHttp ( timelineSetsPromise ! ) ;
1151+ expect ( timelineSets ) . not . toBeNull ( ) ;
1152+ const [ allThreads , myThreads ] = timelineSets ! ;
1153+
1154+ await testPagination ( allThreads , Direction . Backward ) ;
1155+ await testPagination ( myThreads , Direction . Backward ) ;
1156+ } ) ;
1157+
1158+ it ( "should allow fetching all threads" , async function ( ) {
1159+ // @ts -ignore
1160+ client . clientOpts . experimentalThreadSupport = true ;
1161+ Thread . setServerSideSupport ( FeatureSupport . Experimental ) ;
1162+ Thread . setServerSideListSupport ( FeatureSupport . None ) ;
1163+
1164+ const room = client . getRoom ( roomId ) ;
1165+
1166+ const RANDOM_TOKEN = "7280349c7bee430f91defe2a38a0a08c" ;
1167+ function respondToMessagesRequest ( ) {
1168+ httpBackend . when ( "GET" , encodeUri ( "/_matrix/client/r0/rooms/$roomId/messages" , {
1169+ $roomId : roomId ,
1170+ } ) ) . respond ( 200 , {
1171+ chunk : [ STABLE_THREAD_ROOT ] ,
1172+ state : [ ] ,
1173+ start : `${ Direction . Forward } ${ RANDOM_TOKEN } 2` ,
1174+ end : `${ Direction . Backward } ${ RANDOM_TOKEN } 2` ,
1175+ } ) ;
1176+ }
1177+ function respondToFilter ( ) {
1178+ httpBackend . when ( "POST" , "/filter" ) . respond ( 200 , { filter_id : "fid" } ) ;
1179+ }
1180+ function respondToSync ( ) {
1181+ httpBackend . when ( "GET" , "/sync" ) . respond ( 200 , INITIAL_SYNC_DATA ) ;
1182+ }
1183+
1184+ respondToFilter ( ) ;
1185+ respondToSync ( ) ;
1186+ respondToFilter ( ) ;
1187+ respondToSync ( ) ;
1188+
1189+ const timelineSetsPromise = room ?. createThreadsTimelineSets ( ) ;
1190+ expect ( timelineSetsPromise ) . not . toBeNull ( ) ;
1191+ await flushHttp ( timelineSetsPromise ! ) ;
1192+ respondToFilter ( ) ;
1193+ respondToSync ( ) ;
1194+ respondToSync ( ) ;
1195+ respondToSync ( ) ;
1196+ respondToMessagesRequest ( ) ;
1197+ await flushHttp ( room . fetchRoomThreads ( ) ) ;
1198+ } ) ;
1199+ } ) ;
1200+ } ) ;
1201+
9281202 describe ( "event timeline for sent events" , function ( ) {
9291203 const TXN_ID = "txn1" ;
9301204 const event = utils . mkMessage ( {
0 commit comments