@@ -573,6 +573,13 @@ interface IMessagesResponse {
573573 state : IStateEvent [ ] ;
574574}
575575
576+ interface IThreadedMessagesResponse {
577+ prev_batch : string ;
578+ next_batch : string ;
579+ chunk : IRoomEvent [ ] ;
580+ state : IStateEvent [ ] ;
581+ }
582+
576583export interface IRequestTokenResponse {
577584 sid : string ;
578585 submit_url ?: string ;
@@ -1194,12 +1201,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
11941201 }
11951202
11961203 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 ) ;
11991206 } catch ( e ) {
12001207 // Most likely cause is that `doesServerSupportThread` returned `null` (as it
12011208 // is allowed to do) and thus we enter "degraded mode" on threads.
1202- Thread . setServerSideSupport ( false , true ) ;
1209+ Thread . setServerSideSupport ( false , true , false ) ;
12031210 }
12041211
12051212 // shallow-copy the opts dict before modifying and storing it
@@ -5496,6 +5503,63 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
54965503 return this . http . authedRequest ( undefined , Method . Get , path , params ) ;
54975504 }
54985505
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+
54995563 /**
55005564 * Take an EventTimeline, and back/forward-fill results.
55015565 *
@@ -5511,6 +5575,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
55115575 */
55125576 public paginateEventTimeline ( eventTimeline : EventTimeline , opts : IPaginateOpts ) : Promise < boolean > {
55135577 const isNotifTimeline = ( eventTimeline . getTimelineSet ( ) === this . notifTimelineSet ) ;
5578+ const room = this . getRoom ( eventTimeline . getRoomId ( ) ) ;
5579+ const isThreadTimeline = eventTimeline . getTimelineSet ( ) . isThreadTimeline ;
55145580
55155581 // TODO: we should implement a backoff (as per scrollback()) to deal more
55165582 // nicely with HTTP errors.
@@ -5581,8 +5647,43 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
55815647 eventTimeline . paginationRequests [ dir ] = null ;
55825648 } ) ;
55835649 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 ;
55845686 } else {
5585- const room = this . getRoom ( eventTimeline . getRoomId ( ) ) ;
55865687 if ( ! room ) {
55875688 throw new Error ( "Unknown room " + eventTimeline . getRoomId ( ) ) ;
55885689 }
@@ -6665,6 +6766,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
66656766 * @return {Promise<boolean> } true if the feature is supported
66666767 */
66676768 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+ }
66686773 const response = await this . getVersions ( ) ;
66696774 if ( ! response ) return false ;
66706775 const unstableFeatures = response [ "unstable_features" ] ;
@@ -6694,16 +6799,21 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
66946799 public async doesServerSupportThread ( ) : Promise < {
66956800 serverSupport : boolean ;
66966801 stable : boolean ;
6802+ listThreads : boolean ;
66976803 } | null > {
66986804 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+ ] ) ;
67016810
67026811 // TODO: Use `this.isVersionSupported("v1.3")` for whatever spec version includes MSC3440 formally.
67036812
67046813 return {
67056814 serverSupport : hasUnstableSupport || hasStableSupport ,
67066815 stable : hasStableSupport ,
6816+ listThreads : hasListThreadsSupport ,
67076817 } ;
67086818 } catch ( e ) {
67096819 // Assume server support and stability aren't available: null/no data return.
@@ -9086,6 +9196,13 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
90869196 room . processThreadedEvents ( threadedEvents , toStartOfTimeline ) ;
90879197 }
90889198
9199+ /**
9200+ * @experimental
9201+ */
9202+ public processThreadRoots ( room : Room , threadedEvents : MatrixEvent [ ] , toStartOfTimeline : boolean ) : void {
9203+ room . processThreadRoots ( threadedEvents , toStartOfTimeline ) ;
9204+ }
9205+
90899206 public processBeaconEvents (
90909207 room ?: Room ,
90919208 events ?: MatrixEvent [ ] ,
0 commit comments