@@ -359,7 +359,7 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
359359 }
360360
361361 private threadTimelineSetsPromise : Promise < [ EventTimelineSet , EventTimelineSet ] > | null = null ;
362- public async createThreadsTimelineSets ( ) : Promise < [ EventTimelineSet , EventTimelineSet ] > {
362+ public async createThreadsTimelineSets ( ) : Promise < [ EventTimelineSet , EventTimelineSet ] | null > {
363363 if ( this . threadTimelineSetsPromise ) {
364364 return this . threadTimelineSetsPromise ;
365365 }
@@ -372,10 +372,13 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
372372 ] ) ;
373373 const timelineSets = await this . threadTimelineSetsPromise ;
374374 this . threadsTimelineSets . push ( ...timelineSets ) ;
375+ return timelineSets ;
375376 } catch ( e ) {
376377 this . threadTimelineSetsPromise = null ;
378+ return null ;
377379 }
378380 }
381+ return null ;
379382 }
380383
381384 /**
@@ -1612,7 +1615,14 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
16121615
16131616 private async createThreadTimelineSet ( filterType ?: ThreadFilterType ) : Promise < EventTimelineSet > {
16141617 let timelineSet : EventTimelineSet ;
1615- if ( Thread . hasServerSideSupport ) {
1618+ if ( Thread . hasServerSideListSupport ) {
1619+ timelineSet =
1620+ new EventTimelineSet ( this , this . opts , undefined , undefined , Boolean ( Thread . hasServerSideListSupport ) ) ;
1621+ this . reEmitter . reEmit ( timelineSet , [
1622+ RoomEvent . Timeline ,
1623+ RoomEvent . TimelineReset ,
1624+ ] ) ;
1625+ } else if ( Thread . hasServerSideSupport ) {
16161626 const filter = await this . getThreadListFilter ( filterType ) ;
16171627
16181628 timelineSet = this . getOrCreateFilteredTimelineSet (
@@ -1645,81 +1655,148 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
16451655 return timelineSet ;
16461656 }
16471657
1648- public threadsReady = false ;
1658+ private threadsReady = false ;
1659+
1660+ /**
1661+ * Takes the given thread root events and creates threads for them.
1662+ * @param events
1663+ * @param toStartOfTimeline
1664+ */
1665+ public processThreadRoots ( events : MatrixEvent [ ] , toStartOfTimeline : boolean ) : void {
1666+ for ( const rootEvent of events ) {
1667+ EventTimeline . setEventMetadata (
1668+ rootEvent ,
1669+ this . currentState ,
1670+ toStartOfTimeline ,
1671+ ) ;
1672+ if ( ! this . getThread ( rootEvent . getId ( ) ) ) {
1673+ this . createThread ( rootEvent . getId ( ) , rootEvent , [ ] , toStartOfTimeline ) ;
1674+ }
1675+ }
1676+ }
16491677
1678+ /**
1679+ * Fetch the bare minimum of room threads required for the thread list to work reliably.
1680+ * With server support that means fetching one page.
1681+ * Without server support that means fetching as much at once as the server allows us to.
1682+ */
16501683 public async fetchRoomThreads ( ) : Promise < void > {
16511684 if ( this . threadsReady || ! this . client . supportsExperimentalThreads ( ) ) {
16521685 return ;
16531686 }
16541687
1655- const allThreadsFilter = await this . getThreadListFilter ( ) ;
1656-
1657- const { chunk : events } = await this . client . createMessagesRequest (
1658- this . roomId ,
1659- "" ,
1660- Number . MAX_SAFE_INTEGER ,
1661- Direction . Backward ,
1662- allThreadsFilter ,
1663- ) ;
1664-
1665- if ( ! events . length ) return ;
1666-
1667- // Sorted by last_reply origin_server_ts
1668- const threadRoots = events
1669- . map ( this . client . getEventMapper ( ) )
1670- . sort ( ( eventA , eventB ) => {
1671- /**
1672- * `origin_server_ts` in a decentralised world is far from ideal
1673- * but for lack of any better, we will have to use this
1674- * Long term the sorting should be handled by homeservers and this
1675- * is only meant as a short term patch
1676- */
1677- const threadAMetadata = eventA
1678- . getServerAggregatedRelation < IThreadBundledRelationship > ( RelationType . Thread ) ;
1679- const threadBMetadata = eventB
1680- . getServerAggregatedRelation < IThreadBundledRelationship > ( RelationType . Thread ) ;
1681- return threadAMetadata . latest_event . origin_server_ts - threadBMetadata . latest_event . origin_server_ts ;
1682- } ) ;
1688+ if ( Thread . hasServerSideListSupport ) {
1689+ await Promise . all ( [
1690+ this . fetchRoomThreadList ( ThreadFilterType . All ) ,
1691+ this . fetchRoomThreadList ( ThreadFilterType . My ) ,
1692+ ] ) ;
1693+ } else {
1694+ const allThreadsFilter = await this . getThreadListFilter ( ) ;
1695+
1696+ const { chunk : events } = await this . client . createMessagesRequest (
1697+ this . roomId ,
1698+ "" ,
1699+ Number . MAX_SAFE_INTEGER ,
1700+ Direction . Backward ,
1701+ allThreadsFilter ,
1702+ ) ;
16831703
1684- let latestMyThreadsRootEvent : MatrixEvent ;
1685- const roomState = this . getLiveTimeline ( ) . getState ( EventTimeline . FORWARDS ) ;
1686- for ( const rootEvent of threadRoots ) {
1687- this . threadsTimelineSets [ 0 ] . addLiveEvent ( rootEvent , {
1688- duplicateStrategy : DuplicateStrategy . Ignore ,
1689- fromCache : false ,
1690- roomState,
1691- } ) ;
1704+ if ( ! events . length ) return ;
1705+
1706+ // Sorted by last_reply origin_server_ts
1707+ const threadRoots = events
1708+ . map ( this . client . getEventMapper ( ) )
1709+ . sort ( ( eventA , eventB ) => {
1710+ /**
1711+ * `origin_server_ts` in a decentralised world is far from ideal
1712+ * but for lack of any better, we will have to use this
1713+ * Long term the sorting should be handled by homeservers and this
1714+ * is only meant as a short term patch
1715+ */
1716+ const threadAMetadata = eventA
1717+ . getServerAggregatedRelation < IThreadBundledRelationship > ( THREAD_RELATION_TYPE . name ) ;
1718+ const threadBMetadata = eventB
1719+ . getServerAggregatedRelation < IThreadBundledRelationship > ( THREAD_RELATION_TYPE . name ) ;
1720+ return threadAMetadata . latest_event . origin_server_ts -
1721+ threadBMetadata . latest_event . origin_server_ts ;
1722+ } ) ;
16921723
1693- const threadRelationship = rootEvent
1694- . getServerAggregatedRelation < IThreadBundledRelationship > ( RelationType . Thread ) ;
1695- if ( threadRelationship . current_user_participated ) {
1696- this . threadsTimelineSets [ 1 ] . addLiveEvent ( rootEvent , {
1724+ let latestMyThreadsRootEvent : MatrixEvent ;
1725+ const roomState = this . getLiveTimeline ( ) . getState ( EventTimeline . FORWARDS ) ;
1726+ for ( const rootEvent of threadRoots ) {
1727+ this . threadsTimelineSets [ 0 ] . addLiveEvent ( rootEvent , {
16971728 duplicateStrategy : DuplicateStrategy . Ignore ,
16981729 fromCache : false ,
16991730 roomState,
17001731 } ) ;
1701- latestMyThreadsRootEvent = rootEvent ;
1702- }
17031732
1704- if ( ! this . getThread ( rootEvent . getId ( ) ) ) {
1705- this . createThread ( rootEvent . getId ( ) , rootEvent , [ ] , true ) ;
1733+ const threadRelationship = rootEvent
1734+ . getServerAggregatedRelation < IThreadBundledRelationship > ( THREAD_RELATION_TYPE . name ) ;
1735+ if ( threadRelationship . current_user_participated ) {
1736+ this . threadsTimelineSets [ 1 ] . addLiveEvent ( rootEvent , {
1737+ duplicateStrategy : DuplicateStrategy . Ignore ,
1738+ fromCache : false ,
1739+ roomState,
1740+ } ) ;
1741+ latestMyThreadsRootEvent = rootEvent ;
1742+ }
17061743 }
1707- }
17081744
1709- this . client . decryptEventIfNeeded ( threadRoots [ threadRoots . length - 1 ] ) ;
1710- if ( latestMyThreadsRootEvent ) {
1711- this . client . decryptEventIfNeeded ( latestMyThreadsRootEvent ) ;
1745+ this . processThreadRoots ( threadRoots , true ) ;
1746+
1747+ this . client . decryptEventIfNeeded ( threadRoots [ threadRoots . length - 1 ] ) ;
1748+ if ( latestMyThreadsRootEvent ) {
1749+ this . client . decryptEventIfNeeded ( latestMyThreadsRootEvent ) ;
1750+ }
17121751 }
17131752
1753+ this . on ( ThreadEvent . NewReply , this . onThreadNewReply ) ;
17141754 this . threadsReady = true ;
1755+ }
17151756
1716- this . on ( ThreadEvent . NewReply , this . onThreadNewReply ) ;
1757+ /**
1758+ * Fetch a single page of threadlist messages for the specific thread filter
1759+ * @param filter
1760+ * @private
1761+ */
1762+ private async fetchRoomThreadList ( filter ?: ThreadFilterType ) : Promise < void > {
1763+ const timelineSet = filter === ThreadFilterType . My
1764+ ? this . threadsTimelineSets [ 1 ]
1765+ : this . threadsTimelineSets [ 0 ] ;
1766+
1767+ const { chunk : events , end } = await this . client . createThreadListMessagesRequest (
1768+ this . roomId ,
1769+ null ,
1770+ undefined ,
1771+ Direction . Backward ,
1772+ timelineSet . getFilter ( ) ,
1773+ ) ;
1774+
1775+ timelineSet . getLiveTimeline ( ) . setPaginationToken ( end , Direction . Backward ) ;
1776+
1777+ if ( ! events . length ) return ;
1778+
1779+ const matrixEvents = events . map ( this . client . getEventMapper ( ) ) ;
1780+ this . processThreadRoots ( matrixEvents , true ) ;
1781+ const roomState = this . getLiveTimeline ( ) . getState ( EventTimeline . FORWARDS ) ;
1782+ for ( const rootEvent of matrixEvents ) {
1783+ timelineSet . addLiveEvent ( rootEvent , {
1784+ duplicateStrategy : DuplicateStrategy . Replace ,
1785+ fromCache : false ,
1786+ roomState,
1787+ } ) ;
1788+ }
17171789 }
17181790
17191791 private onThreadNewReply ( thread : Thread ) : void {
1792+ const roomState = this . getLiveTimeline ( ) . getState ( EventTimeline . FORWARDS ) ;
17201793 for ( const timelineSet of this . threadsTimelineSets ) {
17211794 timelineSet . removeEvent ( thread . id ) ;
1722- timelineSet . addLiveEvent ( thread . rootEvent ) ;
1795+ timelineSet . addLiveEvent ( thread . rootEvent , {
1796+ duplicateStrategy : DuplicateStrategy . Replace ,
1797+ fromCache : false ,
1798+ roomState,
1799+ } ) ;
17231800 }
17241801 }
17251802
@@ -1865,8 +1942,6 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
18651942 this . lastThread = thread ;
18661943 }
18671944
1868- this . emit ( ThreadEvent . New , thread , toStartOfTimeline ) ;
1869-
18701945 if ( this . threadsReady ) {
18711946 this . threadsTimelineSets . forEach ( timelineSet => {
18721947 if ( thread . rootEvent ) {
@@ -1883,6 +1958,8 @@ export class Room extends ReadReceipt<EmittedEvents, RoomEventHandlerMap> {
18831958 } ) ;
18841959 }
18851960
1961+ this . emit ( ThreadEvent . New , thread , toStartOfTimeline ) ;
1962+
18861963 return thread ;
18871964 }
18881965
0 commit comments