Compare commits
3 Commits
2d26e1826d
...
10c7075bef
Author | SHA1 | Date |
---|---|---|
Bram | 10c7075bef | |
Bram van den Heuvel | cf28a3f210 | |
Bram | cd8163bb41 |
21
elm.json
21
elm.json
|
@ -26,26 +26,7 @@
|
|||
"Internal.Values.Vault",
|
||||
"Matrix",
|
||||
"Matrix.Event",
|
||||
"Matrix.Settings",
|
||||
"Internal.Config.Default",
|
||||
"Internal.Config.Leaks",
|
||||
"Internal.Config.Text",
|
||||
"Internal.Filter.Timeline",
|
||||
"Internal.Tools.Decode",
|
||||
"Internal.Tools.Encode",
|
||||
"Internal.Tools.Hashdict",
|
||||
"Internal.Tools.Iddict",
|
||||
"Internal.Tools.Mashdict",
|
||||
"Internal.Tools.Timestamp",
|
||||
"Internal.Tools.VersionControl",
|
||||
"Internal.Values.Context",
|
||||
"Internal.Values.Envelope",
|
||||
"Internal.Values.Event",
|
||||
"Internal.Values.Settings",
|
||||
"Internal.Values.StateManager",
|
||||
"Internal.Values.Timeline",
|
||||
"Internal.Values.Vault",
|
||||
"Types"
|
||||
"Matrix.Settings"
|
||||
],
|
||||
"elm-version": "0.19.0 <= v < 0.20.0",
|
||||
"dependencies": {
|
||||
|
|
|
@ -35,6 +35,7 @@ of a room.
|
|||
import Internal.Config.Text as Text
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
|
||||
import Json.Encode as E
|
||||
|
||||
|
||||
{-| The Event type occurs everywhere on a user's timeline.
|
||||
|
@ -213,65 +214,6 @@ isEqual e1 e2 =
|
|||
isEqual se1 se2
|
||||
|
||||
|
||||
{-| Compare two events and determine whether they're identical. Used mostly for
|
||||
testing purposes.
|
||||
-}
|
||||
isEqual : Event -> Event -> Bool
|
||||
isEqual e1 e2 =
|
||||
if e1.eventId /= e2.eventId then
|
||||
False
|
||||
|
||||
else if e1.originServerTs /= e2.originServerTs then
|
||||
False
|
||||
|
||||
else if e1.roomId /= e2.roomId then
|
||||
False
|
||||
|
||||
else if e1.sender /= e2.sender then
|
||||
False
|
||||
|
||||
else if e1.stateKey /= e2.stateKey then
|
||||
False
|
||||
|
||||
else if e1.eventType /= e2.eventType then
|
||||
False
|
||||
|
||||
else
|
||||
case ( e1.unsigned, e2.unsigned ) of
|
||||
( Nothing, Nothing ) ->
|
||||
True
|
||||
|
||||
( Just _, Nothing ) ->
|
||||
False
|
||||
|
||||
( Nothing, Just _ ) ->
|
||||
False
|
||||
|
||||
( Just (UnsignedData d1), Just (UnsignedData d2) ) ->
|
||||
if d1.age /= d2.age then
|
||||
False
|
||||
|
||||
else if d1.transactionId /= d2.transactionId then
|
||||
False
|
||||
|
||||
else if Maybe.map (E.encode 0) d1.prevContent /= Maybe.map (E.encode 0) d2.prevContent then
|
||||
False
|
||||
|
||||
else
|
||||
case ( d1.redactedBecause, d2.redactedBecause ) of
|
||||
( Nothing, Nothing ) ->
|
||||
True
|
||||
|
||||
( Nothing, Just _ ) ->
|
||||
False
|
||||
|
||||
( Just _, Nothing ) ->
|
||||
False
|
||||
|
||||
( Just se1, Just se2 ) ->
|
||||
isEqual se1 se2
|
||||
|
||||
|
||||
{-| Determine the previous `content` value for this event. This field is only a
|
||||
`Just value` if the event is a state event, and the Matrix Vault has permission
|
||||
to see the previous content.
|
||||
|
|
|
@ -2,7 +2,7 @@ module Internal.Values.Timeline exposing
|
|||
( Batch, Timeline
|
||||
, empty, singleton
|
||||
, mostRecentEvents
|
||||
, addSync, insert
|
||||
, insert
|
||||
, encode, decoder
|
||||
)
|
||||
|
||||
|
@ -16,6 +16,29 @@ timeline is quite a complex data type, as it is constantly only partially known
|
|||
by the Matrix client. This module exposes a data type that helps explore, track
|
||||
and maintain this room state.
|
||||
|
||||
This design of the timeline uses the batches as waypoints to maintain an order.
|
||||
The Matrix API often returns batches that have the following four pieces of
|
||||
information:
|
||||
|
||||
1. A list of events.
|
||||
2. A filter for which all of the events meet the criteria.
|
||||
3. An end batch token.
|
||||
4. _(Optional)_ A start batch token. If it is not provided, it is the start of
|
||||
the timeline.
|
||||
|
||||
Here's an example of such a timeline batch:
|
||||
|
||||
|-->[■]->[■]->[●]->[■]->[■]->[●]-->|
|
||||
| |
|
||||
|<-- filter: only ■ and ●, no ★ -->|
|
||||
| |
|
||||
start: end:
|
||||
<token_1> <token_2>
|
||||
|
||||
When the Matrix API later returns a batch token that starts with `<token_2>`,
|
||||
we know that we can connect it to the batch above and make a longer list of
|
||||
events!
|
||||
|
||||
|
||||
## Batch
|
||||
|
||||
|
@ -47,8 +70,11 @@ import FastDict as Dict exposing (Dict)
|
|||
import Internal.Filter.Timeline as Filter exposing (Filter)
|
||||
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
||||
import Internal.Tools.Iddict as Iddict exposing (Iddict)
|
||||
import Internal.Tools.Json as Json
|
||||
import Json.Decode as D
|
||||
import Json.Encode as E
|
||||
import Recursion
|
||||
import Recursion.Traverse
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
|
@ -129,7 +155,7 @@ type Timeline
|
|||
{ batches : Iddict IBatch
|
||||
, events : Dict String ( IBatchPTR, List IBatchPTR )
|
||||
, filledBatches : Int
|
||||
, mostRecentSync : ITokenPTR
|
||||
, mostRecentBatch : ITokenPTR
|
||||
, tokens : Hashdict IToken
|
||||
}
|
||||
|
||||
|
@ -140,22 +166,6 @@ type alias TokenValue =
|
|||
String
|
||||
|
||||
|
||||
{-| When syncing a Matrix room to its most recent state, add the most recent
|
||||
batch to the front of the Timeline.
|
||||
-}
|
||||
addSync : Batch -> Timeline -> Timeline
|
||||
addSync batch timeline =
|
||||
case insertBatch batch timeline of
|
||||
( Timeline tl, { start, end } ) ->
|
||||
let
|
||||
oldSync : ITokenPTR
|
||||
oldSync =
|
||||
tl.mostRecentSync
|
||||
in
|
||||
Timeline { tl | mostRecentSync = end }
|
||||
|> connectITokenToIToken oldSync start
|
||||
|
||||
|
||||
{-| Append a token at the end of a batch.
|
||||
-}
|
||||
connectIBatchToIToken : IBatchPTR -> ITokenPTR -> Timeline -> Timeline
|
||||
|
@ -236,158 +246,11 @@ empty =
|
|||
{ batches = Iddict.empty
|
||||
, events = Dict.empty
|
||||
, filledBatches = 0
|
||||
, mostRecentSync = StartOfTimeline
|
||||
, mostRecentBatch = StartOfTimeline
|
||||
, tokens = Hashdict.empty .name
|
||||
}
|
||||
|
||||
|
||||
{-| Decode a Timeline from a JSON value.
|
||||
-}
|
||||
decoder : D.Decoder Timeline
|
||||
decoder =
|
||||
D.map5
|
||||
(\batches events filled sync tokens ->
|
||||
Timeline
|
||||
{ batches = batches
|
||||
, events = events
|
||||
, filledBatches = filled
|
||||
, mostRecentSync = sync
|
||||
, tokens = tokens
|
||||
}
|
||||
)
|
||||
(D.field "batches" <| Iddict.decoder decoderIBatch)
|
||||
(D.map2 Tuple.pair
|
||||
(D.field "head" decoderIBatchPTR)
|
||||
(D.field "tail" <| D.list decoderIBatchPTR)
|
||||
|> D.keyValuePairs
|
||||
|> D.map Dict.fromList
|
||||
|> D.field "events"
|
||||
)
|
||||
(D.succeed 0)
|
||||
(D.field "mostRecentSync" decoderITokenPTR)
|
||||
(D.field "tokens" <| Hashdict.decoder .name decoderIToken)
|
||||
|> D.map recountFilledBatches
|
||||
|
||||
|
||||
decoderIBatch : D.Decoder IBatch
|
||||
decoderIBatch =
|
||||
D.map4 IBatch
|
||||
(D.field "events" <| D.list D.string)
|
||||
(D.field "filter" Filter.decoder)
|
||||
(D.field "start" decoderITokenPTR)
|
||||
(D.field "end" decoderITokenPTR)
|
||||
|
||||
|
||||
decoderIBatchPTR : D.Decoder IBatchPTR
|
||||
decoderIBatchPTR =
|
||||
D.map IBatchPTR decoderIBatchPTRValue
|
||||
|
||||
|
||||
decoderIBatchPTRValue : D.Decoder IBatchPTRValue
|
||||
decoderIBatchPTRValue =
|
||||
D.int
|
||||
|
||||
|
||||
decoderIToken : D.Decoder IToken
|
||||
decoderIToken =
|
||||
D.map5 IToken
|
||||
(D.field "name" decoderTokenValue)
|
||||
(D.field "starts" <| D.map Set.fromList <| D.list decoderIBatchPTRValue)
|
||||
(D.field "ends" <| D.map Set.fromList <| D.list decoderIBatchPTRValue)
|
||||
(D.field "inFrontOf" <| D.map Set.fromList <| D.list decoderITokenPTRValue)
|
||||
(D.field "behind" <| D.map Set.fromList <| D.list decoderITokenPTRValue)
|
||||
|
||||
|
||||
decoderITokenPTR : D.Decoder ITokenPTR
|
||||
decoderITokenPTR =
|
||||
D.oneOf
|
||||
[ D.map ITokenPTR decoderITokenPTRValue
|
||||
, D.null StartOfTimeline
|
||||
]
|
||||
|
||||
|
||||
decoderITokenPTRValue : D.Decoder ITokenPTRValue
|
||||
decoderITokenPTRValue =
|
||||
D.string
|
||||
|
||||
|
||||
decoderTokenValue : D.Decoder TokenValue
|
||||
decoderTokenValue =
|
||||
D.string
|
||||
|
||||
|
||||
{-| Encode a Timeline to a JSON value.
|
||||
-}
|
||||
encode : Timeline -> E.Value
|
||||
encode (Timeline tl) =
|
||||
E.object
|
||||
[ ( "batches", Iddict.encode encodeIBatch tl.batches )
|
||||
, ( "events"
|
||||
, E.dict identity
|
||||
(\( head, tail ) ->
|
||||
E.object
|
||||
[ ( "head", encodeIBatchPTR head )
|
||||
, ( "tail", E.list encodeIBatchPTR tail )
|
||||
]
|
||||
)
|
||||
(Dict.toCoreDict tl.events)
|
||||
)
|
||||
, ( "mostRecentSync", encodeITokenPTR tl.mostRecentSync )
|
||||
, ( "tokens", Hashdict.encode encodeIToken tl.tokens )
|
||||
]
|
||||
|
||||
|
||||
encodeIBatch : IBatch -> E.Value
|
||||
encodeIBatch batch =
|
||||
E.object
|
||||
[ ( "events", E.list E.string batch.events )
|
||||
, ( "filter", Filter.encode batch.filter )
|
||||
, ( "start", encodeITokenPTR batch.start )
|
||||
, ( "end", encodeITokenPTR batch.end )
|
||||
]
|
||||
|
||||
|
||||
encodeIBatchPTR : IBatchPTR -> E.Value
|
||||
encodeIBatchPTR (IBatchPTR value) =
|
||||
encodeIBatchPTRValue value
|
||||
|
||||
|
||||
encodeIBatchPTRValue : IBatchPTRValue -> E.Value
|
||||
encodeIBatchPTRValue =
|
||||
E.int
|
||||
|
||||
|
||||
encodeIToken : IToken -> E.Value
|
||||
encodeIToken itoken =
|
||||
E.object
|
||||
[ ( "name", encodeTokenValue itoken.name )
|
||||
, ( "starts", E.set encodeIBatchPTRValue itoken.starts )
|
||||
, ( "ends", E.set encodeIBatchPTRValue itoken.ends )
|
||||
, ( "inFrontOf", E.set encodeITokenPTRValue itoken.inFrontOf )
|
||||
, ( "behind", E.set encodeITokenPTRValue itoken.behind )
|
||||
]
|
||||
|
||||
|
||||
encodeITokenPTR : ITokenPTR -> E.Value
|
||||
encodeITokenPTR token =
|
||||
case token of
|
||||
ITokenPTR value ->
|
||||
encodeITokenPTRValue value
|
||||
|
||||
StartOfTimeline ->
|
||||
E.null
|
||||
|
||||
|
||||
encodeITokenPTRValue : ITokenPTRValue -> E.Value
|
||||
encodeITokenPTRValue =
|
||||
E.string
|
||||
|
||||
|
||||
encodeTokenValue : TokenValue -> E.Value
|
||||
encodeTokenValue =
|
||||
E.string
|
||||
|
||||
|
||||
{-| Get an IBatch from the Timeline.
|
||||
-}
|
||||
getIBatch : IBatchPTR -> Timeline -> Maybe IBatch
|
||||
|
@ -516,9 +379,47 @@ invokeIToken value (Timeline tl) =
|
|||
|
||||
{-| Under a given filter, find the most recent events.
|
||||
-}
|
||||
mostRecentEvents : Filter -> Timeline -> List String
|
||||
mostRecentEvents _ _ =
|
||||
[]
|
||||
mostRecentEvents : Filter -> Timeline -> List (List String)
|
||||
mostRecentEvents filter (Timeline timeline) =
|
||||
mostRecentEventsFrom filter (Timeline timeline) timeline.mostRecentBatch
|
||||
|
||||
|
||||
{-| Under a given filter, starting from a given ITokenPTR, find the most recent
|
||||
events.
|
||||
-}
|
||||
mostRecentEventsFrom : Filter -> Timeline -> ITokenPTR -> List (List String)
|
||||
mostRecentEventsFrom filter timeline ptr =
|
||||
Recursion.runRecursion
|
||||
(\p ->
|
||||
case getITokenFromPTR p.ptr timeline of
|
||||
Nothing ->
|
||||
Recursion.base []
|
||||
|
||||
Just token ->
|
||||
if Set.member token.name p.visited then
|
||||
Recursion.base []
|
||||
|
||||
else
|
||||
token.ends
|
||||
|> Set.toList
|
||||
|> List.filterMap (\bptrv -> getIBatch (IBatchPTR bptrv) timeline)
|
||||
|> List.filter (\ibatch -> Filter.subsetOf ibatch.filter filter)
|
||||
|> Recursion.Traverse.traverseList
|
||||
(\ibatch ->
|
||||
Recursion.recurseThen
|
||||
{ ptr = ibatch.start, visited = Set.insert token.name p.visited }
|
||||
(\optionalTimelines ->
|
||||
optionalTimelines
|
||||
|> List.map
|
||||
(\outTimeline ->
|
||||
List.append outTimeline ibatch.events
|
||||
)
|
||||
|> Recursion.base
|
||||
)
|
||||
)
|
||||
|> Recursion.map List.concat
|
||||
)
|
||||
{ ptr = ptr, visited = Set.empty }
|
||||
|
||||
|
||||
{-| Recount the Timeline's amount of filled batches. Since the Timeline
|
||||
|
|
Loading…
Reference in New Issue