@@ -5400,65 +5400,116 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
54005400 * Get an EventTimeline for the latest events in the room. This will just
54015401 * call `/messages` to get the latest message in the room, then use
54025402 * `client.getEventTimeline(...)` to construct a new timeline from it.
5403+ * Always returns timeline in the given `timelineSet`.
54035404 *
54045405 * @param {EventTimelineSet } timelineSet The timelineSet to find or add the timeline to
54055406 *
54065407 * @return {Promise } Resolves:
54075408 * {@link module:models/event-timeline~EventTimeline} timeline with the latest events in the room
54085409 */
5409- public async getLatestTimeline ( timelineSet : EventTimelineSet ) : Promise < Optional < EventTimeline > > {
5410+ public async getLatestLiveTimeline ( timelineSet : EventTimelineSet ) : Promise < EventTimeline > {
54105411 // don't allow any timeline support unless it's been enabled.
54115412 if ( ! this . timelineSupport ) {
54125413 throw new Error ( "timeline support is disabled. Set the 'timelineSupport'" +
54135414 " parameter to true when creating MatrixClient to enable it." ) ;
54145415 }
54155416
54165417 if ( ! timelineSet . room ) {
5417- throw new Error ( "getLatestTimeline only supports room timelines" ) ;
5418+ throw new Error ( "getLatestLiveTimeline only supports room timelines" ) ;
54185419 }
54195420
5420- let event ;
5421- if ( timelineSet . threadListType !== null ) {
5422- const res = await this . createThreadListMessagesRequest (
5423- timelineSet . room . roomId ,
5424- null ,
5425- 1 ,
5426- Direction . Backward ,
5427- timelineSet . threadListType ,
5428- timelineSet . getFilter ( ) ,
5429- ) ;
5430- event = res . chunk ?. [ 0 ] ;
5431- } else if ( timelineSet . thread && Thread . hasServerSideSupport ) {
5432- const res = await this . fetchRelations (
5433- timelineSet . room . roomId ,
5434- timelineSet . thread . id ,
5435- THREAD_RELATION_TYPE . name ,
5436- null ,
5437- { dir : Direction . Backward , limit : 1 } ,
5438- ) ;
5439- event = res . chunk ?. [ 0 ] ;
5440- } else {
5441- const messagesPath = utils . encodeUri (
5442- "/rooms/$roomId/messages" , {
5443- $roomId : timelineSet . room . roomId ,
5444- } ,
5445- ) ;
5421+ if ( timelineSet . threadListType !== null || timelineSet . thread && Thread . hasServerSideSupport ) {
5422+ throw new Error ( "getLatestLiveTimeline only supports live timelines" ) ;
5423+ }
54465424
5447- const params : Record < string , string | string [ ] > = {
5448- dir : 'b' ,
5449- } ;
5450- if ( this . clientOpts ?. lazyLoadMembers ) {
5451- params . filter = JSON . stringify ( Filter . LAZY_LOADING_MESSAGES_FILTER ) ;
5452- }
5425+ const messagesPath = utils . encodeUri (
5426+ "/rooms/$roomId/messages" , {
5427+ $roomId : timelineSet . room . roomId ,
5428+ } ,
5429+ ) ;
5430+ const messageRequestParams : Record < string , string | string [ ] > = {
5431+ dir : 'b' ,
5432+ // Since we only use the latest message in the response, we only need to
5433+ // fetch the one message here.
5434+ limit : "1" ,
5435+ } ;
5436+ if ( this . clientOpts ?. lazyLoadMembers ) {
5437+ messageRequestParams . filter = JSON . stringify ( Filter . LAZY_LOADING_MESSAGES_FILTER ) ;
5438+ }
5439+ const messagesRes = await this . http . authedRequest < IMessagesResponse > (
5440+ Method . Get ,
5441+ messagesPath ,
5442+ messageRequestParams ,
5443+ ) ;
5444+ const latestEventInTimeline = messagesRes . chunk ?. [ 0 ] ;
5445+ const latestEventIdInTimeline = latestEventInTimeline ?. event_id ;
5446+ if ( ! latestEventIdInTimeline ) {
5447+ throw new Error ( "No message returned when trying to construct getLatestLiveTimeline" ) ;
5448+ }
54535449
5454- const res = await this . http . authedRequest < IMessagesResponse > ( Method . Get , messagesPath , params ) ;
5455- event = res . chunk ?. [ 0 ] ;
5450+ const contextPath = utils . encodeUri (
5451+ "/rooms/$roomId/context/$eventId" , {
5452+ $roomId : timelineSet . room . roomId ,
5453+ $eventId : latestEventIdInTimeline ,
5454+ } ,
5455+ ) ;
5456+ let contextRequestParams : Record < string , string | string [ ] > | undefined = undefined ;
5457+ if ( this . clientOpts ?. lazyLoadMembers ) {
5458+ contextRequestParams = { filter : JSON . stringify ( Filter . LAZY_LOADING_MESSAGES_FILTER ) } ;
54565459 }
5457- if ( ! event ) {
5458- throw new Error ( "No message returned when trying to construct getLatestTimeline" ) ;
5460+ const contextRes = await this . http . authedRequest < IContextResponse > (
5461+ Method . Get ,
5462+ contextPath ,
5463+ contextRequestParams ,
5464+ ) ;
5465+ if ( ! contextRes . event || contextRes . event . event_id !== latestEventIdInTimeline ) {
5466+ throw new Error (
5467+ `getLatestLiveTimeline: \`/context\` response did not include latestEventIdInTimeline=` +
5468+ `${ latestEventIdInTimeline } which we were asking about. This is probably a bug in the ` +
5469+ `homeserver since we just saw the event with the other request above and now the server ` +
5470+ `claims it does not exist.` ,
5471+ ) ;
5472+ }
5473+
5474+ // By the time the request completes, the event might have ended up in the timeline.
5475+ const shortcutTimelineForEvent = timelineSet . getTimelineForEvent ( latestEventIdInTimeline ) ;
5476+ if ( shortcutTimelineForEvent ) {
5477+ return shortcutTimelineForEvent ;
5478+ }
5479+
5480+ const mapper = this . getEventMapper ( ) ;
5481+ const latestMatrixEventInTimeline = mapper ( contextRes . event ) ;
5482+ const events = [
5483+ // Order events from most recent to oldest (reverse-chronological).
5484+ // We start with the last event, since that's the point at which we have known state.
5485+ // events_after is already backwards; events_before is forwards.
5486+ ...contextRes . events_after . reverse ( ) . map ( mapper ) ,
5487+ latestMatrixEventInTimeline ,
5488+ ...contextRes . events_before . map ( mapper ) ,
5489+ ] ;
5490+
5491+ // This function handles non-thread timelines only, but we still process any
5492+ // thread events to populate thread summaries.
5493+ let timeline = timelineSet . getTimelineForEvent ( events [ 0 ] . getId ( ) ! ) ;
5494+ if ( timeline ) {
5495+ timeline . getState ( EventTimeline . BACKWARDS ) ! . setUnknownStateEvents ( contextRes . state . map ( mapper ) ) ;
5496+ } else {
5497+ // If the `latestEventIdInTimeline` does not belong to this `timelineSet`
5498+ // then it will be ignored and not added to the `timelineSet`. We'll instead
5499+ // just create a new blank timeline in the `timelineSet` with the proper
5500+ // pagination tokens setup to continue paginating.
5501+ timeline = timelineSet . addTimeline ( ) ;
5502+ timeline . initialiseState ( contextRes . state . map ( mapper ) ) ;
5503+ timeline . getState ( EventTimeline . FORWARDS ) ! . paginationToken = contextRes . end ;
54595504 }
54605505
5461- return this . getEventTimeline ( timelineSet , event . event_id ) ;
5506+ const [ timelineEvents , threadedEvents ] = timelineSet . room . partitionThreadedEvents ( events ) ;
5507+ timelineSet . addEventsToTimeline ( timelineEvents , true , timeline , contextRes . start ) ;
5508+ // The target event is not in a thread but process the contextual events, so we can show any threads around it.
5509+ this . processThreadEvents ( timelineSet . room , threadedEvents , true ) ;
5510+ this . processBeaconEvents ( timelineSet . room , timelineEvents ) ;
5511+
5512+ return timelineSet . getTimelineForEvent ( latestEventIdInTimeline ) ?? timeline ;
54625513 }
54635514
54645515 /**
0 commit comments