Add toUpdate function for sync V4

pull/31/head
Bram 2024-07-09 16:48:53 +02:00
parent c5d07f0a94
commit 632158f309
5 changed files with 227 additions and 11 deletions

View File

@ -967,6 +967,7 @@ toUnsigned : UnsignedData -> Event.UnsignedData
toUnsigned u =
Event.UnsignedData
{ age = u.age
, membership = Nothing
, prevContent = Nothing
, redactedBecause = Nothing
, transactionId = u.transactionId

View File

@ -740,6 +740,7 @@ toUnsigned ev unsigned =
( Just e, Nothing ) ->
{ age = Nothing
, membership = Nothing
, prevContent = Nothing
, redactedBecause = Just e
, transactionId = Nothing
@ -749,6 +750,7 @@ toUnsigned ev unsigned =
( _, Just (UnsignedData u) ) ->
{ age = u.age
, membership = Nothing
, prevContent = u.prevContent
, redactedBecause = ev
, transactionId = u.transactionId

View File

@ -11,9 +11,19 @@ This API module represents the /sync endpoint on Matrix spec version v1.11.
-}
import FastDict exposing (Dict)
import FastDict as Dict exposing (Dict)
import Internal.Api.Sync.V3 as PV
import Internal.Config.Log exposing (Log, log)
import Internal.Config.Text as Text
import Internal.Filter.Timeline exposing (Filter)
import Internal.Tools.Json as Json
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
import Internal.Values.Envelope as E
import Internal.Values.Event as Event
import Internal.Values.Room as R
import Internal.Values.User as User exposing (User)
import Internal.Values.Vault as V
import Recursion
type alias SyncResponse =
@ -60,7 +70,7 @@ type alias InviteState =
type alias StrippedStateEvent =
{ content : Json.Value
, sender : String
, sender : User
, stateKey : String
, eventType : String
}
@ -88,8 +98,8 @@ type alias State =
type alias ClientEventWithoutRoomID =
{ content : Json.Value
, eventId : String
, originServerTs : Int
, sender : String
, originServerTs : Timestamp
, sender : User
, stateKey : Maybe String
, eventType : String
, unsigned : Maybe UnsignedData
@ -159,7 +169,7 @@ type alias ToDevice =
type alias ToDeviceEvent =
{ content : Maybe Json.Value
, sender : Maybe String
, sender : Maybe User
, eventType : Maybe String
}
@ -371,7 +381,7 @@ coderStrippedStateEvent =
{ fieldName = "sender"
, toField = .sender
, description = [ "The sender for the event." ]
, coder = Json.string
, coder = User.coder
}
)
(Json.field.required
@ -505,14 +515,14 @@ coderClientEventWithoutRoomID =
{ fieldName = "origin_server_ts"
, toField = .originServerTs
, description = [ "Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent." ]
, coder = Json.int
, coder = Timestamp.coder
}
)
(Json.field.required
{ fieldName = "sender"
, toField = .sender
, description = [ "Contains the fully-qualified ID of the user who sent this event." ]
, coder = Json.string
, coder = User.coder
}
)
(Json.field.optional.value
@ -815,7 +825,7 @@ coderToDeviceEvent =
{ fieldName = "sender"
, toField = .sender
, description = [ "The Matrix user ID of the user who sent this event." ]
, coder = Json.string
, coder = User.coder
}
)
(Json.field.optional.value
@ -825,3 +835,194 @@ coderToDeviceEvent =
, coder = Json.string
}
)
updateSyncResponse : { filter : Filter, since : Maybe String } -> SyncResponse -> ( E.EnvelopeUpdate V.VaultUpdate, List Log )
updateSyncResponse { filter, since } response =
-- Account data
[ response.accountData
|> Maybe.andThen .events
|> Maybe.map (List.map (\e -> V.SetAccountData e.eventType e.content))
|> Maybe.map
(\x ->
( E.ContentUpdate <| V.More x
, if List.length x > 0 then
List.length x
|> Text.logs.syncAccountDataFound
|> log.debug
|> List.singleton
else
[]
)
)
-- TODO: Add device lists
-- Next batch
, Just ( E.SetNextBatch response.nextBatch, [] )
-- TODO: Add presence
-- Rooms
, Maybe.map
(updateRooms { filter = filter, nextBatch = response.nextBatch, since = since }
>> Tuple.mapFirst E.ContentUpdate
)
response.rooms
-- TODO: Add to_device
]
|> List.filterMap identity
|> List.unzip
|> Tuple.mapFirst E.More
|> Tuple.mapSecond List.concat
updateRooms : { filter : Filter, nextBatch : String, since : Maybe String } -> Rooms -> ( V.VaultUpdate, List Log )
updateRooms { filter, nextBatch, since } rooms =
let
( roomUpdate, roomLogs ) =
rooms.join
|> Maybe.withDefault Dict.empty
|> Dict.toList
|> List.map
(\( roomId, room ) ->
let
( u, l ) =
updateJoinedRoom
{ filter = filter
, nextBatch = nextBatch
, roomId = roomId
, since = since
}
room
in
( V.MapRoom roomId u, l )
)
|> List.unzip
|> Tuple.mapBoth V.More List.concat
in
( V.More
-- Add rooms
[ rooms.join
|> Maybe.withDefault Dict.empty
|> Dict.keys
|> List.map V.CreateRoomIfNotExists
|> V.More
-- Update rooms
, roomUpdate
-- TODO: Add invited rooms
-- TODO: Add knocked rooms
-- TODO: Add left rooms
]
, roomLogs
)
updateJoinedRoom : { filter : Filter, nextBatch : String, roomId : String, since : Maybe String } -> JoinedRoom -> ( R.RoomUpdate, List Log )
updateJoinedRoom data room =
( R.More
[ room.accountData
|> Maybe.andThen .events
|> Maybe.map
(\events ->
events
|> List.map (\e -> R.SetAccountData e.eventType e.content)
|> R.More
)
|> R.Optional
, room.ephemeral
|> Maybe.andThen .events
|> Maybe.map R.SetEphemeral
|> R.Optional
-- TODO: Add state
-- TODO: Add RoomSummary
, room.timeline
|> Maybe.map (updateTimeline data)
|> R.Optional
-- TODO: Add unread notifications
-- TODO: Add unread thread notifications
]
, []
)
updateTimeline : { filter : Filter, nextBatch : String, roomId : String, since : Maybe String } -> Timeline -> R.RoomUpdate
updateTimeline { filter, nextBatch, roomId, since } timeline =
R.AddSync
{ events = List.map (toEvent roomId) timeline.events
, filter = filter
, start =
case timeline.prevBatch of
Just _ ->
timeline.prevBatch
Nothing ->
since
, end = nextBatch
}
toEvent : String -> ClientEventWithoutRoomID -> Event.Event
toEvent roomId event =
Recursion.runRecursion
(\ev ->
case Maybe.andThen (\(UnsignedData u) -> u.redactedBecause) ev.unsigned of
Just e ->
Recursion.recurseThen e
(\eo ->
Recursion.base
{ content = ev.content
, eventId = ev.eventId
, originServerTs = ev.originServerTs
, roomId = roomId
, sender = ev.sender
, stateKey = ev.stateKey
, eventType = ev.eventType
, unsigned = toUnsigned (Just eo) ev.unsigned
}
)
Nothing ->
Recursion.base
{ content = ev.content
, eventId = ev.eventId
, originServerTs = ev.originServerTs
, roomId = roomId
, sender = ev.sender
, stateKey = ev.stateKey
, eventType = ev.eventType
, unsigned = toUnsigned Nothing ev.unsigned
}
)
event
toUnsigned : Maybe Event.Event -> Maybe UnsignedData -> Maybe Event.UnsignedData
toUnsigned ev unsigned =
case ( ev, unsigned ) of
( Nothing, Nothing ) ->
Nothing
( Just e, Nothing ) ->
{ age = Nothing
, membership = Nothing
, prevContent = Nothing
, redactedBecause = Just e
, transactionId = Nothing
}
|> Event.UnsignedData
|> Just
( _, Just (UnsignedData u) ) ->
{ age = u.age
, membership = Nothing
, prevContent = u.prevContent
, redactedBecause = ev
, transactionId = u.transactionId
}
|> Event.UnsignedData
|> Just

View File

@ -348,6 +348,7 @@ fields :
}
, unsigned :
{ age : Desc
, membership : Desc
, prevContent : Desc
, redactedBecause : Desc
, transactionId : Desc
@ -563,6 +564,9 @@ fields =
{ age =
[ "The time in milliseconds that has elapsed since the event was sent. This field is generated by the local homeserver, and may be incorrect if the local time on at least one of the two servers is out of sync, which can cause the age to either be negative or greater than it actually is."
]
, membership =
[ "The room membership of the user making the request, at the time of the event."
]
, prevContent =
[ "The previous content for this event. This field is generated by the local homeserver, and is only returned if the event is a state event, and the client has permission to see the previous content."
]

View File

@ -59,6 +59,7 @@ helper functions.
type UnsignedData
= UnsignedData
{ age : Maybe Int
, membership : Maybe String
, prevContent : Maybe Json.Value
, redactedBecause : Maybe Event
, transactionId : Maybe String
@ -242,10 +243,10 @@ transactionId event =
unsignedCoder : Json.Coder UnsignedData
unsignedCoder =
Json.object4
Json.object5
{ name = Text.docs.unsigned.name
, description = Text.docs.unsigned.description
, init = \a b c d -> UnsignedData { age = a, prevContent = b, redactedBecause = c, transactionId = d }
, init = \a b c d e -> UnsignedData { age = a, membership = b, prevContent = c, redactedBecause = d, transactionId = e }
}
(Json.field.optional.value
{ fieldName = "age"
@ -254,6 +255,13 @@ unsignedCoder =
, coder = Json.int
}
)
(Json.field.optional.value
{ fieldName = "membership"
, toField = \(UnsignedData data) -> data.membership
, description = Text.fields.unsigned.membership
, coder = Json.string
}
)
(Json.field.optional.value
{ fieldName = "prevContent"
, toField = \(UnsignedData data) -> data.prevContent