Add first design of timeline
The current unfinished design is a first implementation of how the timeline could be built properly. It yet needs a simple way of adding data types, however, which should be a bit more universal.refactor
parent
a68253cc43
commit
8e647a870e
|
@ -6,367 +6,484 @@ a simple way to view events.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import FastDict as Dict exposing (Dict)
|
import FastDict as Dict exposing (Dict)
|
||||||
import Internal.Config.Leaking as Leaking
|
|
||||||
import Internal.Tools.Fold as Fold
|
|
||||||
import Internal.Values.Event as Event exposing (IEvent)
|
|
||||||
import Internal.Values.StateManager as StateManager exposing (StateManager)
|
|
||||||
import Internal.Tools.DefaultDict as DefaultDict exposing (DefaultDict)
|
|
||||||
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
|
||||||
import Internal.Tools.Iddict as Iddict exposing (Iddict)
|
import Internal.Tools.Iddict as Iddict exposing (Iddict)
|
||||||
import Internal.Tools.Filters.Main as Filter exposing (Filter)
|
import Internal.Tools.Filters.Main as Filter exposing (Filter)
|
||||||
import Internal.Config.Leaking exposing (nextBatch)
|
import Internal.Tools.Iddict as Iddict
|
||||||
|
|
||||||
{-| The Timeline is a comprehensive object describing a timeline in a room.
|
type alias Timeline =
|
||||||
|
{ mostRecentToken : TokenId
|
||||||
Any Timeline type contains the following pieces of information:
|
, slices : Iddict Slice
|
||||||
|
, tokenToId : Dict String TokenId
|
||||||
- `events` Comprehensive dictionary containing all locally stored timeline events
|
, tokens : Iddict Token
|
||||||
- `batches` Comprehensive dictionary containing all batches. Batches are pieces
|
|
||||||
of the timeline that have been sent by the homeserver.
|
|
||||||
- `token` Dictionary that maps for each batch token which batches it borders
|
|
||||||
- `mostRecentSync` Id of the most "recent" batch in the timeline
|
|
||||||
-}
|
|
||||||
type Timeline
|
|
||||||
= Timeline
|
|
||||||
{ events : Hashdict IEvent
|
|
||||||
, batches : Iddict TimelineBatch
|
|
||||||
, token : DefaultDict String (List Int)
|
|
||||||
, mostRecentSync : Maybe Int
|
|
||||||
}
|
|
||||||
|
|
||||||
{-| A BatchToken is a token that has been handed out by the server to mark the end of a -}
|
|
||||||
type alias BatchToken = String
|
|
||||||
|
|
||||||
type alias TimelineBatch =
|
|
||||||
{ prevBatch : List Batch
|
|
||||||
, nextBatch : List Batch
|
|
||||||
, filter : Filter
|
|
||||||
, events : List String
|
|
||||||
, stateDelta : StateManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Batch
|
type TokenId = TokenId Int
|
||||||
= Token BatchToken
|
|
||||||
| Batch Int
|
|
||||||
|
|
||||||
addNewSync :
|
type SliceId = SliceId Int
|
||||||
{ events : List IEvent
|
|
||||||
, filter : Filter
|
|
||||||
, limited : Bool
|
|
||||||
, nextBatch : String
|
|
||||||
, prevBatch : String
|
|
||||||
, stateDelta : Maybe StateManager
|
|
||||||
} -> Timeline -> Timeline
|
|
||||||
addNewSync data (Timeline timeline) =
|
|
||||||
let
|
|
||||||
batchToInsert : TimelineBatch
|
|
||||||
batchToInsert =
|
|
||||||
{ prevBatch =
|
|
||||||
[ Just <| Token data.prevBatch
|
|
||||||
, Maybe.map Batch timeline.mostRecentSync
|
|
||||||
]
|
|
||||||
|> List.filterMap identity
|
|
||||||
, nextBatch =
|
|
||||||
[ Token data.nextBatch ]
|
|
||||||
, filter = data.filter
|
|
||||||
, events = List.map Event.eventId data.events
|
|
||||||
, stateDelta = Maybe.withDefault StateManager.empty data.stateDelta
|
|
||||||
}
|
|
||||||
in
|
|
||||||
case Iddict.insert batchToInsert timeline.batches of
|
|
||||||
( batchId, batches ) ->
|
|
||||||
Timeline
|
|
||||||
{ events = List.foldl Hashdict.insert timeline.events data.events
|
|
||||||
, batches = batches
|
|
||||||
, mostRecentSync = Just batchId
|
|
||||||
, token =
|
|
||||||
timeline.token
|
|
||||||
|> DefaultDict.update data.prevBatch
|
|
||||||
(\value ->
|
|
||||||
case value of
|
|
||||||
Just v ->
|
|
||||||
Just (batchId :: v)
|
|
||||||
Nothing ->
|
|
||||||
Just [ batchId ]
|
|
||||||
)
|
|
||||||
|> DefaultDict.update data.nextBatch
|
|
||||||
(\value ->
|
|
||||||
case value of
|
|
||||||
Just v ->
|
|
||||||
Just (batchId :: v)
|
|
||||||
Nothing ->
|
|
||||||
Just [ batchId ]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
type Slice
|
||||||
|
= Slice
|
||||||
|
{ events : List EventId
|
||||||
|
, filter : Filter
|
||||||
|
, next : List TokenId
|
||||||
|
, previous : List TokenId
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token
|
||||||
|
= Token
|
||||||
|
{ next : List SliceId
|
||||||
|
, previous : List SliceId
|
||||||
|
, head : String
|
||||||
|
, tail : List String
|
||||||
|
}
|
||||||
|
|
||||||
|
type alias EventId = String
|
||||||
|
|
||||||
|
{-| Add a new token to the timeline. If it already exists, this function does
|
||||||
|
nothing and instead returns the existing token id.
|
||||||
|
-}
|
||||||
|
addToken : Token -> Timeline -> ( TokenId, Timeline )
|
||||||
|
addToken ((Token { head }) as token) timeline =
|
||||||
|
case Dict.get head timeline.tokenToId of
|
||||||
|
Just tokenId ->
|
||||||
|
( tokenId, timeline )
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
insertToken token timeline
|
||||||
|
|
||||||
|
{-| Sometimes two separate tokens point to the same location in the timeline.
|
||||||
|
You can add a new token value as an alias to the timeline using this function.
|
||||||
|
-}
|
||||||
|
addTokenAlias : String -> String -> Timeline -> Timeline
|
||||||
|
addTokenAlias old new timeline =
|
||||||
|
case Dict.get old timeline.tokenToId of
|
||||||
|
Just tokenId ->
|
||||||
|
timeline
|
||||||
|
-- Update the token
|
||||||
|
|> mapToken
|
||||||
|
tokenId
|
||||||
|
(\(Token t) ->
|
||||||
|
Token { t | head = new, tail = t.head :: t.tail }
|
||||||
|
)
|
||||||
|
-- Add a token pointer for the new value
|
||||||
|
|> (\tl -> { tl | tokenToId = Dict.insert new tokenId tl.tokenToId })
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
timeline
|
||||||
|
|
||||||
|
{-| Get an empty timeline.
|
||||||
|
-}
|
||||||
|
empty : Timeline
|
||||||
|
empty =
|
||||||
|
{ mostRecentToken = TokenId 0
|
||||||
|
, slices = Iddict.empty
|
||||||
|
, tokenToId = Dict.empty
|
||||||
|
, tokens = Iddict.empty
|
||||||
|
}
|
||||||
|
|
||||||
|
{-| Get a slice of events from the timeline.
|
||||||
|
-}
|
||||||
|
getSlice : SliceId -> Timeline -> Maybe Slice
|
||||||
|
getSlice (SliceId key) { slices } =
|
||||||
|
Iddict.get key slices
|
||||||
|
|
||||||
|
{-| Get a token value from the timeline.
|
||||||
|
-}
|
||||||
|
getToken : TokenId -> Timeline -> Maybe Token
|
||||||
|
getToken (TokenId key) { tokens } =
|
||||||
|
Iddict.get key tokens
|
||||||
|
|
||||||
|
{-| Get the token id of an existing token value.
|
||||||
|
-}
|
||||||
|
getTokenId : String -> Timeline -> Maybe TokenId
|
||||||
|
getToken v timeline =
|
||||||
|
Dict.get v timeline.tokenToId
|
||||||
|
|
||||||
|
{-| Insert a new slice into the timeline.
|
||||||
|
-}
|
||||||
|
insertSlice : Slice -> Timeline -> ( SliceId, Timeline )
|
||||||
|
insertSlice slice timeline =
|
||||||
|
timeline.slices
|
||||||
|
|> Iddict.insert slice
|
||||||
|
|> Tuple.mapBoth SliceId (\x -> { timeline | slices = x })
|
||||||
|
|
||||||
|
{-| Insert a new token into the timeline.
|
||||||
|
-}
|
||||||
|
insertToken : Token -> Timeline -> ( TokenId, Timeline )
|
||||||
|
insertToken ((Token { head }) as token) timeline =
|
||||||
|
case Iddict.insert token timeline.tokens of
|
||||||
|
( tokenId, tokens ) ->
|
||||||
|
( TokenId tokenId
|
||||||
|
, { timeline
|
||||||
|
| tokenToId = Dict.insert head (TokenId tokenId) timeline.tokenToId
|
||||||
|
, tokens = tokens
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
{-| Update an existing slice based on its id.
|
||||||
|
-}
|
||||||
|
mapSlice : SliceId -> (Slice -> Slice) -> Timeline -> Timeline
|
||||||
|
mapSlice (SliceId sliceId) f timeline =
|
||||||
|
{ timeline | slices = Iddict.map sliceId f timeline.slices }
|
||||||
|
|
||||||
|
{-| Update an existing token based on its id.
|
||||||
|
-}
|
||||||
|
mapToken : TokenId -> (Token -> Token) -> Timeline -> Timeline
|
||||||
|
mapToken (TokenId tokenId) f timeline =
|
||||||
|
{ timeline | tokens = Iddict.map tokenId f timeline.tokens }
|
||||||
|
|
||||||
|
|
||||||
|
-- {-| The Timeline is a comprehensive object describing a timeline in a room.
|
||||||
|
|
||||||
|
-- Any Timeline type contains the following pieces of information:
|
||||||
|
|
||||||
|
-- - `events` Comprehensive dictionary containing all locally stored timeline events
|
||||||
|
-- - `batches` Comprehensive dictionary containing all batches. Batches are pieces
|
||||||
|
-- of the timeline that have been sent by the homeserver.
|
||||||
|
-- - `token` Dictionary that maps for each batch token which batches it borders
|
||||||
|
-- - `mostRecentSync` Id of the most "recent" batch in the timeline
|
||||||
|
-- -}
|
||||||
-- type Timeline
|
-- type Timeline
|
||||||
-- = Timeline
|
-- = Timeline
|
||||||
-- { prevBatch : String
|
-- { events : Hashdict IEvent
|
||||||
-- , nextBatch : String
|
-- , batches : Iddict TimelineBatch
|
||||||
-- , events : List IEvent
|
-- , token : DefaultDict String (List Int)
|
||||||
-- , stateAtStart : StateManager
|
-- , mostRecentSync : Maybe Int
|
||||||
-- , previous : BeforeTimeline
|
-- }
|
||||||
|
|
||||||
|
-- {-| A BatchToken is a token that has been handed out by the server to mark the end of a -}
|
||||||
|
-- type alias BatchToken = String
|
||||||
|
|
||||||
|
-- type alias TimelineBatch =
|
||||||
|
-- { prevBatch : List Batch
|
||||||
|
-- , nextBatch : List Batch
|
||||||
|
-- , filter : Filter
|
||||||
|
-- , events : List String
|
||||||
|
-- , stateDelta : StateManager
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- type Batch
|
||||||
|
-- = Token BatchToken
|
||||||
|
-- | Batch Int
|
||||||
|
|
||||||
|
-- addNewSync :
|
||||||
|
-- { events : List IEvent
|
||||||
|
-- , filter : Filter
|
||||||
|
-- , limited : Bool
|
||||||
|
-- , nextBatch : String
|
||||||
|
-- , prevBatch : String
|
||||||
|
-- , stateDelta : Maybe StateManager
|
||||||
|
-- } -> Timeline -> Timeline
|
||||||
|
-- addNewSync data (Timeline timeline) =
|
||||||
|
-- let
|
||||||
|
-- batchToInsert : TimelineBatch
|
||||||
|
-- batchToInsert =
|
||||||
|
-- { prevBatch =
|
||||||
|
-- [ Just <| Token data.prevBatch
|
||||||
|
-- , Maybe.map Batch timeline.mostRecentSync
|
||||||
|
-- ]
|
||||||
|
-- |> List.filterMap identity
|
||||||
|
-- , nextBatch =
|
||||||
|
-- [ Token data.nextBatch ]
|
||||||
|
-- , filter = data.filter
|
||||||
|
-- , events = List.map Event.eventId data.events
|
||||||
|
-- , stateDelta = Maybe.withDefault StateManager.empty data.stateDelta
|
||||||
|
-- }
|
||||||
|
-- in
|
||||||
|
-- case Iddict.insert batchToInsert timeline.batches of
|
||||||
|
-- ( batchId, batches ) ->
|
||||||
|
-- Timeline
|
||||||
|
-- { events = List.foldl Hashdict.insert timeline.events data.events
|
||||||
|
-- , batches = batches
|
||||||
|
-- , mostRecentSync = Just batchId
|
||||||
|
-- , token =
|
||||||
|
-- timeline.token
|
||||||
|
-- |> DefaultDict.update data.prevBatch
|
||||||
|
-- (\value ->
|
||||||
|
-- case value of
|
||||||
|
-- Just v ->
|
||||||
|
-- Just (batchId :: v)
|
||||||
|
-- Nothing ->
|
||||||
|
-- Just [ batchId ]
|
||||||
|
-- )
|
||||||
|
-- |> DefaultDict.update data.nextBatch
|
||||||
|
-- (\value ->
|
||||||
|
-- case value of
|
||||||
|
-- Just v ->
|
||||||
|
-- Just (batchId :: v)
|
||||||
|
-- Nothing ->
|
||||||
|
-- Just [ batchId ]
|
||||||
|
-- )
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- -- type Timeline
|
||||||
|
-- -- = Timeline
|
||||||
|
-- -- { prevBatch : String
|
||||||
|
-- -- , nextBatch : String
|
||||||
|
-- -- , events : List IEvent
|
||||||
|
-- -- , stateAtStart : StateManager
|
||||||
|
-- -- , previous : BeforeTimeline
|
||||||
|
-- -- }
|
||||||
|
|
||||||
|
|
||||||
|
-- type BeforeTimeline
|
||||||
|
-- = Endless String
|
||||||
|
-- | Gap Timeline
|
||||||
|
-- | StartOfTimeline
|
||||||
|
|
||||||
|
|
||||||
|
-- {-| Add a new batch of events to the front of the timeline.
|
||||||
|
-- -}
|
||||||
|
-- addNewEvents :
|
||||||
|
-- { events : List IEvent
|
||||||
|
-- , limited : Bool
|
||||||
|
-- , nextBatch : String
|
||||||
|
-- , prevBatch : String
|
||||||
|
-- , stateDelta : Maybe StateManager
|
||||||
|
-- }
|
||||||
|
-- -> Timeline
|
||||||
|
-- -> Timeline
|
||||||
|
-- addNewEvents { events, limited, nextBatch, prevBatch, stateDelta } (Timeline t) =
|
||||||
|
-- Timeline
|
||||||
|
-- (if prevBatch == t.nextBatch || not limited then
|
||||||
|
-- { t
|
||||||
|
-- | events = t.events ++ events
|
||||||
|
-- , nextBatch = nextBatch
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- else
|
||||||
|
-- { prevBatch = prevBatch
|
||||||
|
-- , nextBatch = nextBatch
|
||||||
|
-- , events = events
|
||||||
|
-- , stateAtStart =
|
||||||
|
-- t
|
||||||
|
-- |> Timeline
|
||||||
|
-- |> mostRecentState
|
||||||
|
-- |> StateManager.updateRoomStateWith
|
||||||
|
-- (stateDelta
|
||||||
|
-- |> Maybe.withDefault StateManager.empty
|
||||||
|
-- )
|
||||||
|
-- , previous = Gap (Timeline t)
|
||||||
|
-- }
|
||||||
|
-- )
|
||||||
|
|
||||||
|
|
||||||
|
-- {-| Create a new timeline.
|
||||||
|
-- -}
|
||||||
|
-- newFromEvents :
|
||||||
|
-- { events : List IEvent
|
||||||
|
-- , nextBatch : String
|
||||||
|
-- , prevBatch : Maybe String
|
||||||
|
-- , stateDelta : Maybe StateManager
|
||||||
|
-- }
|
||||||
|
-- -> Timeline
|
||||||
|
-- newFromEvents { events, nextBatch, prevBatch, stateDelta } =
|
||||||
|
-- Timeline
|
||||||
|
-- { events = events
|
||||||
|
-- , nextBatch = nextBatch
|
||||||
|
-- , prevBatch =
|
||||||
|
-- prevBatch
|
||||||
|
-- |> Maybe.withDefault Leaking.prevBatch
|
||||||
|
-- , previous =
|
||||||
|
-- prevBatch
|
||||||
|
-- |> Maybe.map Endless
|
||||||
|
-- |> Maybe.withDefault StartOfTimeline
|
||||||
|
-- , stateAtStart =
|
||||||
|
-- stateDelta
|
||||||
|
-- |> Maybe.withDefault StateManager.empty
|
||||||
-- }
|
-- }
|
||||||
|
|
||||||
|
|
||||||
type BeforeTimeline
|
-- {-| Insert events starting from a known batch token.
|
||||||
= Endless String
|
-- -}
|
||||||
| Gap Timeline
|
-- insertEvents :
|
||||||
| StartOfTimeline
|
-- { events : List IEvent
|
||||||
|
-- , nextBatch : String
|
||||||
|
-- , prevBatch : Maybe String
|
||||||
|
-- , stateDelta : Maybe StateManager
|
||||||
|
-- }
|
||||||
|
-- -> Timeline
|
||||||
|
-- -> Timeline
|
||||||
|
-- insertEvents ({ events, nextBatch, prevBatch, stateDelta } as data) (Timeline t) =
|
||||||
|
-- Timeline
|
||||||
|
-- (case prevBatch of
|
||||||
|
-- -- No prevbatch suggests the start of the timeline.
|
||||||
|
-- -- This means that we must recurse until we've hit the bottom,
|
||||||
|
-- -- and then mark the bottom of the timeline.
|
||||||
|
-- Nothing ->
|
||||||
|
-- case t.previous of
|
||||||
|
-- Gap prevT ->
|
||||||
|
-- { t
|
||||||
|
-- | previous =
|
||||||
|
-- prevT
|
||||||
|
-- |> insertEvents data
|
||||||
|
-- |> Gap
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- _ ->
|
||||||
|
-- if nextBatch == t.prevBatch then
|
||||||
|
-- { t | previous = StartOfTimeline, events = events ++ t.events, stateAtStart = StateManager.empty }
|
||||||
|
|
||||||
|
-- else
|
||||||
|
-- { t | previous = Gap <| newFromEvents data }
|
||||||
|
|
||||||
|
-- -- If there is a prevbatch, it is not the start of the timeline
|
||||||
|
-- -- and could be located anywhere.
|
||||||
|
-- -- Starting at the front, look for a way to match it with the existing timeline.
|
||||||
|
-- Just p ->
|
||||||
|
-- -- Piece connects to the front of the timeline.
|
||||||
|
-- if t.nextBatch == p then
|
||||||
|
-- { t
|
||||||
|
-- | events = t.events ++ events
|
||||||
|
-- , nextBatch = nextBatch
|
||||||
|
-- }
|
||||||
|
-- -- Piece connects to the back of the timeline.
|
||||||
|
|
||||||
|
-- else if nextBatch == t.prevBatch then
|
||||||
|
-- case t.previous of
|
||||||
|
-- Gap (Timeline prevT) ->
|
||||||
|
-- -- Piece also connects to the timeline in the back,
|
||||||
|
-- -- allowing the two timelines to merge.
|
||||||
|
-- if prevT.nextBatch == p then
|
||||||
|
-- { events = prevT.events ++ events ++ t.events
|
||||||
|
-- , nextBatch = t.nextBatch
|
||||||
|
-- , prevBatch = prevT.prevBatch
|
||||||
|
-- , stateAtStart = prevT.stateAtStart
|
||||||
|
-- , previous = prevT.previous
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- else
|
||||||
|
-- { t
|
||||||
|
-- | events = events ++ t.events
|
||||||
|
-- , prevBatch = p
|
||||||
|
-- , stateAtStart =
|
||||||
|
-- stateDelta
|
||||||
|
-- |> Maybe.withDefault StateManager.empty
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- Endless _ ->
|
||||||
|
-- { t
|
||||||
|
-- | events = events ++ t.events
|
||||||
|
-- , prevBatch = p
|
||||||
|
-- , stateAtStart =
|
||||||
|
-- stateDelta
|
||||||
|
-- |> Maybe.withDefault StateManager.empty
|
||||||
|
-- , previous = Endless p
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- _ ->
|
||||||
|
-- { t
|
||||||
|
-- | events = events ++ t.events
|
||||||
|
-- , prevBatch = p
|
||||||
|
-- , stateAtStart =
|
||||||
|
-- stateDelta
|
||||||
|
-- |> Maybe.withDefault StateManager.empty
|
||||||
|
-- }
|
||||||
|
-- -- Piece doesn't connect to this piece of the timeline.
|
||||||
|
-- -- Consequently, look for previous parts of the timeline to see if it connects.
|
||||||
|
|
||||||
|
-- else
|
||||||
|
-- case t.previous of
|
||||||
|
-- Gap prevT ->
|
||||||
|
-- { t
|
||||||
|
-- | previous =
|
||||||
|
-- prevT
|
||||||
|
-- |> insertEvents data
|
||||||
|
-- |> Gap
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- _ ->
|
||||||
|
-- t
|
||||||
|
-- )
|
||||||
|
|
||||||
|
|
||||||
{-| Add a new batch of events to the front of the timeline.
|
-- {-| Get the width of the latest gap. This data is usually accessed when trying to get more messages.
|
||||||
-}
|
-- -}
|
||||||
addNewEvents :
|
-- latestGap : Timeline -> Maybe { from : Maybe String, to : String }
|
||||||
{ events : List IEvent
|
-- latestGap (Timeline t) =
|
||||||
, limited : Bool
|
-- case t.previous of
|
||||||
, nextBatch : String
|
-- StartOfTimeline ->
|
||||||
, prevBatch : String
|
-- Nothing
|
||||||
, stateDelta : Maybe StateManager
|
|
||||||
}
|
|
||||||
-> Timeline
|
|
||||||
-> Timeline
|
|
||||||
addNewEvents { events, limited, nextBatch, prevBatch, stateDelta } (Timeline t) =
|
|
||||||
Timeline
|
|
||||||
(if prevBatch == t.nextBatch || not limited then
|
|
||||||
{ t
|
|
||||||
| events = t.events ++ events
|
|
||||||
, nextBatch = nextBatch
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
-- Endless prevBatch ->
|
||||||
{ prevBatch = prevBatch
|
-- Just { from = Nothing, to = prevBatch }
|
||||||
, nextBatch = nextBatch
|
|
||||||
, events = events
|
-- Gap (Timeline pt) ->
|
||||||
, stateAtStart =
|
-- Just { from = Just pt.nextBatch, to = t.prevBatch }
|
||||||
t
|
|
||||||
|> Timeline
|
|
||||||
|> mostRecentState
|
|
||||||
|> StateManager.updateRoomStateWith
|
|
||||||
(stateDelta
|
|
||||||
|> Maybe.withDefault StateManager.empty
|
|
||||||
)
|
|
||||||
, previous = Gap (Timeline t)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a new timeline.
|
-- {-| Get the longest uninterrupted length of most recent events.
|
||||||
-}
|
-- -}
|
||||||
newFromEvents :
|
-- localSize : Timeline -> Int
|
||||||
{ events : List IEvent
|
-- localSize =
|
||||||
, nextBatch : String
|
-- mostRecentEvents >> List.length
|
||||||
, prevBatch : Maybe String
|
|
||||||
, stateDelta : Maybe StateManager
|
|
||||||
}
|
|
||||||
-> Timeline
|
|
||||||
newFromEvents { events, nextBatch, prevBatch, stateDelta } =
|
|
||||||
Timeline
|
|
||||||
{ events = events
|
|
||||||
, nextBatch = nextBatch
|
|
||||||
, prevBatch =
|
|
||||||
prevBatch
|
|
||||||
|> Maybe.withDefault Leaking.prevBatch
|
|
||||||
, previous =
|
|
||||||
prevBatch
|
|
||||||
|> Maybe.map Endless
|
|
||||||
|> Maybe.withDefault StartOfTimeline
|
|
||||||
, stateAtStart =
|
|
||||||
stateDelta
|
|
||||||
|> Maybe.withDefault StateManager.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{-| Insert events starting from a known batch token.
|
-- {-| Get a list of the most recent events recorded.
|
||||||
-}
|
-- -}
|
||||||
insertEvents :
|
-- mostRecentEvents : Timeline -> List IEvent
|
||||||
{ events : List IEvent
|
-- mostRecentEvents (Timeline t) =
|
||||||
, nextBatch : String
|
-- t.events
|
||||||
, prevBatch : Maybe String
|
|
||||||
, stateDelta : Maybe StateManager
|
|
||||||
}
|
|
||||||
-> Timeline
|
|
||||||
-> Timeline
|
|
||||||
insertEvents ({ events, nextBatch, prevBatch, stateDelta } as data) (Timeline t) =
|
|
||||||
Timeline
|
|
||||||
(case prevBatch of
|
|
||||||
-- No prevbatch suggests the start of the timeline.
|
|
||||||
-- This means that we must recurse until we've hit the bottom,
|
|
||||||
-- and then mark the bottom of the timeline.
|
|
||||||
Nothing ->
|
|
||||||
case t.previous of
|
|
||||||
Gap prevT ->
|
|
||||||
{ t
|
|
||||||
| previous =
|
|
||||||
prevT
|
|
||||||
|> insertEvents data
|
|
||||||
|> Gap
|
|
||||||
}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
if nextBatch == t.prevBatch then
|
|
||||||
{ t | previous = StartOfTimeline, events = events ++ t.events, stateAtStart = StateManager.empty }
|
|
||||||
|
|
||||||
else
|
|
||||||
{ t | previous = Gap <| newFromEvents data }
|
|
||||||
|
|
||||||
-- If there is a prevbatch, it is not the start of the timeline
|
|
||||||
-- and could be located anywhere.
|
|
||||||
-- Starting at the front, look for a way to match it with the existing timeline.
|
|
||||||
Just p ->
|
|
||||||
-- Piece connects to the front of the timeline.
|
|
||||||
if t.nextBatch == p then
|
|
||||||
{ t
|
|
||||||
| events = t.events ++ events
|
|
||||||
, nextBatch = nextBatch
|
|
||||||
}
|
|
||||||
-- Piece connects to the back of the timeline.
|
|
||||||
|
|
||||||
else if nextBatch == t.prevBatch then
|
|
||||||
case t.previous of
|
|
||||||
Gap (Timeline prevT) ->
|
|
||||||
-- Piece also connects to the timeline in the back,
|
|
||||||
-- allowing the two timelines to merge.
|
|
||||||
if prevT.nextBatch == p then
|
|
||||||
{ events = prevT.events ++ events ++ t.events
|
|
||||||
, nextBatch = t.nextBatch
|
|
||||||
, prevBatch = prevT.prevBatch
|
|
||||||
, stateAtStart = prevT.stateAtStart
|
|
||||||
, previous = prevT.previous
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{ t
|
|
||||||
| events = events ++ t.events
|
|
||||||
, prevBatch = p
|
|
||||||
, stateAtStart =
|
|
||||||
stateDelta
|
|
||||||
|> Maybe.withDefault StateManager.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
Endless _ ->
|
|
||||||
{ t
|
|
||||||
| events = events ++ t.events
|
|
||||||
, prevBatch = p
|
|
||||||
, stateAtStart =
|
|
||||||
stateDelta
|
|
||||||
|> Maybe.withDefault StateManager.empty
|
|
||||||
, previous = Endless p
|
|
||||||
}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{ t
|
|
||||||
| events = events ++ t.events
|
|
||||||
, prevBatch = p
|
|
||||||
, stateAtStart =
|
|
||||||
stateDelta
|
|
||||||
|> Maybe.withDefault StateManager.empty
|
|
||||||
}
|
|
||||||
-- Piece doesn't connect to this piece of the timeline.
|
|
||||||
-- Consequently, look for previous parts of the timeline to see if it connects.
|
|
||||||
|
|
||||||
else
|
|
||||||
case t.previous of
|
|
||||||
Gap prevT ->
|
|
||||||
{ t
|
|
||||||
| previous =
|
|
||||||
prevT
|
|
||||||
|> insertEvents data
|
|
||||||
|> Gap
|
|
||||||
}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
t
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the width of the latest gap. This data is usually accessed when trying to get more messages.
|
-- {-| Get the needed `since` parameter to get the latest events.
|
||||||
-}
|
-- -}
|
||||||
latestGap : Timeline -> Maybe { from : Maybe String, to : String }
|
-- nextSyncToken : Timeline -> String
|
||||||
latestGap (Timeline t) =
|
-- nextSyncToken (Timeline t) =
|
||||||
case t.previous of
|
-- t.nextBatch
|
||||||
StartOfTimeline ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
Endless prevBatch ->
|
|
||||||
Just { from = Nothing, to = prevBatch }
|
|
||||||
|
|
||||||
Gap (Timeline pt) ->
|
|
||||||
Just { from = Just pt.nextBatch, to = t.prevBatch }
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the longest uninterrupted length of most recent events.
|
-- {-| Get the state of the room after the most recent event.
|
||||||
-}
|
-- -}
|
||||||
localSize : Timeline -> Int
|
-- mostRecentState : Timeline -> StateManager
|
||||||
localSize =
|
-- mostRecentState (Timeline t) =
|
||||||
mostRecentEvents >> List.length
|
-- t.stateAtStart
|
||||||
|
-- |> StateManager.updateRoomStateWith
|
||||||
|
-- (StateManager.fromEventList t.events)
|
||||||
|
|
||||||
|
|
||||||
{-| Get a list of the most recent events recorded.
|
-- {-| Get the timeline's room state at any given event. The function returns `Nothing` if the event is not found in the timeline.
|
||||||
-}
|
-- -}
|
||||||
mostRecentEvents : Timeline -> List IEvent
|
-- stateAtEvent : IEvent -> Timeline -> Maybe StateManager
|
||||||
mostRecentEvents (Timeline t) =
|
-- stateAtEvent event (Timeline t) =
|
||||||
t.events
|
-- if
|
||||||
|
-- t.events
|
||||||
|
-- |> List.map Event.eventId
|
||||||
|
-- |> List.member (Event.eventId event)
|
||||||
|
-- then
|
||||||
|
-- Fold.untilCompleted
|
||||||
|
-- List.foldl
|
||||||
|
-- (\e ->
|
||||||
|
-- StateManager.addEvent e
|
||||||
|
-- >> (if Event.eventId e == Event.eventId event then
|
||||||
|
-- Fold.AnswerWith
|
||||||
|
|
||||||
|
-- else
|
||||||
|
-- Fold.ContinueWith
|
||||||
|
-- )
|
||||||
|
-- )
|
||||||
|
-- t.stateAtStart
|
||||||
|
-- t.events
|
||||||
|
-- |> Just
|
||||||
|
|
||||||
|
-- else
|
||||||
|
-- case t.previous of
|
||||||
|
-- Gap prevT ->
|
||||||
|
-- stateAtEvent event prevT
|
||||||
|
|
||||||
|
-- _ ->
|
||||||
|
-- Nothing
|
||||||
|
|
||||||
|
|
||||||
{-| Get the needed `since` parameter to get the latest events.
|
-- {-| Count how many events the current timeline is storing.
|
||||||
-}
|
-- -}
|
||||||
nextSyncToken : Timeline -> String
|
-- size : Timeline -> Int
|
||||||
nextSyncToken (Timeline t) =
|
-- size (Timeline t) =
|
||||||
t.nextBatch
|
-- (case t.previous of
|
||||||
|
-- Gap prev ->
|
||||||
|
-- size prev
|
||||||
|
|
||||||
|
-- _ ->
|
||||||
{-| Get the state of the room after the most recent event.
|
-- 0
|
||||||
-}
|
-- )
|
||||||
mostRecentState : Timeline -> StateManager
|
-- + List.length t.events
|
||||||
mostRecentState (Timeline t) =
|
|
||||||
t.stateAtStart
|
|
||||||
|> StateManager.updateRoomStateWith
|
|
||||||
(StateManager.fromEventList t.events)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the timeline's room state at any given event. The function returns `Nothing` if the event is not found in the timeline.
|
|
||||||
-}
|
|
||||||
stateAtEvent : IEvent -> Timeline -> Maybe StateManager
|
|
||||||
stateAtEvent event (Timeline t) =
|
|
||||||
if
|
|
||||||
t.events
|
|
||||||
|> List.map Event.eventId
|
|
||||||
|> List.member (Event.eventId event)
|
|
||||||
then
|
|
||||||
Fold.untilCompleted
|
|
||||||
List.foldl
|
|
||||||
(\e ->
|
|
||||||
StateManager.addEvent e
|
|
||||||
>> (if Event.eventId e == Event.eventId event then
|
|
||||||
Fold.AnswerWith
|
|
||||||
|
|
||||||
else
|
|
||||||
Fold.ContinueWith
|
|
||||||
)
|
|
||||||
)
|
|
||||||
t.stateAtStart
|
|
||||||
t.events
|
|
||||||
|> Just
|
|
||||||
|
|
||||||
else
|
|
||||||
case t.previous of
|
|
||||||
Gap prevT ->
|
|
||||||
stateAtEvent event prevT
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Count how many events the current timeline is storing.
|
|
||||||
-}
|
|
||||||
size : Timeline -> Int
|
|
||||||
size (Timeline t) =
|
|
||||||
(case t.previous of
|
|
||||||
Gap prev ->
|
|
||||||
size prev
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
0
|
|
||||||
)
|
|
||||||
+ List.length t.events
|
|
||||||
|
|
Loading…
Reference in New Issue