@@ -573,6 +573,13 @@ interface IMessagesResponse {
573
573
state : IStateEvent [ ] ;
574
574
}
575
575
576
+ interface IThreadedMessagesResponse {
577
+ prev_batch : string ;
578
+ next_batch : string ;
579
+ chunk : IRoomEvent [ ] ;
580
+ state : IStateEvent [ ] ;
581
+ }
582
+
576
583
export interface IRequestTokenResponse {
577
584
sid : string ;
578
585
submit_url ?: string ;
@@ -1194,12 +1201,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
1194
1201
}
1195
1202
1196
1203
try {
1197
- const { serverSupport, stable } = await this . doesServerSupportThread ( ) ;
1198
- Thread . setServerSideSupport ( serverSupport , stable ) ;
1204
+ const { serverSupport, stable, listThreads } = await this . doesServerSupportThread ( ) ;
1205
+ Thread . setServerSideSupport ( serverSupport , stable , listThreads ) ;
1199
1206
} catch ( e ) {
1200
1207
// Most likely cause is that `doesServerSupportThread` returned `null` (as it
1201
1208
// is allowed to do) and thus we enter "degraded mode" on threads.
1202
- Thread . setServerSideSupport ( false , true ) ;
1209
+ Thread . setServerSideSupport ( false , true , false ) ;
1203
1210
}
1204
1211
1205
1212
// shallow-copy the opts dict before modifying and storing it
@@ -5496,6 +5503,63 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
5496
5503
return this . http . authedRequest ( undefined , Method . Get , path , params ) ;
5497
5504
}
5498
5505
5506
+ /**
5507
+ * Makes a request to /messages with the appropriate lazy loading filter set.
5508
+ * XXX: if we do get rid of scrollback (as it's not used at the moment),
5509
+ * we could inline this method again in paginateEventTimeline as that would
5510
+ * then be the only call-site
5511
+ * @param {string } roomId
5512
+ * @param {string } fromToken
5513
+ * @param {number } limit the maximum amount of events the retrieve
5514
+ * @param {string } dir 'f' or 'b'
5515
+ * @param {Filter } timelineFilter the timeline filter to pass
5516
+ * @return {Promise }
5517
+ */
5518
+ // XXX: Intended private, used by room.fetchRoomThreads
5519
+ public createThreadMessagesRequest (
5520
+ roomId : string ,
5521
+ fromToken : string | null ,
5522
+ limit = 30 ,
5523
+ dir : Direction ,
5524
+ timelineFilter ?: Filter ,
5525
+ ) : Promise < IMessagesResponse > {
5526
+ const path = utils . encodeUri ( "/rooms/$roomId/threads" , { $roomId : roomId } ) ;
5527
+
5528
+ const params : Record < string , string > = {
5529
+ limit : limit . toString ( ) ,
5530
+ dir : dir ,
5531
+ include : 'all' ,
5532
+ } ;
5533
+
5534
+ if ( fromToken ) {
5535
+ params . from = fromToken ;
5536
+ }
5537
+
5538
+ let filter = null ;
5539
+ if ( this . clientOpts . lazyLoadMembers ) {
5540
+ // create a shallow copy of LAZY_LOADING_MESSAGES_FILTER,
5541
+ // so the timelineFilter doesn't get written into it below
5542
+ filter = Object . assign ( { } , Filter . LAZY_LOADING_MESSAGES_FILTER ) ;
5543
+ }
5544
+ if ( timelineFilter ) {
5545
+ // XXX: it's horrific that /messages' filter parameter doesn't match
5546
+ // /sync's one - see https://matrix.org/jira/browse/SPEC-451
5547
+ filter = filter || { } ;
5548
+ Object . assign ( filter , timelineFilter . getRoomTimelineFilterComponent ( ) ?. toJSON ( ) ) ;
5549
+ }
5550
+ if ( filter ) {
5551
+ params . filter = JSON . stringify ( filter ) ;
5552
+ }
5553
+
5554
+ return this . http . authedRequest < IThreadedMessagesResponse > ( undefined , Method . Get , path , params , undefined , {
5555
+ prefix : "/_matrix/client/unstable/org.matrix.msc3856" ,
5556
+ } ) . then ( res => ( {
5557
+ ...res ,
5558
+ start : res . prev_batch ,
5559
+ end : res . next_batch ,
5560
+ } ) ) ;
5561
+ }
5562
+
5499
5563
/**
5500
5564
* Take an EventTimeline, and back/forward-fill results.
5501
5565
*
@@ -5511,6 +5575,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
5511
5575
*/
5512
5576
public paginateEventTimeline ( eventTimeline : EventTimeline , opts : IPaginateOpts ) : Promise < boolean > {
5513
5577
const isNotifTimeline = ( eventTimeline . getTimelineSet ( ) === this . notifTimelineSet ) ;
5578
+ const room = this . getRoom ( eventTimeline . getRoomId ( ) ) ;
5579
+ const isThreadTimeline = eventTimeline . getTimelineSet ( ) . isThreadTimeline ;
5514
5580
5515
5581
// TODO: we should implement a backoff (as per scrollback()) to deal more
5516
5582
// nicely with HTTP errors.
@@ -5581,8 +5647,43 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
5581
5647
eventTimeline . paginationRequests [ dir ] = null ;
5582
5648
} ) ;
5583
5649
eventTimeline . paginationRequests [ dir ] = promise ;
5650
+ } else if ( isThreadTimeline ) {
5651
+ if ( ! room ) {
5652
+ throw new Error ( "Unknown room " + eventTimeline . getRoomId ( ) ) ;
5653
+ }
5654
+
5655
+ promise = this . createThreadMessagesRequest (
5656
+ eventTimeline . getRoomId ( ) ,
5657
+ token ,
5658
+ opts . limit ,
5659
+ dir ,
5660
+ eventTimeline . getFilter ( ) ,
5661
+ ) . then ( ( res ) => {
5662
+ if ( res . state ) {
5663
+ const roomState = eventTimeline . getState ( dir ) ;
5664
+ const stateEvents = res . state . map ( this . getEventMapper ( ) ) ;
5665
+ roomState . setUnknownStateEvents ( stateEvents ) ;
5666
+ }
5667
+ const token = res . end ;
5668
+ const matrixEvents = res . chunk . map ( this . getEventMapper ( ) ) ;
5669
+
5670
+ const timelineSet = eventTimeline . getTimelineSet ( ) ;
5671
+ timelineSet . addEventsToTimeline ( matrixEvents , backwards , eventTimeline , token ) ;
5672
+ this . processBeaconEvents ( timelineSet . room , matrixEvents ) ;
5673
+ this . processThreadRoots ( timelineSet . room , matrixEvents , backwards ) ;
5674
+
5675
+ // if we've hit the end of the timeline, we need to stop trying to
5676
+ // paginate. We need to keep the 'forwards' token though, to make sure
5677
+ // we can recover from gappy syncs.
5678
+ if ( backwards && res . end == res . start ) {
5679
+ eventTimeline . setPaginationToken ( null , dir ) ;
5680
+ }
5681
+ return res . end != res . start ;
5682
+ } ) . finally ( ( ) => {
5683
+ eventTimeline . paginationRequests [ dir ] = null ;
5684
+ } ) ;
5685
+ eventTimeline . paginationRequests [ dir ] = promise ;
5584
5686
} else {
5585
- const room = this . getRoom ( eventTimeline . getRoomId ( ) ) ;
5586
5687
if ( ! room ) {
5587
5688
throw new Error ( "Unknown room " + eventTimeline . getRoomId ( ) ) ;
5588
5689
}
@@ -6665,6 +6766,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
6665
6766
* @return {Promise<boolean> } true if the feature is supported
6666
6767
*/
6667
6768
public async doesServerSupportUnstableFeature ( feature : string ) : Promise < boolean > {
6769
+ // FIXME: WORKAROUND FOR NOW
6770
+ if ( feature === "org.matrix.msc3856" ) {
6771
+ return this . http . opts . baseUrl === "https://threads-dev.lab.element.dev" ;
6772
+ }
6668
6773
const response = await this . getVersions ( ) ;
6669
6774
if ( ! response ) return false ;
6670
6775
const unstableFeatures = response [ "unstable_features" ] ;
@@ -6694,16 +6799,21 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
6694
6799
public async doesServerSupportThread ( ) : Promise < {
6695
6800
serverSupport : boolean ;
6696
6801
stable : boolean ;
6802
+ listThreads : boolean ;
6697
6803
} | null > {
6698
6804
try {
6699
- const hasUnstableSupport = await this . doesServerSupportUnstableFeature ( "org.matrix.msc3440" ) ;
6700
- const hasStableSupport = await this . doesServerSupportUnstableFeature ( "org.matrix.msc3440.stable" ) ;
6805
+ const [ hasUnstableSupport , hasStableSupport , hasListThreadsSupport ] = await Promise . all ( [
6806
+ this . doesServerSupportUnstableFeature ( "org.matrix.msc3440" ) ,
6807
+ this . doesServerSupportUnstableFeature ( "org.matrix.msc3440.stable" ) ,
6808
+ this . doesServerSupportUnstableFeature ( "org.matrix.msc3856" ) ,
6809
+ ] ) ;
6701
6810
6702
6811
// TODO: Use `this.isVersionSupported("v1.3")` for whatever spec version includes MSC3440 formally.
6703
6812
6704
6813
return {
6705
6814
serverSupport : hasUnstableSupport || hasStableSupport ,
6706
6815
stable : hasStableSupport ,
6816
+ listThreads : hasListThreadsSupport ,
6707
6817
} ;
6708
6818
} catch ( e ) {
6709
6819
// Assume server support and stability aren't available: null/no data return.
@@ -9086,6 +9196,13 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
9086
9196
room . processThreadedEvents ( threadedEvents , toStartOfTimeline ) ;
9087
9197
}
9088
9198
9199
+ /**
9200
+ * @experimental
9201
+ */
9202
+ public processThreadRoots ( room : Room , threadedEvents : MatrixEvent [ ] , toStartOfTimeline : boolean ) : void {
9203
+ room . processThreadRoots ( threadedEvents , toStartOfTimeline ) ;
9204
+ }
9205
+
9089
9206
public processBeaconEvents (
9090
9207
room ?: Room ,
9091
9208
events ?: MatrixEvent [ ] ,
0 commit comments