Compare commits
No commits in common. "10c7075bef7a7c2a33679d33a9730620da0a917f" and "2d26e1826df7c05244defee21518a03ed97d0d06" have entirely different histories.
10c7075bef
...
2d26e1826d
21
elm.json
21
elm.json
|
@ -26,7 +26,26 @@
|
||||||
"Internal.Values.Vault",
|
"Internal.Values.Vault",
|
||||||
"Matrix",
|
"Matrix",
|
||||||
"Matrix.Event",
|
"Matrix.Event",
|
||||||
"Matrix.Settings"
|
"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"
|
||||||
],
|
],
|
||||||
"elm-version": "0.19.0 <= v < 0.20.0",
|
"elm-version": "0.19.0 <= v < 0.20.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -35,7 +35,6 @@ of a room.
|
||||||
import Internal.Config.Text as Text
|
import Internal.Config.Text as Text
|
||||||
import Internal.Tools.Json as Json
|
import Internal.Tools.Json as Json
|
||||||
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
|
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
|
||||||
import Json.Encode as E
|
|
||||||
|
|
||||||
|
|
||||||
{-| The Event type occurs everywhere on a user's timeline.
|
{-| The Event type occurs everywhere on a user's timeline.
|
||||||
|
@ -214,6 +213,65 @@ isEqual e1 e2 =
|
||||||
isEqual se1 se2
|
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
|
{-| 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
|
`Just value` if the event is a state event, and the Matrix Vault has permission
|
||||||
to see the previous content.
|
to see the previous content.
|
||||||
|
|
|
@ -2,7 +2,7 @@ module Internal.Values.Timeline exposing
|
||||||
( Batch, Timeline
|
( Batch, Timeline
|
||||||
, empty, singleton
|
, empty, singleton
|
||||||
, mostRecentEvents
|
, mostRecentEvents
|
||||||
, insert
|
, addSync, insert
|
||||||
, encode, decoder
|
, encode, decoder
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,29 +16,6 @@ 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
|
by the Matrix client. This module exposes a data type that helps explore, track
|
||||||
and maintain this room state.
|
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
|
## Batch
|
||||||
|
|
||||||
|
@ -70,11 +47,8 @@ import FastDict as Dict exposing (Dict)
|
||||||
import Internal.Filter.Timeline as Filter exposing (Filter)
|
import Internal.Filter.Timeline as Filter exposing (Filter)
|
||||||
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
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.Json as Json
|
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
import Json.Encode as E
|
import Json.Encode as E
|
||||||
import Recursion
|
|
||||||
import Recursion.Traverse
|
|
||||||
import Set exposing (Set)
|
import Set exposing (Set)
|
||||||
|
|
||||||
|
|
||||||
|
@ -155,7 +129,7 @@ type Timeline
|
||||||
{ batches : Iddict IBatch
|
{ batches : Iddict IBatch
|
||||||
, events : Dict String ( IBatchPTR, List IBatchPTR )
|
, events : Dict String ( IBatchPTR, List IBatchPTR )
|
||||||
, filledBatches : Int
|
, filledBatches : Int
|
||||||
, mostRecentBatch : ITokenPTR
|
, mostRecentSync : ITokenPTR
|
||||||
, tokens : Hashdict IToken
|
, tokens : Hashdict IToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +140,22 @@ type alias TokenValue =
|
||||||
String
|
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.
|
{-| Append a token at the end of a batch.
|
||||||
-}
|
-}
|
||||||
connectIBatchToIToken : IBatchPTR -> ITokenPTR -> Timeline -> Timeline
|
connectIBatchToIToken : IBatchPTR -> ITokenPTR -> Timeline -> Timeline
|
||||||
|
@ -246,11 +236,158 @@ empty =
|
||||||
{ batches = Iddict.empty
|
{ batches = Iddict.empty
|
||||||
, events = Dict.empty
|
, events = Dict.empty
|
||||||
, filledBatches = 0
|
, filledBatches = 0
|
||||||
, mostRecentBatch = StartOfTimeline
|
, mostRecentSync = StartOfTimeline
|
||||||
, tokens = Hashdict.empty .name
|
, 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.
|
{-| Get an IBatch from the Timeline.
|
||||||
-}
|
-}
|
||||||
getIBatch : IBatchPTR -> Timeline -> Maybe IBatch
|
getIBatch : IBatchPTR -> Timeline -> Maybe IBatch
|
||||||
|
@ -379,47 +516,9 @@ invokeIToken value (Timeline tl) =
|
||||||
|
|
||||||
{-| Under a given filter, find the most recent events.
|
{-| Under a given filter, find the most recent events.
|
||||||
-}
|
-}
|
||||||
mostRecentEvents : Filter -> Timeline -> List (List String)
|
mostRecentEvents : Filter -> Timeline -> List String
|
||||||
mostRecentEvents filter (Timeline timeline) =
|
mostRecentEvents _ _ =
|
||||||
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
|
{-| Recount the Timeline's amount of filled batches. Since the Timeline
|
||||||
|
|
Loading…
Reference in New Issue