2026-09-13 14:14:38 -04:00
"use strict" ;
var _interopRequireDefault = require ( "@babel/runtime/helpers/interopRequireDefault" ) ;
Object . defineProperty ( exports , "__esModule" , {
value : true
} ) ;
exports . EventTimeline = exports . Direction = void 0 ;
var _defineProperty2 = _interopRequireDefault ( require ( "@babel/runtime/helpers/defineProperty" ) ) ;
var _logger = require ( "../logger" ) ;
var _roomState2 = require ( "./room-state" ) ;
var _event = require ( "../@types/event" ) ;
2026-09-12 23:57:45 -04:00
/ *
Copyright 2016 - 2021 The Matrix . org Foundation C . I . C .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2026-09-13 14:14:38 -04:00
let Direction = /*#__PURE__*/ function ( Direction ) {
2026-09-12 23:57:45 -04:00
Direction [ "Backward" ] = "b" ;
Direction [ "Forward" ] = "f" ;
return Direction ;
} ( { } ) ;
2026-09-13 14:14:38 -04:00
exports . Direction = Direction ;
class EventTimeline {
2026-09-12 23:57:45 -04:00
/ * *
* Static helper method to set sender and target properties
*
* @ param event - the event whose metadata is to be set
* @ param stateContext - the room state to be queried
* @ param toStartOfTimeline - if true the event ' s forwardLooking flag is set false
* /
static setEventMetadata ( event , stateContext , toStartOfTimeline ) {
2026-09-13 14:14:38 -04:00
var _event$sender , _event$target ;
// When we try to generate a sentinel member before we have that member
// in the members object, we still generate a sentinel but it doesn't
// have a membership event, so test to see if events.member is set. We
// check this to avoid overriding non-sentinel members by sentinel ones
// when adding the event to a filtered timeline
if ( ! ( ( _event$sender = event . sender ) !== null && _event$sender !== void 0 && ( _event$sender = _event$sender . events ) !== null && _event$sender !== void 0 && _event$sender . member ) ) {
event . sender = stateContext . getSentinelMember ( event . getSender ( ) ) ;
}
if ( ! ( ( _event$target = event . target ) !== null && _event$target !== void 0 && ( _event$target = _event$target . events ) !== null && _event$target !== void 0 && _event$target . member ) && event . getType ( ) === _event . EventType . RoomMember ) {
event . target = stateContext . getSentinelMember ( event . getStateKey ( ) ) ;
}
if ( event . isState ( ) ) {
// room state has no concept of 'old' or 'current', but we want the
// room state to regress back to previous values if toStartOfTimeline
// is set, which means inspecting prev_content if it exists. This
// is done by toggling the forwardLooking flag.
if ( toStartOfTimeline ) {
event . forwardLooking = false ;
}
}
2026-09-12 23:57:45 -04:00
}
/ * *
* Construct a new EventTimeline
*
* < p > An EventTimeline represents a contiguous sequence of events in a room .
*
* < p > As well as keeping track of the events themselves , it stores the state of
* the room at the beginning and end of the timeline , and pagination tokens for
* going backwards and forwards in the timeline .
*
* < p > In order that clients can meaningfully maintain an index into a timeline ,
* the EventTimeline object tracks a 'baseIndex' . This starts at zero , but is
* incremented when events are prepended to the timeline . The index of an event
* relative to baseIndex therefore remains constant .
*
* < p > Once a timeline joins up with its neighbour , they are linked together into a
* doubly - linked list .
*
* @ param eventTimelineSet - the set of timelines this is part of
* /
constructor ( eventTimelineSet ) {
2026-09-13 14:14:38 -04:00
var _eventTimelineSet$roo , _eventTimelineSet$roo2 ;
this . eventTimelineSet = eventTimelineSet ;
( 0 , _defineProperty2 . default ) ( this , "roomId" , void 0 ) ;
( 0 , _defineProperty2 . default ) ( this , "name" , void 0 ) ;
( 0 , _defineProperty2 . default ) ( this , "events" , [ ] ) ;
( 0 , _defineProperty2 . default ) ( this , "baseIndex" , 0 ) ;
( 0 , _defineProperty2 . default ) ( this , "startState" , void 0 ) ;
( 0 , _defineProperty2 . default ) ( this , "endState" , void 0 ) ;
2026-09-12 23:57:45 -04:00
// If we have a roomId then we delegate pagination token storage to the room state objects `startState` and
// `endState`, but for things like the notification timeline which mix multiple rooms we store the tokens ourselves.
2026-09-13 14:14:38 -04:00
( 0 , _defineProperty2 . default ) ( this , "startToken" , null ) ;
( 0 , _defineProperty2 . default ) ( this , "endToken" , null ) ;
( 0 , _defineProperty2 . default ) ( this , "prevTimeline" , null ) ;
( 0 , _defineProperty2 . default ) ( this , "nextTimeline" , null ) ;
( 0 , _defineProperty2 . default ) ( this , "paginationRequests" , {
2026-09-12 23:57:45 -04:00
[ Direction . Backward ] : null ,
[ Direction . Forward ] : null
} ) ;
2026-09-13 14:14:38 -04:00
this . roomId = ( _eventTimelineSet$roo = ( _eventTimelineSet$roo2 = eventTimelineSet . room ) === null || _eventTimelineSet$roo2 === void 0 ? void 0 : _eventTimelineSet$roo2 . roomId ) !== null && _eventTimelineSet$roo !== void 0 ? _eventTimelineSet$roo : null ;
2026-09-12 23:57:45 -04:00
if ( this . roomId ) {
2026-09-13 14:14:38 -04:00
this . startState = new _roomState2 . RoomState ( this . roomId ) ;
this . endState = new _roomState2 . RoomState ( this . roomId ) ;
2026-09-12 23:57:45 -04:00
}
// this is used by client.js
this . paginationRequests = {
b : null ,
f : null
} ;
this . name = this . roomId + ":" + new Date ( ) . toISOString ( ) ;
}
/ * *
* Initialise the start and end state with the given events
*
* < p > This can only be called before any events are added .
*
* @ param stateEvents - list of state events to initialise the
* state with .
* @ throws Error if an attempt is made to call this after addEvent is called .
* /
initialiseState ( stateEvents , {
timelineWasEmpty
} = { } ) {
2026-09-13 14:14:38 -04:00
var _this$startState , _this$endState ;
2026-09-12 23:57:45 -04:00
if ( this . events . length > 0 ) {
throw new Error ( "Cannot initialise state after events are added" ) ;
}
2026-09-13 14:14:38 -04:00
( _this$startState = this . startState ) === null || _this$startState === void 0 ? void 0 : _this$startState . setStateEvents ( stateEvents , {
2026-09-12 23:57:45 -04:00
timelineWasEmpty
} ) ;
2026-09-13 14:14:38 -04:00
( _this$endState = this . endState ) === null || _this$endState === void 0 ? void 0 : _this$endState . setStateEvents ( stateEvents , {
2026-09-12 23:57:45 -04:00
timelineWasEmpty
} ) ;
}
/ * *
* Forks the ( live ) timeline , taking ownership of the existing directional state of this timeline .
* All attached listeners will keep receiving state updates from the new live timeline state .
* The end state of this timeline gets replaced with an independent copy of the current RoomState ,
* and will need a new pagination token if it ever needs to paginate forwards .
* @ param direction - EventTimeline . BACKWARDS to get the state at the
* start of the timeline ; EventTimeline . FORWARDS to get the state at the end
* of the timeline .
*
* @ returns the new timeline
* /
forkLive ( direction ) {
const forkState = this . getState ( direction ) ;
const timeline = new EventTimeline ( this . eventTimelineSet ) ;
2026-09-13 14:14:38 -04:00
timeline . startState = forkState === null || forkState === void 0 ? void 0 : forkState . clone ( ) ;
2026-09-12 23:57:45 -04:00
// Now clobber the end state of the new live timeline with that from the
// previous live timeline. It will be identical except that we'll keep
// using the same RoomMember objects for the 'live' set of members with any
// listeners still attached
timeline . endState = forkState ;
// Firstly, we just stole the current timeline's end state, so it needs a new one.
// Make an immutable copy of the state so back pagination will get the correct sentinels.
2026-09-13 14:14:38 -04:00
this . endState = forkState === null || forkState === void 0 ? void 0 : forkState . clone ( ) ;
2026-09-12 23:57:45 -04:00
return timeline ;
}
/ * *
* Creates an independent timeline , inheriting the directional state from this timeline .
*
* @ param direction - EventTimeline . BACKWARDS to get the state at the
* start of the timeline ; EventTimeline . FORWARDS to get the state at the end
* of the timeline .
*
* @ returns the new timeline
* /
fork ( direction ) {
const forkState = this . getState ( direction ) ;
const timeline = new EventTimeline ( this . eventTimelineSet ) ;
2026-09-13 14:14:38 -04:00
timeline . startState = forkState === null || forkState === void 0 ? void 0 : forkState . clone ( ) ;
timeline . endState = forkState === null || forkState === void 0 ? void 0 : forkState . clone ( ) ;
2026-09-12 23:57:45 -04:00
return timeline ;
}
/ * *
* Get the ID of the room for this timeline
* @ returns room ID
* /
getRoomId ( ) {
return this . roomId ;
}
/ * *
* Get the filter for this timeline ' s timelineSet ( if any )
* @ returns filter
* /
getFilter ( ) {
return this . eventTimelineSet . getFilter ( ) ;
}
/ * *
* Get the timelineSet for this timeline
* @ returns timelineSet
* /
getTimelineSet ( ) {
return this . eventTimelineSet ;
}
/ * *
* Get the base index .
*
* < p > This is an index which is incremented when events are prepended to the
* timeline . An individual event therefore stays at the same index in the array
* relative to the base index ( although note that a given event ' s index may
* well be less than the base index , thus giving that event a negative relative
* index ) .
* /
getBaseIndex ( ) {
return this . baseIndex ;
}
/ * *
* Get the list of events in this context
*
* @ returns An array of MatrixEvents
* /
getEvents ( ) {
return this . events ;
}
/ * *
* Get the room state at the start / end of the timeline
*
* @ param direction - EventTimeline . BACKWARDS to get the state at the
* start of the timeline ; EventTimeline . FORWARDS to get the state at the end
* of the timeline .
*
* @ returns state at the start / end of the timeline
* /
getState ( direction ) {
if ( direction == EventTimeline . BACKWARDS ) {
return this . startState ;
} else if ( direction == EventTimeline . FORWARDS ) {
return this . endState ;
} else {
throw new Error ( "Invalid direction '" + direction + "'" ) ;
}
}
/ * *
* Get a pagination token
*
2026-09-13 14:14:38 -04:00
* @ param direction - EventTimeline . BACKWARDS to get the pagination
2026-09-12 23:57:45 -04:00
* token for going backwards in time ; EventTimeline . FORWARDS to get the
* pagination token for going forwards in time .
*
* @ returns pagination token
* /
getPaginationToken ( direction ) {
if ( this . roomId ) {
return this . getState ( direction ) . paginationToken ;
} else if ( direction === Direction . Backward ) {
return this . startToken ;
} else {
return this . endToken ;
}
}
/ * *
* Set a pagination token
*
* @ param token - pagination token
*
* @ param direction - EventTimeline . BACKWARDS to set the pagination
* token for going backwards in time ; EventTimeline . FORWARDS to set the
* pagination token for going forwards in time .
* /
setPaginationToken ( token , direction ) {
if ( this . roomId ) {
this . getState ( direction ) . paginationToken = token ;
} else if ( direction === Direction . Backward ) {
this . startToken = token ;
} else {
this . endToken = token ;
}
}
/ * *
* Get the next timeline in the series
*
* @ param direction - EventTimeline . BACKWARDS to get the previous
* timeline ; EventTimeline . FORWARDS to get the next timeline .
*
* @ returns previous or following timeline , if they have been
* joined up .
* /
getNeighbouringTimeline ( direction ) {
if ( direction == EventTimeline . BACKWARDS ) {
return this . prevTimeline ;
} else if ( direction == EventTimeline . FORWARDS ) {
return this . nextTimeline ;
} else {
throw new Error ( "Invalid direction '" + direction + "'" ) ;
}
}
/ * *
* Set the next timeline in the series
*
* @ param neighbour - previous / following timeline
*
* @ param direction - EventTimeline . BACKWARDS to set the previous
* timeline ; EventTimeline . FORWARDS to set the next timeline .
*
* @ throws Error if an attempt is made to set the neighbouring timeline when
* it is already set .
* /
setNeighbouringTimeline ( neighbour , direction ) {
if ( this . getNeighbouringTimeline ( direction ) ) {
throw new Error ( "timeline already has a neighbouring timeline - " + "cannot reset neighbour (direction: " + direction + ")" ) ;
}
if ( direction == EventTimeline . BACKWARDS ) {
this . prevTimeline = neighbour ;
} else if ( direction == EventTimeline . FORWARDS ) {
this . nextTimeline = neighbour ;
} else {
throw new Error ( "Invalid direction '" + direction + "'" ) ;
}
// make sure we don't try to paginate this timeline
this . setPaginationToken ( null , direction ) ;
}
/ * *
* Add a new event to the timeline , and update the state
*
* @ param event - new event
* @ param options - addEvent options
* /
2026-09-13 14:14:38 -04:00
/ * *
* @ deprecated In favor of the overload with ` IAddEventOptions `
* /
addEvent ( event , toStartOfTimelineOrOpts , roomState ) {
let toStartOfTimeline = ! ! toStartOfTimelineOrOpts ;
let timelineWasEmpty ;
if ( typeof toStartOfTimelineOrOpts === "object" ) {
( {
toStartOfTimeline ,
roomState ,
timelineWasEmpty
} = toStartOfTimelineOrOpts ) ;
} else if ( toStartOfTimelineOrOpts !== undefined ) {
// Deprecation warning
// FIXME: Remove after 2023-06-01 (technical debt)
_logger . logger . warn ( "Overload deprecated: " + "`EventTimeline.addEvent(event, toStartOfTimeline, roomState?)` " + "is deprecated in favor of the overload with `EventTimeline.addEvent(event, IAddEventOptions)`" ) ;
}
2026-09-12 23:57:45 -04:00
if ( ! roomState ) {
roomState = toStartOfTimeline ? this . startState : this . endState ;
}
const timelineSet = this . getTimelineSet ( ) ;
if ( timelineSet . room ) {
EventTimeline . setEventMetadata ( event , roomState , toStartOfTimeline ) ;
// modify state but only on unfiltered timelineSets
2026-09-13 14:14:38 -04:00
if ( event . isState ( ) && timelineSet . room . getUnfilteredTimelineSet ( ) === timelineSet ) {
var _roomState ;
( _roomState = roomState ) === null || _roomState === void 0 ? void 0 : _roomState . setStateEvents ( [ event ] , {
2026-09-12 23:57:45 -04:00
timelineWasEmpty
} ) ;
// it is possible that the act of setting the state event means we
// can set more metadata (specifically sender/target props), so try
// it again if the prop wasn't previously set. It may also mean that
// the sender/target is updated (if the event set was a room member event)
// so we want to use the *updated* member (new avatar/name) instead.
//
// However, we do NOT want to do this on member events if we're going
// back in time, else we'll set the .sender value for BEFORE the given
// member event, whereas we want to set the .sender value for the ACTUAL
// member event itself.
2026-09-13 14:14:38 -04:00
if ( ! event . sender || event . getType ( ) === _event . EventType . RoomMember && ! toStartOfTimeline ) {
2026-09-12 23:57:45 -04:00
EventTimeline . setEventMetadata ( event , roomState , toStartOfTimeline ) ;
}
}
}
let insertIndex ;
if ( toStartOfTimeline ) {
insertIndex = 0 ;
} else {
insertIndex = this . events . length ;
}
this . events . splice ( insertIndex , 0 , event ) ; // insert element
if ( toStartOfTimeline ) {
this . baseIndex ++ ;
}
}
/ * *
* Insert a new event into the timeline , and update the state .
*
* TEMPORARY : until we have recursive relations , we need this function
* to exist to allow us to insert events in timeline order , which is our
* best guess for Sync Order .
* This is a copy of addEvent above , modified to allow inserting an event at
* a specific index .
*
* @ internal
* /
2026-09-13 14:14:38 -04:00
insertEvent ( event , insertIndex , roomState ) {
2026-09-12 23:57:45 -04:00
const timelineSet = this . getTimelineSet ( ) ;
if ( timelineSet . room ) {
EventTimeline . setEventMetadata ( event , roomState , false ) ;
// modify state but only on unfiltered timelineSets
2026-09-13 14:14:38 -04:00
if ( event . isState ( ) && timelineSet . room . getUnfilteredTimelineSet ( ) === timelineSet ) {
2026-09-12 23:57:45 -04:00
roomState . setStateEvents ( [ event ] , { } ) ;
// it is possible that the act of setting the state event means we
// can set more metadata (specifically sender/target props), so try
// it again if the prop wasn't previously set. It may also mean that
// the sender/target is updated (if the event set was a room member event)
// so we want to use the *updated* member (new avatar/name) instead.
//
// However, we do NOT want to do this on member events if we're going
// back in time, else we'll set the .sender value for BEFORE the given
// member event, whereas we want to set the .sender value for the ACTUAL
// member event itself.
2026-09-13 14:14:38 -04:00
if ( ! event . sender || event . getType ( ) === _event . EventType . RoomMember ) {
2026-09-12 23:57:45 -04:00
EventTimeline . setEventMetadata ( event , roomState , false ) ;
}
}
}
this . events . splice ( insertIndex , 0 , event ) ; // insert element
}
/ * *
* Remove an event from the timeline
*
* @ param eventId - ID of event to be removed
* @ returns removed event , or null if not found
* /
removeEvent ( eventId ) {
for ( let i = this . events . length - 1 ; i >= 0 ; i -- ) {
const ev = this . events [ i ] ;
if ( ev . getId ( ) == eventId ) {
this . events . splice ( i , 1 ) ;
if ( i < this . baseIndex ) {
this . baseIndex -- ;
}
return ev ;
}
}
return null ;
}
/ * *
* Return a string to identify this timeline , for debugging
*
* @ returns name for this timeline
* /
toString ( ) {
return this . name ;
}
}
2026-09-13 14:14:38 -04:00
exports . EventTimeline = EventTimeline ;
2026-09-12 23:57:45 -04:00
/ * *
* Symbolic constant for methods which take a 'direction' argument :
* refers to the start of the timeline , or backwards in time .
* /
2026-09-13 14:14:38 -04:00
( 0 , _defineProperty2 . default ) ( EventTimeline , "BACKWARDS" , Direction . Backward ) ;
2026-09-12 23:57:45 -04:00
/ * *
* Symbolic constant for methods which take a 'direction' argument :
* refers to the end of the timeline , or forwards in time .
* /
2026-09-13 14:14:38 -04:00
( 0 , _defineProperty2 . default ) ( EventTimeline , "FORWARDS" , Direction . Forward ) ;
2026-09-12 23:57:45 -04:00
//# sourceMappingURL=event-timeline.js.map