Compare commits
13 Commits
7b00a46ffa
...
b239eecc6b
Author | SHA1 | Date |
---|---|---|
Bram | b239eecc6b | |
Bram | e7d3a129b1 | |
Bram | e122a7b262 | |
Bram | 29906ff976 | |
Bram | 80bb05fd30 | |
Bram | 1d0a9de7da | |
Bram | 95f0aa2934 | |
Bram | 7ab21b4314 | |
Bram | 0092f96a25 | |
Bram | 693124aa15 | |
Bram | 6783186c18 | |
Bram | 0ded7ab6bd | |
Bram | f6a6bb535e |
|
@ -0,0 +1,94 @@
|
|||
module Internal.Api.Sync.Api exposing (..)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Sync
|
||||
|
||||
The sync module might be one of the most crucial parts of the Elm SDK. It offers
|
||||
users the guarantee that the `Vault` type remains up-to-date, and it helps
|
||||
communicate with the Matrix server about the Vault's needs.
|
||||
|
||||
@docs Phantom
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Api.Api as A
|
||||
import Internal.Api.Request as R
|
||||
import Internal.Api.Sync.V1 as V1
|
||||
import Internal.Filter.Timeline as Filter
|
||||
|
||||
|
||||
|
||||
-- For simplicity, we will not use a filter for now
|
||||
-- and assume that every client always wants to receive all events.
|
||||
-- type FilterV1
|
||||
-- = FilterV1 Filter
|
||||
-- | FilterIdV1 String Filter
|
||||
-- | NoFilter
|
||||
|
||||
|
||||
type alias Phantom a =
|
||||
{ a | accessToken : (), baseUrl : (), versions : () }
|
||||
|
||||
|
||||
type alias PhantomV1 a =
|
||||
{ a | accessToken : (), baseUrl : () }
|
||||
|
||||
|
||||
type PresenceV1
|
||||
= OfflineV1
|
||||
|
||||
|
||||
type alias SyncInput =
|
||||
{ -- filter : FilterV1,
|
||||
fullState : Maybe Bool
|
||||
, presenceV1 : Maybe PresenceV1
|
||||
, since : Maybe String
|
||||
, timeout : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias SyncInputV1 a =
|
||||
{ a
|
||||
| -- filter : FilterV1 ,
|
||||
since : Maybe String
|
||||
, fullState : Maybe Bool
|
||||
, presenceV1 : Maybe PresenceV1
|
||||
, timeout : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
sync : SyncInput -> A.TaskChain (Phantom a) (Phantom a)
|
||||
sync =
|
||||
A.startWithVersion "r0.0.0" syncV1
|
||||
|> A.versionChain
|
||||
|
||||
|
||||
syncV1 : SyncInputV1 i -> A.TaskChain (PhantomV1 a) (PhantomV1 a)
|
||||
syncV1 data =
|
||||
A.request
|
||||
{ attributes =
|
||||
[ R.accessToken
|
||||
, R.queryOpString "filter" Nothing -- FILTER HERE
|
||||
, R.queryOpString "since" data.since
|
||||
, R.queryOpBool "full_state" data.fullState
|
||||
, data.presenceV1
|
||||
|> Maybe.map (always "offline")
|
||||
|> R.queryOpString "set_presence"
|
||||
, R.queryOpInt "timeout" data.timeout
|
||||
]
|
||||
, coder = V1.syncResponseCoder
|
||||
, contextChange = always identity
|
||||
, method = "GET"
|
||||
, path = [ "_matrix", "client", "r0", "sync" ]
|
||||
, toUpdate =
|
||||
\out ->
|
||||
( V1.syncResponseToUpdate
|
||||
{ filter = Filter.pass -- FILTER HERE
|
||||
, since = data.since
|
||||
}
|
||||
out
|
||||
, []
|
||||
)
|
||||
}
|
|
@ -0,0 +1,895 @@
|
|||
module Internal.Api.Sync.V1 exposing (..)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Sync response
|
||||
|
||||
This API module represents the /sync endpoint on Matrix spec version v1.1.
|
||||
|
||||
<https://spec.matrix.org/v1.1/client-server-api/#syncing>
|
||||
|
||||
-}
|
||||
|
||||
import FastDict as Dict exposing (Dict)
|
||||
import Internal.Config.Log exposing (Log)
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Tools.StrippedEvent as StrippedEvent exposing (StrippedEvent)
|
||||
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
|
||||
import Internal.Values.Envelope as E
|
||||
import Internal.Values.Room as R
|
||||
import Internal.Values.Vault as V
|
||||
|
||||
|
||||
type alias SyncResponse =
|
||||
{ accountData : Maybe AccountData
|
||||
, deviceLists : Maybe DeviceLists
|
||||
, deviceOneTimeKeysCount : Maybe (Dict String Int)
|
||||
, nextBatch : String
|
||||
, presence : Maybe Presence
|
||||
, rooms : Maybe Rooms
|
||||
, toDevice : Maybe ToDevice
|
||||
}
|
||||
|
||||
|
||||
type alias AccountData =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Event =
|
||||
{ content : Json.Value
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias Presence =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Rooms =
|
||||
{ invite : Maybe (Dict String InvitedRoom)
|
||||
, join : Maybe (Dict String JoinedRoom)
|
||||
, knock : Maybe (Dict String KnockedRoom)
|
||||
, leave : Maybe (Dict String LeftRoom)
|
||||
}
|
||||
|
||||
|
||||
type alias InvitedRoom =
|
||||
{ inviteState : Maybe InviteState }
|
||||
|
||||
|
||||
type alias InviteState =
|
||||
{ events : Maybe (List StrippedState) }
|
||||
|
||||
|
||||
type alias StrippedState =
|
||||
{ content : Json.Value
|
||||
, sender : String
|
||||
, stateKey : String
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias JoinedRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, ephemeral : Maybe Ephemeral
|
||||
, state : Maybe State
|
||||
, summary : Maybe RoomSummary
|
||||
, timeline : Maybe Timeline
|
||||
, unreadNotifications : Maybe UnreadNotificationCounts
|
||||
}
|
||||
|
||||
|
||||
type alias Ephemeral =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias State =
|
||||
{ events : Maybe (List SyncStateEvent) }
|
||||
|
||||
|
||||
type alias SyncStateEvent =
|
||||
{ content : Json.Value
|
||||
, eventId : String
|
||||
, originServerTs : Timestamp
|
||||
, prevContent : Maybe Json.Value
|
||||
, sender : String
|
||||
, stateKey : String
|
||||
, eventType : String
|
||||
, unsigned : Maybe UnsignedData
|
||||
}
|
||||
|
||||
|
||||
type alias UnsignedData =
|
||||
{ age : Maybe Int
|
||||
, redactedBecause : Maybe Event
|
||||
, transactionId : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias RoomSummary =
|
||||
{ mHeroes : Maybe (List String)
|
||||
, mInvitedMemberCount : Maybe Int
|
||||
, mJoinedMemberCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias Timeline =
|
||||
{ events : Maybe (List SyncRoomEvent)
|
||||
, limited : Maybe Bool
|
||||
, prevBatch : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias SyncRoomEvent =
|
||||
{ content : Json.Value
|
||||
, eventId : String
|
||||
, originServerTs : Timestamp
|
||||
, sender : String
|
||||
, eventType : String
|
||||
, unsigned : Maybe UnsignedData
|
||||
}
|
||||
|
||||
|
||||
type alias UnreadNotificationCounts =
|
||||
{ highlightCount : Maybe Int
|
||||
, notificationCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias KnockedRoom =
|
||||
{ knockState : Maybe KnockState }
|
||||
|
||||
|
||||
type alias KnockState =
|
||||
{ events : Maybe (List StrippedState) }
|
||||
|
||||
|
||||
type alias LeftRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, state : Maybe State
|
||||
, timeline : Maybe Timeline
|
||||
}
|
||||
|
||||
|
||||
type alias DeviceLists =
|
||||
{ changed : Maybe (List String)
|
||||
, left : Maybe (List String)
|
||||
}
|
||||
|
||||
|
||||
type alias ToDevice =
|
||||
{ events : Maybe (List ToDeviceEvent) }
|
||||
|
||||
|
||||
type alias ToDeviceEvent =
|
||||
{ content : Maybe Json.Value
|
||||
, sender : Maybe String
|
||||
, eventType : Maybe String
|
||||
}
|
||||
|
||||
|
||||
coderSyncResponse : Json.Coder SyncResponse
|
||||
coderSyncResponse =
|
||||
Json.object7
|
||||
{ name = "SyncResponse"
|
||||
, description = [ "The event that is returned on a 200 response." ]
|
||||
, init = SyncResponse
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The global private data created by this user." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_lists"
|
||||
, toField = .deviceLists
|
||||
, description = [ "Information on end-to-end device updates, as specified in End-to-end encryption." ]
|
||||
, coder = coderDeviceLists
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_one_time_keys_count"
|
||||
, toField = .deviceOneTimeKeysCount
|
||||
, description = [ "Information on end-to-end encryption keys, as specified in End-to-end encryption." ]
|
||||
, coder = Json.fastDict Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "next_batch"
|
||||
, toField = .nextBatch
|
||||
, description = [ "Required: The batch token to supply in the since param of the next /sync request." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "presence"
|
||||
, toField = .presence
|
||||
, description = [ "The updates to the presence status of other users." ]
|
||||
, coder = coderPresence
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "rooms"
|
||||
, toField = .rooms
|
||||
, description = [ "Updates to rooms." ]
|
||||
, coder = coderRooms
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "to_device"
|
||||
, toField = .toDevice
|
||||
, description = [ "Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging." ]
|
||||
, coder = coderToDevice
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderAccountData : Json.Coder AccountData
|
||||
coderAccountData =
|
||||
Json.object1
|
||||
{ name = "AccountData"
|
||||
, description = [ "The global private data created by this user." ]
|
||||
, init = AccountData
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderEvent : Json.Coder Event
|
||||
coderEvent =
|
||||
StrippedEvent.coder
|
||||
|
||||
|
||||
coderPresence : Json.Coder Presence
|
||||
coderPresence =
|
||||
Json.object1
|
||||
{ name = "Presence"
|
||||
, description = [ "The updates to the presence status of other users." ]
|
||||
, init = Presence
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderRooms : Json.Coder Rooms
|
||||
coderRooms =
|
||||
Json.object4
|
||||
{ name = "Rooms"
|
||||
, description = [ "Updates to rooms." ]
|
||||
, init = Rooms
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "invite"
|
||||
, toField = .invite
|
||||
, description = [ "The rooms that the user has been invited to, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderInvitedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "join"
|
||||
, toField = .join
|
||||
, description = [ "The rooms that the user has joined, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderJoinedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "knock"
|
||||
, toField = .knock
|
||||
, description = [ "The rooms that the user has knocked upon, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderKnockedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "leave"
|
||||
, toField = .leave
|
||||
, description = [ "The rooms that the user has left or been banned from, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderLeftRoom
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderInvitedRoom : Json.Coder InvitedRoom
|
||||
coderInvitedRoom =
|
||||
Json.object1
|
||||
{ name = "InvitedRoom"
|
||||
, description = [ "The rooms that the user has been invited to, mapped as room ID to room information." ]
|
||||
, init = InvitedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "invite_state"
|
||||
, toField = .inviteState
|
||||
, description = [ "The state of a room that the user has been invited to.", "These state events may only have the sender, type, state_key and content keys present.", "These events do not replace any state that the client already has for the room, for example if the client has archived the room.", "Instead the client should keep two separate copies of the state: the one from the invite_state and one from the archived state.", "If the client joins the room then the current state will be given as a delta against the archived state not the invite_state." ]
|
||||
, coder = coderInviteState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderInviteState : Json.Coder InviteState
|
||||
coderInviteState =
|
||||
Json.object1
|
||||
{ name = "InviteState"
|
||||
, description = [ "The state of a room that the user has been invited to." ]
|
||||
, init = InviteState
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "The StrippedState events that form the invite state." ]
|
||||
, coder = Json.list coderStrippedState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderStrippedState : Json.Coder StrippedState
|
||||
coderStrippedState =
|
||||
Json.object4
|
||||
{ name = "StrippedState"
|
||||
, description = [ "The StrippedState events that form the invite state." ]
|
||||
, init = StrippedState
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The content for the event." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description = [ "The sender for the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "state_key"
|
||||
, toField = .stateKey
|
||||
, description = [ "The state_key for the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type for the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderJoinedRoom : Json.Coder JoinedRoom
|
||||
coderJoinedRoom =
|
||||
Json.object6
|
||||
{ name = "JoinedRoom"
|
||||
, description = [ "The rooms that the user has joined, mapped as room ID to room information." ]
|
||||
, init = JoinedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The private data that this user has attached to this room." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "ephemeral"
|
||||
, toField = .ephemeral
|
||||
, description = [ "The ephemeral events in the room that aren’t recorded in the timeline or state of the room. e.g. typing." ]
|
||||
, coder = coderEphemeral
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state"
|
||||
, toField = .state
|
||||
, description = [ "Updates to the state, between the time indicated by the since parameter, and the start of the timeline (or all state up to the start of the timeline, if since is not given, or full_state is true).", "N.B. state updates for m.room.member events will be incomplete if lazy_load_members is enabled in the /sync filter, and only return the member events required to display the senders of the timeline events in this response." ]
|
||||
, coder = coderState
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "summary"
|
||||
, toField = .summary
|
||||
, description = [ "Information about the room which clients may need to correctly render it to users." ]
|
||||
, coder = coderRoomSummary
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "timeline"
|
||||
, toField = .timeline
|
||||
, description = [ "The timeline of messages and state changes in the room." ]
|
||||
, coder = coderTimeline
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unread_notifications"
|
||||
, toField = .unreadNotifications
|
||||
, description = [ "Counts of unread notifications for this room. See the Receiving notifications section for more information on how these are calculated." ]
|
||||
, coder = coderUnreadNotificationCounts
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderEphemeral : Json.Coder Ephemeral
|
||||
coderEphemeral =
|
||||
Json.object1
|
||||
{ name = "Ephemeral"
|
||||
, description = [ "The ephemeral events in the room that aren’t recorded in the timeline or state of the room. e.g. typing." ]
|
||||
, init = Ephemeral
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderState : Json.Coder State
|
||||
coderState =
|
||||
Json.object1
|
||||
{ name = "State"
|
||||
, description = [ "Updates to the state, between the time indicated by the since parameter, and the start of the timeline (or all state up to the start of the timeline, if since is not given, or full_state is true)." ]
|
||||
, init = State
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderSyncStateEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderSyncStateEvent : Json.Coder SyncStateEvent
|
||||
coderSyncStateEvent =
|
||||
Json.object8
|
||||
{ name = "SyncStateEvent"
|
||||
, description = [ "Represents a state event within a sync response." ]
|
||||
, init = SyncStateEvent
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "event_id"
|
||||
, toField = .eventId
|
||||
, description = [ "The globally unique event identifier." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "origin_server_ts"
|
||||
, toField = .originServerTs
|
||||
, description = [ "Timestamp in milliseconds on originating homeserver when this event was sent." ]
|
||||
, coder = Timestamp.coder
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "prev_content"
|
||||
, toField = .prevContent
|
||||
, description = [ "Optional. The previous content for this event. If there is no previous content, this key will be missing." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description = [ "Contains the fully-qualified ID of the user who sent this event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "state_key"
|
||||
, toField = .stateKey
|
||||
, description = [ "A unique key which defines the overwriting semantics for this piece of room state.", "This value is often a zero-length string. The presence of this key makes this event a State Event.", "State keys starting with an @ are reserved for referencing user IDs, such as room members.", "With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. ‘com.example.subdomain.event.type’" ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unsigned"
|
||||
, toField = .unsigned
|
||||
, description = [ "Contains optional extra information about the event." ]
|
||||
, coder = coderUnsignedData
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderUnsignedData : Json.Coder UnsignedData
|
||||
coderUnsignedData =
|
||||
Json.object3
|
||||
{ name = "UnsignedData"
|
||||
, description = [ "Contains optional extra information about the event." ]
|
||||
, init = UnsignedData
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "age"
|
||||
, toField = .age
|
||||
, description = [ "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." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "redacted_because"
|
||||
, toField = .redactedBecause
|
||||
, description = [ "The event that redacted this event, if any." ]
|
||||
, coder = coderEvent
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "transaction_id"
|
||||
, toField = .transactionId
|
||||
, description = [ "The client-supplied transaction ID, for example, provided via PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderRoomSummary : Json.Coder RoomSummary
|
||||
coderRoomSummary =
|
||||
Json.object3
|
||||
{ name = "RoomSummary"
|
||||
, description = [ "Information about the room which clients may need to correctly render it to users." ]
|
||||
, init = RoomSummary
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "m.heroes"
|
||||
, toField = .mHeroes
|
||||
, description = [ "The users which can be used to generate a room name if the room does not have one. Required if the room’s m.room.name or m.room.canonical_alias state events are unset or empty.", "This should be the first 5 members of the room, ordered by stream ordering, which are joined or invited.", "The list must never include the client’s own user ID.", "When no joined or invited members are available, this should consist of the banned and left users.", "More than 5 members may be provided, however less than 5 should only be provided when there are less than 5 members to represent.", "When lazy-loading room members is enabled, the membership events for the heroes MUST be included in the state, unless they are redundant.", "When the list of users changes, the server notifies the client by sending a fresh list of heroes.", "If there are no changes since the last sync, this field may be omitted." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "m.invited_member_count"
|
||||
, toField = .mInvitedMemberCount
|
||||
, description = [ "The number of users with membership of invite.", "If this field has not changed since the last sync, it may be omitted. Required otherwise." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "m.joined_member_count"
|
||||
, toField = .mJoinedMemberCount
|
||||
, description = [ "The number of users with membership of join, including the client’s own user ID.", "If this field has not changed since the last sync, it may be omitted. Required otherwise." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderTimeline : Json.Coder Timeline
|
||||
coderTimeline =
|
||||
Json.object3
|
||||
{ name = "Timeline"
|
||||
, description = [ "The timeline of messages and state changes in the room." ]
|
||||
, init = Timeline
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderSyncRoomEvent
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "limited"
|
||||
, toField = .limited
|
||||
, description = [ "True if the number of events returned was limited by the limit on the filter." ]
|
||||
, coder = Json.bool
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "prev_batch"
|
||||
, toField = .prevBatch
|
||||
, description = [ "A token that can be supplied to the from parameter of the /rooms/<room_id>/messages endpoint in order to retrieve earlier events.", "If no earlier events are available, this property may be omitted from the response." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderSyncRoomEvent : Json.Coder SyncRoomEvent
|
||||
coderSyncRoomEvent =
|
||||
Json.object6
|
||||
{ name = "SyncRoomEvent"
|
||||
, description = [ "Represents a room event within a sync response." ]
|
||||
, init = SyncRoomEvent
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "event_id"
|
||||
, toField = .eventId
|
||||
, description = [ "The globally unique event identifier." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "origin_server_ts"
|
||||
, toField = .originServerTs
|
||||
, description = [ "Timestamp in milliseconds on originating homeserver when this event was sent." ]
|
||||
, 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
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. ‘com.example.subdomain.event.type’" ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unsigned"
|
||||
, toField = .unsigned
|
||||
, description = [ "Contains optional extra information about the event." ]
|
||||
, coder = coderUnsignedData
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderUnreadNotificationCounts : Json.Coder UnreadNotificationCounts
|
||||
coderUnreadNotificationCounts =
|
||||
Json.object2
|
||||
{ name = "UnreadNotificationCounts"
|
||||
, description = [ "Counts of unread notifications for this room." ]
|
||||
, init = UnreadNotificationCounts
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "highlight_count"
|
||||
, toField = .highlightCount
|
||||
, description = [ "The number of unread notifications for this room with the highlight flag set." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "notification_count"
|
||||
, toField = .notificationCount
|
||||
, description = [ "The total number of unread notifications for this room." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderKnockedRoom : Json.Coder KnockedRoom
|
||||
coderKnockedRoom =
|
||||
Json.object1
|
||||
{ name = "KnockedRoom"
|
||||
, description = [ "The rooms that the user has knocked upon, mapped as room ID to room information." ]
|
||||
, init = KnockedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "knock_state"
|
||||
, toField = .knockState
|
||||
, description = [ "The state of a room that the user has knocked upon.", "The state events contained here have the same restrictions as InviteState above." ]
|
||||
, coder = coderKnockState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderKnockState : Json.Coder KnockState
|
||||
coderKnockState =
|
||||
Json.object1
|
||||
{ name = "KnockState"
|
||||
, description = [ "The state of a room that the user has knocked upon." ]
|
||||
, init = KnockState
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "The StrippedState events that form the knock state." ]
|
||||
, coder = Json.list coderStrippedState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderLeftRoom : Json.Coder LeftRoom
|
||||
coderLeftRoom =
|
||||
Json.object3
|
||||
{ name = "LeftRoom"
|
||||
, description = [ "The rooms that the user has left or been banned from, mapped as room ID to room information." ]
|
||||
, init = LeftRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The private data that this user has attached to this room." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state"
|
||||
, toField = .state
|
||||
, description = [ "The state updates for the room up to the start of the timeline." ]
|
||||
, coder = coderState
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "timeline"
|
||||
, toField = .timeline
|
||||
, description = [ "The timeline of messages and state changes in the room up to the point when the user left." ]
|
||||
, coder = coderTimeline
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderDeviceLists : Json.Coder DeviceLists
|
||||
coderDeviceLists =
|
||||
Json.object2
|
||||
{ name = "DeviceLists"
|
||||
, description = [ "Information on end-to-end device updates, as specified in End-to-end encryption." ]
|
||||
, init = DeviceLists
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "changed"
|
||||
, toField = .changed
|
||||
, description = [ "List of users who have updated their device identity or cross-signing keys, or who now share an encrypted room with the client since the previous sync response." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "left"
|
||||
, toField = .left
|
||||
, description = [ "List of users with whom we do not share any encrypted rooms anymore since the previous sync response." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderToDevice : Json.Coder ToDevice
|
||||
coderToDevice =
|
||||
Json.object1
|
||||
{ name = "ToDevice"
|
||||
, description = [ "Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging." ]
|
||||
, init = ToDevice
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of send-to-device messages." ]
|
||||
, coder = Json.list coderToDeviceEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderToDeviceEvent : Json.Coder ToDeviceEvent
|
||||
coderToDeviceEvent =
|
||||
Json.object3
|
||||
{ name = "ToDeviceEvent"
|
||||
, description = [ "An event." ]
|
||||
, init = ToDeviceEvent
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The content of this event. The fields in this object will vary depending on the type of event." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description = [ "The Matrix user ID of the user who sent this event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type of event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
updateSyncResponse : SyncResponse -> ( E.EnvelopeUpdate V.VaultUpdate, List Log )
|
||||
updateSyncResponse response =
|
||||
-- TODO: Add account data
|
||||
-- TODO: Add device lists
|
||||
-- Next batch
|
||||
[ Just ( E.SetNextBatch response.nextBatch, [] )
|
||||
|
||||
-- TODO: Add presence
|
||||
-- Rooms
|
||||
, Maybe.map (updateRooms >> Tuple.mapFirst E.ContentUpdate) response.rooms
|
||||
|
||||
-- TODO: Add to_device
|
||||
]
|
||||
|> List.filterMap identity
|
||||
|> List.unzip
|
||||
|> Tuple.mapFirst E.More
|
||||
|> Tuple.mapSecond List.concat
|
||||
|
||||
|
||||
updateRooms : Rooms -> ( V.VaultUpdate, List Log )
|
||||
updateRooms rooms =
|
||||
let
|
||||
( roomUpdate, roomLogs ) =
|
||||
rooms.join
|
||||
|> Maybe.withDefault Dict.empty
|
||||
|> Dict.toList
|
||||
|> List.map
|
||||
(\( roomId, room ) ->
|
||||
let
|
||||
( u, l ) =
|
||||
updateJoinedRoom 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 : JoinedRoom -> ( R.RoomUpdate, List Log )
|
||||
updateJoinedRoom 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
|
||||
-- TODO: Add timeline
|
||||
-- TODO: Add unread notifications
|
||||
]
|
||||
, []
|
||||
)
|
|
@ -0,0 +1,559 @@
|
|||
module Internal.Api.Sync.V2 exposing (..)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Sync response
|
||||
|
||||
This API module represents the /sync endpoint on Matrix spec version v1.2 and
|
||||
v1.3.
|
||||
|
||||
<https://spec.matrix.org/v1.2/client-server-api/#syncing>
|
||||
<https://spec.matrix.org/v1.3/client-server-api/#syncing>
|
||||
|
||||
-}
|
||||
|
||||
import FastDict exposing (Dict)
|
||||
import Internal.Api.Sync.V1 as PV
|
||||
import Internal.Tools.Json as Json
|
||||
|
||||
|
||||
type alias SyncResponse =
|
||||
{ accountData : Maybe AccountData
|
||||
, deviceLists : Maybe DeviceLists
|
||||
, deviceOneTimeKeysCount : Maybe (Dict String Int)
|
||||
, deviceUnusedFallbackKeyTypes : List String
|
||||
, nextBatch : String
|
||||
, presence : Maybe Presence
|
||||
, rooms : Maybe Rooms
|
||||
, toDevice : Maybe ToDevice
|
||||
}
|
||||
|
||||
|
||||
type alias AccountData =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Event =
|
||||
{ content : Json.Value
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias Presence =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Rooms =
|
||||
{ invite : Maybe (Dict String InvitedRoom)
|
||||
, join : Maybe (Dict String JoinedRoom)
|
||||
, knock : Maybe (Dict String KnockedRoom)
|
||||
, leave : Maybe (Dict String LeftRoom)
|
||||
}
|
||||
|
||||
|
||||
type alias InvitedRoom =
|
||||
{ inviteState : Maybe InviteState }
|
||||
|
||||
|
||||
type alias InviteState =
|
||||
{ events : Maybe (List StrippedStateEvent) }
|
||||
|
||||
|
||||
type alias StrippedStateEvent =
|
||||
{ content : Json.Value
|
||||
, sender : String
|
||||
, stateKey : String
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias JoinedRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, ephemeral : Maybe Ephemeral
|
||||
, state : Maybe State
|
||||
, summary : Maybe RoomSummary
|
||||
, timeline : Maybe Timeline
|
||||
, unreadNotifications : Maybe UnreadNotificationCounts
|
||||
}
|
||||
|
||||
|
||||
type alias Ephemeral =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias State =
|
||||
{ events : Maybe (List ClientEventWithoutRoomID) }
|
||||
|
||||
|
||||
type alias ClientEventWithoutRoomID =
|
||||
{ content : Json.Value
|
||||
, eventId : String
|
||||
, originServerTs : Int
|
||||
, sender : String
|
||||
, stateKey : Maybe String
|
||||
, eventType : String
|
||||
, unsigned : Maybe UnsignedData
|
||||
}
|
||||
|
||||
|
||||
type UnsignedData
|
||||
= UnsignedData
|
||||
{ age : Maybe Int
|
||||
, prevContent : Maybe Json.Value
|
||||
, redactedBecause : Maybe ClientEventWithoutRoomID
|
||||
, transactionId : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias RoomSummary =
|
||||
{ mHeroes : Maybe (List String)
|
||||
, mInvitedMemberCount : Maybe Int
|
||||
, mJoinedMemberCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias Timeline =
|
||||
{ events : List ClientEventWithoutRoomID
|
||||
, limited : Maybe Bool
|
||||
, prevBatch : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias UnreadNotificationCounts =
|
||||
{ highlightCount : Maybe Int
|
||||
, notificationCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias KnockedRoom =
|
||||
{ knockState : Maybe KnockState }
|
||||
|
||||
|
||||
type alias KnockState =
|
||||
{ events : Maybe (List StrippedStateEvent) }
|
||||
|
||||
|
||||
type alias LeftRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, state : Maybe State
|
||||
, timeline : Maybe Timeline
|
||||
}
|
||||
|
||||
|
||||
type alias DeviceLists =
|
||||
{ changed : Maybe (List String)
|
||||
, left : Maybe (List String)
|
||||
}
|
||||
|
||||
|
||||
type alias ToDevice =
|
||||
{ events : Maybe (List ToDeviceEvent) }
|
||||
|
||||
|
||||
type alias ToDeviceEvent =
|
||||
{ content : Maybe Json.Value
|
||||
, sender : Maybe String
|
||||
, eventType : Maybe String
|
||||
}
|
||||
|
||||
|
||||
coderSyncResponse : Json.Coder SyncResponse
|
||||
coderSyncResponse =
|
||||
Json.object8
|
||||
{ name = "SyncResponse"
|
||||
, description = [ "An event that is part of a response." ]
|
||||
, init = SyncResponse
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The global private data created by this user." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_lists"
|
||||
, toField = .deviceLists
|
||||
, description = [ "Information on end-to-end device updates, as specified in End-to-end encryption." ]
|
||||
, coder = coderDeviceLists
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_one_time_keys_count"
|
||||
, toField = .deviceOneTimeKeysCount
|
||||
, description = [ "Information on end-to-end encryption keys, as specified in End-to-end encryption." ]
|
||||
, coder = Json.fastDict Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "device_unused_fallback_key_types"
|
||||
, toField = .deviceUnusedFallbackKeyTypes
|
||||
, description = [ "The unused fallback key algorithms." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "next_batch"
|
||||
, toField = .nextBatch
|
||||
, description = [ "Required: The batch token to supply in the since param of the next /sync request." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "presence"
|
||||
, toField = .presence
|
||||
, description = [ "The updates to the presence status of other users." ]
|
||||
, coder = coderPresence
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "rooms"
|
||||
, toField = .rooms
|
||||
, description = [ "Updates to rooms." ]
|
||||
, coder = coderRooms
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "to_device"
|
||||
, toField = .toDevice
|
||||
, description = [ "Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging." ]
|
||||
, coder = coderToDevice
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderAccountData : Json.Coder AccountData
|
||||
coderAccountData =
|
||||
PV.coderAccountData
|
||||
|
||||
|
||||
coderEvent : Json.Coder Event
|
||||
coderEvent =
|
||||
PV.coderEvent
|
||||
|
||||
|
||||
coderPresence : Json.Coder Presence
|
||||
coderPresence =
|
||||
PV.coderPresence
|
||||
|
||||
|
||||
coderRooms : Json.Coder Rooms
|
||||
coderRooms =
|
||||
Json.object4
|
||||
{ name = "Rooms"
|
||||
, description = [ "Updates to rooms." ]
|
||||
, init = Rooms
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "invite"
|
||||
, toField = .invite
|
||||
, description = [ "The rooms that the user has been invited to, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderInvitedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "join"
|
||||
, toField = .join
|
||||
, description = [ "The rooms that the user has joined, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderJoinedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "knock"
|
||||
, toField = .knock
|
||||
, description = [ "The rooms that the user has knocked upon, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderKnockedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "leave"
|
||||
, toField = .leave
|
||||
, description = [ "The rooms that the user has left or been banned from, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderLeftRoom
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderInvitedRoom : Json.Coder InvitedRoom
|
||||
coderInvitedRoom =
|
||||
PV.coderInvitedRoom
|
||||
|
||||
|
||||
coderInviteState : Json.Coder InviteState
|
||||
coderInviteState =
|
||||
PV.coderInviteState
|
||||
|
||||
|
||||
coderStrippedStateEvent : Json.Coder StrippedStateEvent
|
||||
coderStrippedStateEvent =
|
||||
PV.coderStrippedState
|
||||
|
||||
|
||||
coderJoinedRoom : Json.Coder JoinedRoom
|
||||
coderJoinedRoom =
|
||||
Json.object6
|
||||
{ name = "JoinedRoom"
|
||||
, description = [ "The rooms that the user has joined." ]
|
||||
, init = JoinedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The private data that this user has attached to this room." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "ephemeral"
|
||||
, toField = .ephemeral
|
||||
, description = [ "The ephemeral events in the room that aren’t recorded in the timeline or state of the room. e.g. typing." ]
|
||||
, coder = coderEphemeral
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state"
|
||||
, toField = .state
|
||||
, description = [ "Updates to the state, between the time indicated by the since parameter, and the start of the timeline (or all state up to the start of the timeline, if since is not given, or full_state is true).", "N.B. state updates for m.room.member events will be incomplete if lazy_load_members is enabled in the /sync filter, and only return the member events required to display the senders of the timeline events in this response." ]
|
||||
, coder = coderState
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "summary"
|
||||
, toField = .summary
|
||||
, description = [ "Information about the room which clients may need to correctly render it to users." ]
|
||||
, coder = coderRoomSummary
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "timeline"
|
||||
, toField = .timeline
|
||||
, description = [ "The timeline of messages and state changes in the room." ]
|
||||
, coder = coderTimeline
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unread_notifications"
|
||||
, toField = .unreadNotifications
|
||||
, description = [ "Counts of unread notifications for this room. See the Receiving notifications section for more information on how these are calculated." ]
|
||||
, coder = coderUnreadNotificationCounts
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderEphemeral : Json.Coder Ephemeral
|
||||
coderEphemeral =
|
||||
PV.coderEphemeral
|
||||
|
||||
|
||||
coderState : Json.Coder State
|
||||
coderState =
|
||||
Json.object1
|
||||
{ name = "State"
|
||||
, description = [ "Updates to the state." ]
|
||||
, init = State
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderClientEventWithoutRoomID
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderClientEventWithoutRoomID : Json.Coder ClientEventWithoutRoomID
|
||||
coderClientEventWithoutRoomID =
|
||||
Json.object7
|
||||
{ name = "ClientEventWithoutRoomID"
|
||||
, description = [ "An event without a room ID." ]
|
||||
, init = ClientEventWithoutRoomID
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "Required: The body of this event, as created by the client which sent it." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "event_id"
|
||||
, toField = .eventId
|
||||
, description = [ "Required: The globally unique identifier for this event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "origin_server_ts"
|
||||
, toField = .originServerTs
|
||||
, description = [ "Required: Timestamp (in milliseconds since the unix epoch) on originating homeserver when this event was sent." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description = [ "Required: Contains the fully-qualified ID of the user who sent this event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state_key"
|
||||
, toField = .stateKey
|
||||
, description = [ "Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "Required: The type of the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unsigned"
|
||||
, toField = .unsigned
|
||||
, description = [ "Contains optional extra information about the event." ]
|
||||
, coder = coderUnsignedData
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderUnsignedData : Json.Coder UnsignedData
|
||||
coderUnsignedData =
|
||||
Json.object4
|
||||
{ name = "UnsignedData"
|
||||
, description = [ "Contains optional extra information about the event." ]
|
||||
, init =
|
||||
\a b c d ->
|
||||
UnsignedData
|
||||
{ age = a
|
||||
, prevContent = b
|
||||
, redactedBecause = c
|
||||
, transactionId = d
|
||||
}
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "age"
|
||||
, toField = \(UnsignedData u) -> u.age
|
||||
, description = [ "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." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "prev_content"
|
||||
, toField = \(UnsignedData u) -> u.prevContent
|
||||
, description = [ "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.", "Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "redacted_because"
|
||||
, toField = \(UnsignedData u) -> u.redactedBecause
|
||||
, description = [ "The event that redacted this event, if any." ]
|
||||
, coder = Json.lazy (\_ -> coderClientEventWithoutRoomID)
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "transaction_id"
|
||||
, toField = \(UnsignedData u) -> u.transactionId
|
||||
, description = [ "The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderRoomSummary : Json.Coder RoomSummary
|
||||
coderRoomSummary =
|
||||
PV.coderRoomSummary
|
||||
|
||||
|
||||
coderTimeline : Json.Coder Timeline
|
||||
coderTimeline =
|
||||
Json.object3
|
||||
{ name = "Timeline"
|
||||
, description = [ "The timeline of messages and state changes in the room." ]
|
||||
, init = Timeline
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "Required: List of events." ]
|
||||
, coder = Json.list coderClientEventWithoutRoomID
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "limited"
|
||||
, toField = .limited
|
||||
, description = [ "True if the number of events returned was limited by the limit on the filter." ]
|
||||
, coder = Json.bool
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "prev_batch"
|
||||
, toField = .prevBatch
|
||||
, description = [ "A token that can be supplied to the from parameter of the /rooms/<room_id>/messages endpoint in order to retrieve earlier events. If no earlier events are available, this property may be omitted from the response." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderUnreadNotificationCounts : Json.Coder UnreadNotificationCounts
|
||||
coderUnreadNotificationCounts =
|
||||
PV.coderUnreadNotificationCounts
|
||||
|
||||
|
||||
coderKnockedRoom : Json.Coder KnockedRoom
|
||||
coderKnockedRoom =
|
||||
PV.coderKnockedRoom
|
||||
|
||||
|
||||
coderKnockState : Json.Coder KnockState
|
||||
coderKnockState =
|
||||
PV.coderKnockState
|
||||
|
||||
|
||||
coderLeftRoom : Json.Coder LeftRoom
|
||||
coderLeftRoom =
|
||||
Json.object3
|
||||
{ name = "LeftRoom"
|
||||
, description = [ "The rooms that the user has left or been banned from." ]
|
||||
, init = LeftRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The private data that this user has attached to this room." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state"
|
||||
, toField = .state
|
||||
, description = [ "The state updates for the room up to the start of the timeline." ]
|
||||
, coder = coderState
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "timeline"
|
||||
, toField = .timeline
|
||||
, description = [ "The timeline of messages and state changes in the room up to the point when the user left." ]
|
||||
, coder = coderTimeline
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderDeviceLists : Json.Coder DeviceLists
|
||||
coderDeviceLists =
|
||||
PV.coderDeviceLists
|
||||
|
||||
|
||||
coderToDevice : Json.Coder ToDevice
|
||||
coderToDevice =
|
||||
PV.coderToDevice
|
||||
|
||||
|
||||
coderToDeviceEvent : Json.Coder ToDeviceEvent
|
||||
coderToDeviceEvent =
|
||||
PV.coderToDeviceEvent
|
|
@ -0,0 +1,443 @@
|
|||
module Internal.Api.Sync.V3 exposing (..)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Sync response
|
||||
|
||||
This API module represents the /sync endpoint on the following Matrix spec
|
||||
versions:
|
||||
|
||||
<https://spec.matrix.org/v1.4/client-server-api/#syncing>
|
||||
<https://spec.matrix.org/v1.5/client-server-api/#syncing>
|
||||
<https://spec.matrix.org/v1.6/client-server-api/#syncing>
|
||||
<https://spec.matrix.org/v1.7/client-server-api/#syncing>
|
||||
<https://spec.matrix.org/v1.8/client-server-api/#syncing>
|
||||
<https://spec.matrix.org/v1.9/client-server-api/#syncing>
|
||||
<https://spec.matrix.org/v1.10/client-server-api/#syncing>
|
||||
|
||||
-}
|
||||
|
||||
import FastDict exposing (Dict)
|
||||
import Internal.Api.Sync.V2 as PV
|
||||
import Internal.Tools.Json as Json
|
||||
|
||||
|
||||
type alias SyncResponse =
|
||||
{ accountData : Maybe AccountData
|
||||
, deviceLists : Maybe DeviceLists
|
||||
, deviceOneTimeKeysCount : Maybe (Dict String Int)
|
||||
, deviceUnusedFallbackKeyTypes : List String
|
||||
, nextBatch : String
|
||||
, presence : Maybe Presence
|
||||
, rooms : Maybe Rooms
|
||||
, toDevice : Maybe ToDevice
|
||||
}
|
||||
|
||||
|
||||
type alias AccountData =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Event =
|
||||
{ content : Json.Value
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias Presence =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Rooms =
|
||||
{ invite : Maybe (Dict String InvitedRoom)
|
||||
, join : Maybe (Dict String JoinedRoom)
|
||||
, knock : Maybe (Dict String KnockedRoom)
|
||||
, leave : Maybe (Dict String LeftRoom)
|
||||
}
|
||||
|
||||
|
||||
type alias InvitedRoom =
|
||||
{ inviteState : Maybe InviteState }
|
||||
|
||||
|
||||
type alias InviteState =
|
||||
{ events : Maybe (List StrippedStateEvent) }
|
||||
|
||||
|
||||
type alias StrippedStateEvent =
|
||||
{ content : Json.Value
|
||||
, sender : String
|
||||
, stateKey : String
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias JoinedRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, ephemeral : Maybe Ephemeral
|
||||
, state : Maybe State
|
||||
, summary : Maybe RoomSummary
|
||||
, timeline : Maybe Timeline
|
||||
, unreadNotifications : Maybe UnreadNotificationCounts
|
||||
, unreadThreadNotifications : Maybe (Dict String ThreadNotificationCounts)
|
||||
}
|
||||
|
||||
|
||||
type alias Ephemeral =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias State =
|
||||
{ events : Maybe (List ClientEventWithoutRoomID) }
|
||||
|
||||
|
||||
type alias ClientEventWithoutRoomID =
|
||||
{ content : Json.Value
|
||||
, eventId : String
|
||||
, originServerTs : Int
|
||||
, sender : String
|
||||
, stateKey : Maybe String
|
||||
, eventType : String
|
||||
, unsigned : Maybe UnsignedData
|
||||
}
|
||||
|
||||
|
||||
type alias UnsignedData =
|
||||
PV.UnsignedData
|
||||
|
||||
|
||||
type alias RoomSummary =
|
||||
{ mHeroes : Maybe (List String)
|
||||
, mInvitedMemberCount : Maybe Int
|
||||
, mJoinedMemberCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias Timeline =
|
||||
{ events : List ClientEventWithoutRoomID
|
||||
, limited : Maybe Bool
|
||||
, prevBatch : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias UnreadNotificationCounts =
|
||||
{ highlightCount : Maybe Int
|
||||
, notificationCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias ThreadNotificationCounts =
|
||||
{ highlightCount : Maybe Int
|
||||
, notificationCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias KnockedRoom =
|
||||
{ knockState : Maybe KnockState }
|
||||
|
||||
|
||||
type alias KnockState =
|
||||
{ events : Maybe (List StrippedStateEvent) }
|
||||
|
||||
|
||||
type alias LeftRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, state : Maybe State
|
||||
, timeline : Maybe Timeline
|
||||
}
|
||||
|
||||
|
||||
type alias DeviceLists =
|
||||
{ changed : Maybe (List String)
|
||||
, left : Maybe (List String)
|
||||
}
|
||||
|
||||
|
||||
type alias ToDevice =
|
||||
{ events : Maybe (List ToDeviceEvent) }
|
||||
|
||||
|
||||
type alias ToDeviceEvent =
|
||||
{ content : Maybe Json.Value
|
||||
, sender : Maybe String
|
||||
, eventType : Maybe String
|
||||
}
|
||||
|
||||
|
||||
coderSyncResponse : Json.Coder SyncResponse
|
||||
coderSyncResponse =
|
||||
Json.object8
|
||||
{ name = "SyncResponse"
|
||||
, description = [ "The response for a sync request." ]
|
||||
, init = SyncResponse
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The global private data created by this user." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_lists"
|
||||
, toField = .deviceLists
|
||||
, description = [ "Information on end-to-end device updates, as specified in End-to-end encryption." ]
|
||||
, coder = coderDeviceLists
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_one_time_keys_count"
|
||||
, toField = .deviceOneTimeKeysCount
|
||||
, description = [ "Information on end-to-end encryption keys, as specified in End-to-end encryption." ]
|
||||
, coder = Json.fastDict Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "device_unused_fallback_key_types"
|
||||
, toField = .deviceUnusedFallbackKeyTypes
|
||||
, description = [ "The unused fallback key algorithms." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "next_batch"
|
||||
, toField = .nextBatch
|
||||
, description = [ "The batch token to supply in the since param of the next /sync request." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "presence"
|
||||
, toField = .presence
|
||||
, description = [ "The updates to the presence status of other users." ]
|
||||
, coder = coderPresence
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "rooms"
|
||||
, toField = .rooms
|
||||
, description = [ "Updates to rooms." ]
|
||||
, coder = coderRooms
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "to_device"
|
||||
, toField = .toDevice
|
||||
, description = [ "Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging." ]
|
||||
, coder = coderToDevice
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderAccountData : Json.Coder AccountData
|
||||
coderAccountData =
|
||||
PV.coderAccountData
|
||||
|
||||
|
||||
coderEvent : Json.Coder Event
|
||||
coderEvent =
|
||||
PV.coderEvent
|
||||
|
||||
|
||||
coderPresence : Json.Coder Presence
|
||||
coderPresence =
|
||||
PV.coderPresence
|
||||
|
||||
|
||||
coderRooms : Json.Coder Rooms
|
||||
coderRooms =
|
||||
Json.object4
|
||||
{ name = "Rooms"
|
||||
, description = [ "Updates to rooms." ]
|
||||
, init = Rooms
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "invite"
|
||||
, toField = .invite
|
||||
, description = [ "The rooms that the user has been invited to, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderInvitedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "join"
|
||||
, toField = .join
|
||||
, description = [ "The rooms that the user has joined, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderJoinedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "knock"
|
||||
, toField = .knock
|
||||
, description = [ "The rooms that the user has knocked upon, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderKnockedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "leave"
|
||||
, toField = .leave
|
||||
, description = [ "The rooms that the user has left or been banned from, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderLeftRoom
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderInvitedRoom : Json.Coder InvitedRoom
|
||||
coderInvitedRoom =
|
||||
PV.coderInvitedRoom
|
||||
|
||||
|
||||
coderInviteState : Json.Coder InviteState
|
||||
coderInviteState =
|
||||
PV.coderInviteState
|
||||
|
||||
|
||||
coderStrippedStateEvent : Json.Coder StrippedStateEvent
|
||||
coderStrippedStateEvent =
|
||||
PV.coderStrippedStateEvent
|
||||
|
||||
|
||||
coderJoinedRoom : Json.Coder JoinedRoom
|
||||
coderJoinedRoom =
|
||||
Json.object7
|
||||
{ name = "JoinedRoom"
|
||||
, description = [ "Information about a room the user has joined." ]
|
||||
, init = JoinedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The private data that this user has attached to this room." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "ephemeral"
|
||||
, toField = .ephemeral
|
||||
, description = [ "The ephemeral events in the room that aren’t recorded in the timeline or state of the room. e.g. typing." ]
|
||||
, coder = coderEphemeral
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state"
|
||||
, toField = .state
|
||||
, description = [ "Updates to the state, between the time indicated by the since parameter, and the start of the timeline (or all state up to the start of the timeline, if since is not given, or full_state is true).", "N.B. state updates for m.room.member events will be incomplete if lazy_load_members is enabled in the /sync filter, and only return the member events required to display the senders of the timeline events in this response." ]
|
||||
, coder = coderState
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "summary"
|
||||
, toField = .summary
|
||||
, description = [ "Information about the room which clients may need to correctly render it to users." ]
|
||||
, coder = coderRoomSummary
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "timeline"
|
||||
, toField = .timeline
|
||||
, description = [ "The timeline of messages and state changes in the room." ]
|
||||
, coder = coderTimeline
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unread_notifications"
|
||||
, toField = .unreadNotifications
|
||||
, description = [ "Counts of unread notifications for this room. See the Receiving notifications section for more information on how these are calculated.", "If unread_thread_notifications was specified as true on the RoomEventFilter, these counts will only be for the main timeline rather than all events in the room. See the threading module for more information.", "Changed in v1.4: Updated to reflect behaviour of having unread_thread_notifications as true in the RoomEventFilter for /sync." ]
|
||||
, coder = coderUnreadNotificationCounts
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unread_thread_notifications"
|
||||
, toField = .unreadThreadNotifications
|
||||
, description = [ "If unread_thread_notifications was specified as true on the RoomEventFilter, the notification counts for each thread in this room. The object is keyed by thread root ID, with values matching unread_notifications.", "If a thread does not have any notifications it can be omitted from this object. If no threads have notification counts, this whole object can be omitted.", "Added in v1.4" ]
|
||||
, coder = Json.fastDict coderThreadNotificationCounts
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderEphemeral : Json.Coder Ephemeral
|
||||
coderEphemeral =
|
||||
PV.coderEphemeral
|
||||
|
||||
|
||||
coderState : Json.Coder State
|
||||
coderState =
|
||||
PV.coderState
|
||||
|
||||
|
||||
coderClientEventWithoutRoomID : Json.Coder ClientEventWithoutRoomID
|
||||
coderClientEventWithoutRoomID =
|
||||
PV.coderClientEventWithoutRoomID
|
||||
|
||||
|
||||
coderUnsignedData : Json.Coder UnsignedData
|
||||
coderUnsignedData =
|
||||
PV.coderUnsignedData
|
||||
|
||||
|
||||
coderRoomSummary : Json.Coder RoomSummary
|
||||
coderRoomSummary =
|
||||
PV.coderRoomSummary
|
||||
|
||||
|
||||
coderTimeline : Json.Coder Timeline
|
||||
coderTimeline =
|
||||
PV.coderTimeline
|
||||
|
||||
|
||||
coderUnreadNotificationCounts : Json.Coder UnreadNotificationCounts
|
||||
coderUnreadNotificationCounts =
|
||||
PV.coderUnreadNotificationCounts
|
||||
|
||||
|
||||
coderThreadNotificationCounts : Json.Coder ThreadNotificationCounts
|
||||
coderThreadNotificationCounts =
|
||||
Json.object2
|
||||
{ name = "ThreadNotificationCounts"
|
||||
, description = [ "The notification counts for each thread in this room." ]
|
||||
, init = ThreadNotificationCounts
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "highlight_count"
|
||||
, toField = .highlightCount
|
||||
, description = [ "The number of unread notifications for this thread with the highlight flag set." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "notification_count"
|
||||
, toField = .notificationCount
|
||||
, description = [ "The total number of unread notifications for this thread." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderKnockedRoom : Json.Coder KnockedRoom
|
||||
coderKnockedRoom =
|
||||
PV.coderKnockedRoom
|
||||
|
||||
|
||||
coderKnockState : Json.Coder KnockState
|
||||
coderKnockState =
|
||||
PV.coderKnockState
|
||||
|
||||
|
||||
coderLeftRoom : Json.Coder LeftRoom
|
||||
coderLeftRoom =
|
||||
PV.coderLeftRoom
|
||||
|
||||
|
||||
coderDeviceLists : Json.Coder DeviceLists
|
||||
coderDeviceLists =
|
||||
PV.coderDeviceLists
|
||||
|
||||
|
||||
coderToDevice : Json.Coder ToDevice
|
||||
coderToDevice =
|
||||
PV.coderToDevice
|
||||
|
||||
|
||||
coderToDeviceEvent : Json.Coder ToDeviceEvent
|
||||
coderToDeviceEvent =
|
||||
PV.coderToDeviceEvent
|
|
@ -0,0 +1,827 @@
|
|||
module Internal.Api.Sync.V4 exposing (..)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Sync response
|
||||
|
||||
This API module represents the /sync endpoint on Matrix spec version v1.11.
|
||||
|
||||
<https://spec.matrix.org/v1.11/client-server-api/#syncing>
|
||||
|
||||
-}
|
||||
|
||||
import FastDict exposing (Dict)
|
||||
import Internal.Api.Sync.V3 as PV
|
||||
import Internal.Tools.Json as Json
|
||||
|
||||
|
||||
type alias SyncResponse =
|
||||
{ accountData : Maybe AccountData
|
||||
, deviceLists : Maybe DeviceLists
|
||||
, deviceOneTimeKeysCount : Maybe (Dict String Int)
|
||||
, deviceUnusedFallbackKeyTypes : List String
|
||||
, nextBatch : String
|
||||
, presence : Maybe Presence
|
||||
, rooms : Maybe Rooms
|
||||
, toDevice : Maybe ToDevice
|
||||
}
|
||||
|
||||
|
||||
type alias AccountData =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Event =
|
||||
{ content : Json.Value
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias Presence =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias Rooms =
|
||||
{ invite : Maybe (Dict String InvitedRoom)
|
||||
, join : Maybe (Dict String JoinedRoom)
|
||||
, knock : Maybe (Dict String KnockedRoom)
|
||||
, leave : Maybe (Dict String LeftRoom)
|
||||
}
|
||||
|
||||
|
||||
type alias InvitedRoom =
|
||||
{ inviteState : Maybe InviteState }
|
||||
|
||||
|
||||
type alias InviteState =
|
||||
{ events : Maybe (List StrippedStateEvent) }
|
||||
|
||||
|
||||
type alias StrippedStateEvent =
|
||||
{ content : Json.Value
|
||||
, sender : String
|
||||
, stateKey : String
|
||||
, eventType : String
|
||||
}
|
||||
|
||||
|
||||
type alias JoinedRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, ephemeral : Maybe Ephemeral
|
||||
, state : Maybe State
|
||||
, summary : Maybe RoomSummary
|
||||
, timeline : Maybe Timeline
|
||||
, unreadNotifications : Maybe UnreadNotificationCounts
|
||||
, unreadThreadNotifications : Maybe (Dict String ThreadNotificationCounts)
|
||||
}
|
||||
|
||||
|
||||
type alias Ephemeral =
|
||||
{ events : Maybe (List Event) }
|
||||
|
||||
|
||||
type alias State =
|
||||
{ events : Maybe (List ClientEventWithoutRoomID) }
|
||||
|
||||
|
||||
type alias ClientEventWithoutRoomID =
|
||||
{ content : Json.Value
|
||||
, eventId : String
|
||||
, originServerTs : Int
|
||||
, sender : String
|
||||
, stateKey : Maybe String
|
||||
, eventType : String
|
||||
, unsigned : Maybe UnsignedData
|
||||
}
|
||||
|
||||
|
||||
type UnsignedData
|
||||
= UnsignedData
|
||||
{ age : Maybe Int
|
||||
, membership : Maybe String
|
||||
, prevContent : Maybe Json.Value
|
||||
, redactedBecause : Maybe ClientEventWithoutRoomID
|
||||
, transactionId : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias RoomSummary =
|
||||
{ mHeroes : Maybe (List String)
|
||||
, mInvitedMemberCount : Maybe Int
|
||||
, mJoinedMemberCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias Timeline =
|
||||
{ events : List ClientEventWithoutRoomID
|
||||
, limited : Maybe Bool
|
||||
, prevBatch : Maybe String
|
||||
}
|
||||
|
||||
|
||||
type alias UnreadNotificationCounts =
|
||||
{ highlightCount : Maybe Int
|
||||
, notificationCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias ThreadNotificationCounts =
|
||||
{ highlightCount : Maybe Int
|
||||
, notificationCount : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias KnockedRoom =
|
||||
{ knockState : Maybe KnockState }
|
||||
|
||||
|
||||
type alias KnockState =
|
||||
{ events : Maybe (List StrippedStateEvent) }
|
||||
|
||||
|
||||
type alias LeftRoom =
|
||||
{ accountData : Maybe AccountData
|
||||
, state : Maybe State
|
||||
, timeline : Maybe Timeline
|
||||
}
|
||||
|
||||
|
||||
type alias DeviceLists =
|
||||
{ changed : Maybe (List String)
|
||||
, left : Maybe (List String)
|
||||
}
|
||||
|
||||
|
||||
type alias ToDevice =
|
||||
{ events : Maybe (List ToDeviceEvent) }
|
||||
|
||||
|
||||
type alias ToDeviceEvent =
|
||||
{ content : Maybe Json.Value
|
||||
, sender : Maybe String
|
||||
, eventType : Maybe String
|
||||
}
|
||||
|
||||
|
||||
coderSyncResponse : Json.Coder SyncResponse
|
||||
coderSyncResponse =
|
||||
Json.object8
|
||||
{ name = "SyncResponse"
|
||||
, description = [ "The response received when the server successfully processes the request." ]
|
||||
, init = SyncResponse
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The global private data created by this user." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_lists"
|
||||
, toField = .deviceLists
|
||||
, description = [ "Information on end-to-end device updates, as specified in End-to-end encryption." ]
|
||||
, coder = coderDeviceLists
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "device_one_time_keys_count"
|
||||
, toField = .deviceOneTimeKeysCount
|
||||
, description = [ "Information on end-to-end encryption keys, as specified in End-to-end encryption." ]
|
||||
, coder = Json.fastDict Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "device_unused_fallback_key_types"
|
||||
, toField = .deviceUnusedFallbackKeyTypes
|
||||
, description = [ "The unused fallback key algorithms." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "next_batch"
|
||||
, toField = .nextBatch
|
||||
, description = [ "The batch token to supply in the since param of the next /sync request." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "presence"
|
||||
, toField = .presence
|
||||
, description = [ "The updates to the presence status of other users." ]
|
||||
, coder = coderPresence
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "rooms"
|
||||
, toField = .rooms
|
||||
, description = [ "Updates to rooms." ]
|
||||
, coder = coderRooms
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "to_device"
|
||||
, toField = .toDevice
|
||||
, description = [ "Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging." ]
|
||||
, coder = coderToDevice
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderAccountData : Json.Coder AccountData
|
||||
coderAccountData =
|
||||
Json.object1
|
||||
{ name = "AccountData"
|
||||
, description = [ "The global private data created by this user." ]
|
||||
, init = AccountData
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderEvent : Json.Coder Event
|
||||
coderEvent =
|
||||
Json.object2
|
||||
{ name = "Event"
|
||||
, description = [ "Details of an event." ]
|
||||
, init = Event
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The fields in this object will vary depending on the type of event. When interacting with the REST API, this is the HTTP body." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type of event. This SHOULD be namespaced similar to Java package naming conventions e.g. ‘com.example.subdomain.event.type’" ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderPresence : Json.Coder Presence
|
||||
coderPresence =
|
||||
Json.object1
|
||||
{ name = "Presence"
|
||||
, description = [ "The updates to the presence status of other users." ]
|
||||
, init = Presence
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderRooms : Json.Coder Rooms
|
||||
coderRooms =
|
||||
Json.object4
|
||||
{ name = "Rooms"
|
||||
, description = [ "Updates to rooms." ]
|
||||
, init = Rooms
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "invite"
|
||||
, toField = .invite
|
||||
, description = [ "The rooms that the user has been invited to, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderInvitedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "join"
|
||||
, toField = .join
|
||||
, description = [ "The rooms that the user has joined, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderJoinedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "knock"
|
||||
, toField = .knock
|
||||
, description = [ "The rooms that the user has knocked upon, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderKnockedRoom
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "leave"
|
||||
, toField = .leave
|
||||
, description = [ "The rooms that the user has left or been banned from, mapped as room ID to room information." ]
|
||||
, coder = Json.fastDict coderLeftRoom
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderInvitedRoom : Json.Coder InvitedRoom
|
||||
coderInvitedRoom =
|
||||
Json.object1
|
||||
{ name = "InvitedRoom"
|
||||
, description = [ "The room that the user has been invited to." ]
|
||||
, init = InvitedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "invite_state"
|
||||
, toField = .inviteState
|
||||
, description = [ "The stripped state of a room that the user has been invited to." ]
|
||||
, coder = coderInviteState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderInviteState : Json.Coder InviteState
|
||||
coderInviteState =
|
||||
Json.object1
|
||||
{ name = "InviteState"
|
||||
, description = [ "The stripped state of a room that the user has been invited to." ]
|
||||
, init = InviteState
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "The stripped state events that form the invite state." ]
|
||||
, coder = Json.list coderStrippedStateEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderStrippedStateEvent : Json.Coder StrippedStateEvent
|
||||
coderStrippedStateEvent =
|
||||
Json.object4
|
||||
{ name = "StrippedStateEvent"
|
||||
, description = [ "A stripped state event that forms part of the invite state." ]
|
||||
, init = StrippedStateEvent
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The content for the event." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description = [ "The sender for the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "state_key"
|
||||
, toField = .stateKey
|
||||
, description = [ "The state_key for the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type for the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderJoinedRoom : Json.Coder JoinedRoom
|
||||
coderJoinedRoom =
|
||||
Json.object7
|
||||
{ name = "JoinedRoom"
|
||||
, description = [ "The room that the user has joined." ]
|
||||
, init = JoinedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The private data that this user has attached to this room." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "ephemeral"
|
||||
, toField = .ephemeral
|
||||
, description = [ "The new ephemeral events in the room (events that aren’t recorded in the timeline or state of the room). In this version of the spec, these are typing notification and read receipt events." ]
|
||||
, coder = coderEphemeral
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state"
|
||||
, toField = .state
|
||||
, description = [ "Updates to the state, between the time indicated by the since parameter, and the start of the timeline (or all state up to the start of the timeline, if since is not given, or full_state is true).", "N.B. state updates for m.room.member events will be incomplete if lazy_load_members is enabled in the /sync filter, and only return the member events required to display the senders of the timeline events in this response." ]
|
||||
, coder = coderState
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "summary"
|
||||
, toField = .summary
|
||||
, description = [ "Information about the room which clients may need to correctly render it to users." ]
|
||||
, coder = coderRoomSummary
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "timeline"
|
||||
, toField = .timeline
|
||||
, description = [ "The timeline of messages and state changes in the room." ]
|
||||
, coder = coderTimeline
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unread_notifications"
|
||||
, toField = .unreadNotifications
|
||||
, description = [ "Counts of unread notifications for this room. See the Receiving notifications section for more information on how these are calculated.", "If unread_thread_notifications was specified as true on the RoomEventFilter, these counts will only be for the main timeline rather than all events in the room. See the threading module for more information.", "Changed in v1.4: Updated to reflect behaviour of having unread_thread_notifications as true in the RoomEventFilter for /sync." ]
|
||||
, coder = coderUnreadNotificationCounts
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unread_thread_notifications"
|
||||
, toField = .unreadThreadNotifications
|
||||
, description = [ "If unread_thread_notifications was specified as true on the RoomEventFilter, the notification counts for each thread in this room. The object is keyed by thread root ID, with values matching unread_notifications.", "If a thread does not have any notifications it can be omitted from this object. If no threads have notification counts, this whole object can be omitted.", "Added in v1.4" ]
|
||||
, coder = Json.fastDict coderThreadNotificationCounts
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderEphemeral : Json.Coder Ephemeral
|
||||
coderEphemeral =
|
||||
Json.object1
|
||||
{ name = "Ephemeral"
|
||||
, description = [ "The new ephemeral events in the room." ]
|
||||
, init = Ephemeral
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderState : Json.Coder State
|
||||
coderState =
|
||||
Json.object1
|
||||
{ name = "State"
|
||||
, description = [ "Updates to the state of the room." ]
|
||||
, init = State
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderClientEventWithoutRoomID
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderClientEventWithoutRoomID : Json.Coder ClientEventWithoutRoomID
|
||||
coderClientEventWithoutRoomID =
|
||||
Json.object7
|
||||
{ name = "ClientEventWithoutRoomID"
|
||||
, description = [ "An event without the room ID." ]
|
||||
, init = ClientEventWithoutRoomID
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The body of this event, as created by the client which sent it." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "event_id"
|
||||
, toField = .eventId
|
||||
, description = [ "The globally unique identifier for this event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ 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
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description = [ "Contains the fully-qualified ID of the user who sent this event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state_key"
|
||||
, toField = .stateKey
|
||||
, description = [ "Present if, and only if, this event is a state event. The key making this piece of state unique in the room. Note that it is often an empty string.", "State keys starting with an @ are reserved for referencing user IDs, such as room members. With the exception of a few events, state events set with a given user’s ID as the state key MUST only be set by that user." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type of the event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "unsigned"
|
||||
, toField = .unsigned
|
||||
, description = [ "Contains optional extra information about the event." ]
|
||||
, coder = coderUnsignedData
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderUnsignedData : Json.Coder UnsignedData
|
||||
coderUnsignedData =
|
||||
Json.object5
|
||||
{ name = "UnsignedData"
|
||||
, description = [ "Contains optional extra information about the event." ]
|
||||
, init =
|
||||
\a b c d e ->
|
||||
UnsignedData
|
||||
{ age = a
|
||||
, membership = b
|
||||
, prevContent = c
|
||||
, redactedBecause = d
|
||||
, transactionId = e
|
||||
}
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "age"
|
||||
, toField = \(UnsignedData u) -> u.age
|
||||
, description = [ "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." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "membership"
|
||||
, toField = \(UnsignedData u) -> u.membership
|
||||
, description = [ "The room membership of the user making the request, at the time of the event.", "This property is the value of the membership property of the requesting user’s m.room.member state at the point of the event, including any changes caused by the event. If the user had yet to join the room at the time of the event (i.e, they have no m.room.member state), this property is set to leave.", "Homeservers SHOULD populate this property wherever practical, but they MAY omit it if necessary (for example, if calculating the value is expensive, servers might choose to only implement it in encrypted rooms). The property is not normally populated in events pushed to application services via the application service transaction API (where there is no clear definition of “requesting user”).", "Added in v1.11" ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "prev_content"
|
||||
, toField = \(UnsignedData u) -> u.prevContent
|
||||
, description = [ "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.", "Changed in v1.2: Previously, this field was specified at the top level of returned events rather than in unsigned (with the exception of the GET .../notifications endpoint), though in practice no known server implementations honoured this." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "redacted_because"
|
||||
, toField = \(UnsignedData u) -> u.redactedBecause
|
||||
, description = [ "The event that redacted this event, if any." ]
|
||||
, coder = Json.lazy (\_ -> coderClientEventWithoutRoomID)
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "transaction_id"
|
||||
, toField = \(UnsignedData u) -> u.transactionId
|
||||
, description = [ "The client-supplied transaction ID, for example, provided via PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}, if the client being given the event is the same one which sent it." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderRoomSummary : Json.Coder RoomSummary
|
||||
coderRoomSummary =
|
||||
Json.object3
|
||||
{ name = "RoomSummary"
|
||||
, description = [ "Information about the room which clients may need to correctly render it to users." ]
|
||||
, init = RoomSummary
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "m.heroes"
|
||||
, toField = .mHeroes
|
||||
, description = [ "The users which can be used to generate a room name if the room does not have one. Required if the room’s m.room.name or m.room.canonical_alias state events are unset or empty.", "This should be the first 5 members of the room, ordered by stream ordering, which are joined or invited. The list must never include the client’s own user ID. When no joined or invited members are available, this should consist of the banned and left users. More than 5 members may be provided, however less than 5 should only be provided when there are less than 5 members to represent.", "When lazy-loading room members is enabled, the membership events for the heroes MUST be included in the state, unless they are redundant. When the list of users changes, the server notifies the client by sending a fresh list of heroes. If there are no changes since the last sync, this field may be omitted." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "m.invited_member_count"
|
||||
, toField = .mInvitedMemberCount
|
||||
, description = [ "The number of users with membership of invite. If this field has not changed since the last sync, it may be omitted. Required otherwise." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "m.joined_member_count"
|
||||
, toField = .mJoinedMemberCount
|
||||
, description = [ "The number of users with membership of join, including the client’s own user ID. If this field has not changed since the last sync, it may be omitted. Required otherwise." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderTimeline : Json.Coder Timeline
|
||||
coderTimeline =
|
||||
Json.object3
|
||||
{ name = "Timeline"
|
||||
, description = [ "The timeline of messages and state changes in the room." ]
|
||||
, init = Timeline
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of events." ]
|
||||
, coder = Json.list coderClientEventWithoutRoomID
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "limited"
|
||||
, toField = .limited
|
||||
, description = [ "True if the number of events returned was limited by the limit on the filter." ]
|
||||
, coder = Json.bool
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "prev_batch"
|
||||
, toField = .prevBatch
|
||||
, description = [ "A token that can be supplied to the from parameter of the /rooms/<room_id>/messages endpoint in order to retrieve earlier events. If no earlier events are available, this property may be omitted from the response." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderUnreadNotificationCounts : Json.Coder UnreadNotificationCounts
|
||||
coderUnreadNotificationCounts =
|
||||
Json.object2
|
||||
{ name = "UnreadNotificationCounts"
|
||||
, description = [ "Counts of unread notifications for this room." ]
|
||||
, init = UnreadNotificationCounts
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "highlight_count"
|
||||
, toField = .highlightCount
|
||||
, description = [ "The number of unread notifications for this room with the highlight flag set." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "notification_count"
|
||||
, toField = .notificationCount
|
||||
, description = [ "The total number of unread notifications for this room." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderThreadNotificationCounts : Json.Coder ThreadNotificationCounts
|
||||
coderThreadNotificationCounts =
|
||||
Json.object2
|
||||
{ name = "ThreadNotificationCounts"
|
||||
, description = [ "The notification counts for each thread in this room." ]
|
||||
, init = ThreadNotificationCounts
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "highlight_count"
|
||||
, toField = .highlightCount
|
||||
, description = [ "The number of unread notifications for this thread with the highlight flag set." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "notification_count"
|
||||
, toField = .notificationCount
|
||||
, description = [ "The total number of unread notifications for this thread." ]
|
||||
, coder = Json.int
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderKnockedRoom : Json.Coder KnockedRoom
|
||||
coderKnockedRoom =
|
||||
Json.object1
|
||||
{ name = "KnockedRoom"
|
||||
, description = [ "The room that the user has knocked upon." ]
|
||||
, init = KnockedRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "knock_state"
|
||||
, toField = .knockState
|
||||
, description = [ "The stripped state of a room that the user has knocked upon." ]
|
||||
, coder = coderKnockState
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderKnockState : Json.Coder KnockState
|
||||
coderKnockState =
|
||||
Json.object1
|
||||
{ name = "KnockState"
|
||||
, description = [ "The stripped state of a room that the user has knocked upon." ]
|
||||
, init = KnockState
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "The stripped state events that form the knock state." ]
|
||||
, coder = Json.list coderStrippedStateEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderLeftRoom : Json.Coder LeftRoom
|
||||
coderLeftRoom =
|
||||
Json.object3
|
||||
{ name = "LeftRoom"
|
||||
, description = [ "The room that the user has left or been banned from." ]
|
||||
, init = LeftRoom
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "account_data"
|
||||
, toField = .accountData
|
||||
, description = [ "The private data that this user has attached to this room." ]
|
||||
, coder = coderAccountData
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "state"
|
||||
, toField = .state
|
||||
, description = [ "The state updates for the room up to the start of the timeline." ]
|
||||
, coder = coderState
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "timeline"
|
||||
, toField = .timeline
|
||||
, description = [ "The timeline of messages and state changes in the room up to the point when the user left." ]
|
||||
, coder = coderTimeline
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderDeviceLists : Json.Coder DeviceLists
|
||||
coderDeviceLists =
|
||||
Json.object2
|
||||
{ name = "DeviceLists"
|
||||
, description = [ "Information on end-to-end device updates, as specified in End-to-end encryption." ]
|
||||
, init = DeviceLists
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "changed"
|
||||
, toField = .changed
|
||||
, description = [ "List of users who have updated their device identity or cross-signing keys, or who now share an encrypted room with the client since the previous sync response." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "left"
|
||||
, toField = .left
|
||||
, description = [ "List of users with whom we do not share any encrypted rooms anymore since the previous sync response." ]
|
||||
, coder = Json.list Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderToDevice : Json.Coder ToDevice
|
||||
coderToDevice =
|
||||
Json.object1
|
||||
{ name = "ToDevice"
|
||||
, description = [ "Information on the send-to-device messages for the client device, as defined in Send-to-Device messaging." ]
|
||||
, init = ToDevice
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "events"
|
||||
, toField = .events
|
||||
, description = [ "List of send-to-device messages." ]
|
||||
, coder = Json.list coderToDeviceEvent
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
coderToDeviceEvent : Json.Coder ToDeviceEvent
|
||||
coderToDeviceEvent =
|
||||
Json.object3
|
||||
{ name = "ToDeviceEvent"
|
||||
, description = [ "A send-to-device event." ]
|
||||
, init = ToDeviceEvent
|
||||
}
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description = [ "The content of this event. The fields in this object will vary depending on the type of event." ]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "sender"
|
||||
, toField = .sender
|
||||
, description = [ "The Matrix user ID of the user who sent this event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "type"
|
||||
, toField = .eventType
|
||||
, description = [ "The type of event." ]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
|
@ -86,6 +86,5 @@ versionsCoder =
|
|||
Set.empty
|
||||
}
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "{}"
|
||||
}
|
||||
)
|
||||
|
|
|
@ -124,6 +124,7 @@ docs :
|
|||
, room : TypeDocs
|
||||
, settings : TypeDocs
|
||||
, stateManager : TypeDocs
|
||||
, strippedEvent : TypeDocs
|
||||
, timeline : TypeDocs
|
||||
, timelineFilter : TypeDocs
|
||||
, unsigned : TypeDocs
|
||||
|
@ -206,6 +207,12 @@ docs =
|
|||
, "Instead of making the user loop through the room's timeline of events, the StateManager offers the user a dictionary-like experience to navigate through the Matrix room state."
|
||||
]
|
||||
}
|
||||
, strippedEvent =
|
||||
{ name = "StrippedEvent"
|
||||
, description =
|
||||
[ "The StrippedEvent is a simplified Matrix event that contains no metadata."
|
||||
]
|
||||
}
|
||||
, timeline =
|
||||
{ name = "Timeline"
|
||||
, description =
|
||||
|
@ -270,6 +277,7 @@ fields :
|
|||
, baseUrl : Desc
|
||||
, deviceId : Desc
|
||||
, experimental : Desc
|
||||
, nextBatch : Desc
|
||||
, now : Desc
|
||||
, password : Desc
|
||||
, refreshToken : Desc
|
||||
|
@ -313,6 +321,7 @@ fields :
|
|||
}
|
||||
, room :
|
||||
{ accountData : Desc
|
||||
, ephemeral : Desc
|
||||
, events : Desc
|
||||
, roomId : Desc
|
||||
, state : Desc
|
||||
|
@ -345,6 +354,7 @@ fields :
|
|||
}
|
||||
, vault :
|
||||
{ accountData : Desc
|
||||
, nextBatch : Desc
|
||||
, rooms : Desc
|
||||
, user : Desc
|
||||
}
|
||||
|
@ -379,6 +389,9 @@ fields =
|
|||
, experimental =
|
||||
[ "Experimental features supported by the homeserver."
|
||||
]
|
||||
, nextBatch =
|
||||
[ "The batch token to supply in the since param of the next /sync request."
|
||||
]
|
||||
, now =
|
||||
[ "The most recently found timestamp."
|
||||
]
|
||||
|
@ -486,6 +499,9 @@ fields =
|
|||
, room =
|
||||
{ accountData =
|
||||
[ "Room account data tracking the user's private storage about this room." ]
|
||||
, ephemeral =
|
||||
[ "Ephemeral events that were sent recently in this room."
|
||||
]
|
||||
, events =
|
||||
[ "Database containing events that were sent in this room." ]
|
||||
, roomId =
|
||||
|
@ -561,6 +577,9 @@ fields =
|
|||
{ accountData =
|
||||
[ "The account's global private data."
|
||||
]
|
||||
, nextBatch =
|
||||
[ "The next batch that can be used to sync with the Matrix API."
|
||||
]
|
||||
, rooms =
|
||||
[ "Directory of joined rooms that the user is a member of."
|
||||
]
|
||||
|
|
|
@ -183,7 +183,6 @@ coder =
|
|||
, description = Text.fields.timelineFilter.senders
|
||||
, coder = Json.set Json.string
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
|
@ -199,7 +198,6 @@ coder =
|
|||
, description = Text.fields.timelineFilter.types
|
||||
, coder = Json.set Json.string
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module Internal.Tools.DecodeExtra exposing
|
||||
( opField, opFieldWithDefault
|
||||
, map9, map10, map11
|
||||
, map9, map10, map11, map12
|
||||
)
|
||||
|
||||
{-|
|
||||
|
@ -18,7 +18,7 @@ This module contains helper functions that help decode JSON.
|
|||
|
||||
## Extended map functions
|
||||
|
||||
@docs map9, map10, map11
|
||||
@docs map9, map10, map11, map12
|
||||
|
||||
-}
|
||||
|
||||
|
@ -153,3 +153,35 @@ map11 func da db dc dd de df dg dh di dj dk =
|
|||
(D.map2 Tuple.pair df dg)
|
||||
(D.map2 Tuple.pair dh di)
|
||||
(D.map2 Tuple.pair dj dk)
|
||||
|
||||
|
||||
{-| Try 12 decoders and combine the result.
|
||||
-}
|
||||
map12 :
|
||||
(a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l -> value)
|
||||
-> D.Decoder a
|
||||
-> D.Decoder b
|
||||
-> D.Decoder c
|
||||
-> D.Decoder d
|
||||
-> D.Decoder e
|
||||
-> D.Decoder f
|
||||
-> D.Decoder g
|
||||
-> D.Decoder h
|
||||
-> D.Decoder i
|
||||
-> D.Decoder j
|
||||
-> D.Decoder k
|
||||
-> D.Decoder l
|
||||
-> D.Decoder value
|
||||
map12 func da db dc dd de df dg dh di dj dk dl =
|
||||
D.map8
|
||||
(\a b c d ( e, f ) ( g, h ) ( i, j ) ( k, l ) ->
|
||||
func a b c d e f g h i j k l
|
||||
)
|
||||
da
|
||||
db
|
||||
dc
|
||||
dd
|
||||
(D.map2 Tuple.pair de df)
|
||||
(D.map2 Tuple.pair dg dh)
|
||||
(D.map2 Tuple.pair di dj)
|
||||
(D.map2 Tuple.pair dk dl)
|
||||
|
|
|
@ -80,7 +80,6 @@ coder x =
|
|||
, description = Text.fields.iddict.cursor
|
||||
, coder = Json.int
|
||||
, default = ( 0, [] )
|
||||
, defaultToString = String.fromInt
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
|
|
|
@ -5,7 +5,7 @@ module Internal.Tools.Json exposing
|
|||
, Docs(..), RequiredField(..), toDocs
|
||||
, list, listWithOne, slowDict, fastDict, fastIntDict, set, maybe
|
||||
, Field, field, parser
|
||||
, object1, object2, object3, object4, object5, object6, object7, object8, object9, object10, object11
|
||||
, object1, object2, object3, object4, object5, object6, object7, object8, object9, object10, object11, object12
|
||||
)
|
||||
|
||||
{-|
|
||||
|
@ -62,7 +62,7 @@ first.
|
|||
|
||||
Once all fields are constructed, the user can create JSON objects.
|
||||
|
||||
@docs object1, object2, object3, object4, object5, object6, object7, object8, object9, object10, object11
|
||||
@docs object1, object2, object3, object4, object5, object6, object7, object8, object9, object10, object11, object12
|
||||
|
||||
-}
|
||||
|
||||
|
@ -362,7 +362,7 @@ then the following field type would be used:
|
|||
, coder = string
|
||||
}
|
||||
|
||||
Suppose the JSO isn't obligated to provide a list of hobbies, and the list would
|
||||
Suppose the JSON isn't obligated to provide a list of hobbies, and the list would
|
||||
by default be overriden with an empty list, then we would use the following
|
||||
field type:
|
||||
|
||||
|
@ -373,8 +373,7 @@ field type:
|
|||
[ "The hobbies of the person. Can be omitted."
|
||||
]
|
||||
, coder = list string
|
||||
, default = ( [], [] ) -- The `List Log` can be inserted in case you wish to insert a message when relying on a default
|
||||
, defaultToString = always "[]" -- Default converted to a string
|
||||
, default = ( [ "football" ], [] ) -- The `List Log` can be inserted in case you wish to insert a message when relying on a default
|
||||
}
|
||||
|
||||
-}
|
||||
|
@ -382,7 +381,7 @@ field :
|
|||
{ required : { fieldName : String, toField : object -> a, description : List String, coder : Coder a } -> Field a object
|
||||
, optional :
|
||||
{ value : { fieldName : String, toField : object -> Maybe a, description : List String, coder : Coder a } -> Field (Maybe a) object
|
||||
, withDefault : { fieldName : String, toField : object -> a, description : List String, coder : Coder a, default : ( a, List Log ), defaultToString : a -> String } -> Field a object
|
||||
, withDefault : { fieldName : String, toField : object -> a, description : List String, coder : Coder a, default : ( a, List Log ) } -> Field a object
|
||||
}
|
||||
}
|
||||
field =
|
||||
|
@ -425,7 +424,7 @@ field =
|
|||
, requiredness = OptionalField
|
||||
}
|
||||
, withDefault =
|
||||
\{ fieldName, toField, description, coder, default, defaultToString } ->
|
||||
\{ fieldName, toField, description, coder, default } ->
|
||||
case coder of
|
||||
Coder { encoder, decoder, docs } ->
|
||||
Field
|
||||
|
@ -449,7 +448,8 @@ field =
|
|||
, requiredness =
|
||||
default
|
||||
|> Tuple.first
|
||||
|> defaultToString
|
||||
|> encoder
|
||||
|> E.encode 0
|
||||
|> OptionalFieldWithDefault
|
||||
}
|
||||
}
|
||||
|
@ -1175,6 +1175,81 @@ object11 { name, description, init } fa fb fc fd fe ff fg fh fi fj fk =
|
|||
}
|
||||
|
||||
|
||||
{-| Define an object with 12 keys
|
||||
-}
|
||||
object12 :
|
||||
Descriptive { init : a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l -> object }
|
||||
-> Field a object
|
||||
-> Field b object
|
||||
-> Field c object
|
||||
-> Field d object
|
||||
-> Field e object
|
||||
-> Field f object
|
||||
-> Field g object
|
||||
-> Field h object
|
||||
-> Field i object
|
||||
-> Field j object
|
||||
-> Field k object
|
||||
-> Field l object
|
||||
-> Coder object
|
||||
object12 { name, description, init } fa fb fc fd fe ff fg fh fi fj fk fl =
|
||||
Coder
|
||||
{ encoder =
|
||||
objectEncoder
|
||||
[ toEncodeField fa
|
||||
, toEncodeField fb
|
||||
, toEncodeField fc
|
||||
, toEncodeField fd
|
||||
, toEncodeField fe
|
||||
, toEncodeField ff
|
||||
, toEncodeField fg
|
||||
, toEncodeField fh
|
||||
, toEncodeField fi
|
||||
, toEncodeField fj
|
||||
, toEncodeField fk
|
||||
, toEncodeField fl
|
||||
]
|
||||
, decoder =
|
||||
D.map12
|
||||
(\( a, la ) ( b, lb ) ( c, lc ) ( d, ld ) ( e, le ) ( f, lf ) ( g, lg ) ( h, lh ) ( i, li ) ( j, lj ) ( k, lk ) ( l, ll ) ->
|
||||
( init a b c d e f g h i j k l
|
||||
, List.concat [ la, lb, lc, ld, le, lf, lg, lh, li, lj, lk, ll ]
|
||||
)
|
||||
)
|
||||
(toDecoderField fa)
|
||||
(toDecoderField fb)
|
||||
(toDecoderField fc)
|
||||
(toDecoderField fd)
|
||||
(toDecoderField fe)
|
||||
(toDecoderField ff)
|
||||
(toDecoderField fg)
|
||||
(toDecoderField fh)
|
||||
(toDecoderField fi)
|
||||
(toDecoderField fj)
|
||||
(toDecoderField fk)
|
||||
(toDecoderField fl)
|
||||
, docs =
|
||||
DocsObject
|
||||
{ name = name
|
||||
, description = description
|
||||
, keys =
|
||||
[ toDocsField fa
|
||||
, toDocsField fb
|
||||
, toDocsField fc
|
||||
, toDocsField fd
|
||||
, toDocsField fe
|
||||
, toDocsField ff
|
||||
, toDocsField fg
|
||||
, toDocsField fh
|
||||
, toDocsField fi
|
||||
, toDocsField fj
|
||||
, toDocsField fk
|
||||
, toDocsField fl
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{-| Define a parser that converts a string into a custom Elm type.
|
||||
-}
|
||||
parser : { name : String, p : P.Parser ( a, List Log ), toString : a -> String } -> Coder a
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
module Internal.Tools.StrippedEvent exposing (StrippedEvent, coder, strip)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Stripped event
|
||||
|
||||
The stripped event is a simple Matrix event that does not contain any metadata.
|
||||
|
||||
@docs StrippedEvent, coder, strip
|
||||
|
||||
-}
|
||||
|
||||
import Internal.Config.Text as Text
|
||||
import Internal.Tools.Json as Json
|
||||
|
||||
|
||||
type alias StrippedEvent =
|
||||
{ content : Json.Value, eventType : String }
|
||||
|
||||
|
||||
coder : Json.Coder StrippedEvent
|
||||
coder =
|
||||
Json.object2
|
||||
{ name = Text.docs.strippedEvent.name
|
||||
, description = Text.docs.strippedEvent.description
|
||||
, init = StrippedEvent
|
||||
}
|
||||
(Json.field.required
|
||||
{ fieldName = "content"
|
||||
, toField = .content
|
||||
, description =
|
||||
[ "Event content"
|
||||
]
|
||||
, coder = Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "eventType"
|
||||
, toField = .eventType
|
||||
, description =
|
||||
[ "Event type, generally namespaced using the Java package naming convention."
|
||||
]
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
strip : { a | content : Json.Value, eventType : String } -> StrippedEvent
|
||||
strip { content, eventType } =
|
||||
{ content = content, eventType = eventType }
|
|
@ -95,6 +95,7 @@ type alias Context =
|
|||
{ accessTokens : Hashdict AccessToken
|
||||
, baseUrl : Maybe String
|
||||
, deviceId : Maybe String
|
||||
, nextBatch : Maybe String
|
||||
, now : Maybe Timestamp
|
||||
, password : Maybe String
|
||||
, refreshToken : Maybe String
|
||||
|
@ -152,7 +153,7 @@ fromApiFormat (APIContext c) =
|
|||
-}
|
||||
coder : Json.Coder Context
|
||||
coder =
|
||||
Json.object11
|
||||
Json.object12
|
||||
{ name = Text.docs.context.name
|
||||
, description = Text.docs.context.description
|
||||
, init = Context
|
||||
|
@ -178,6 +179,13 @@ coder =
|
|||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "nextBatch"
|
||||
, toField = .nextBatch
|
||||
, description = Text.fields.context.nextBatch
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "now"
|
||||
, toField = .now
|
||||
|
@ -303,6 +311,7 @@ init sn =
|
|||
{ accessTokens = Hashdict.empty .value
|
||||
, baseUrl = Nothing
|
||||
, deviceId = Nothing
|
||||
, nextBatch = Nothing
|
||||
, now = Nothing
|
||||
, refreshToken = Nothing
|
||||
, password = Nothing
|
||||
|
@ -439,6 +448,5 @@ versionsCoder =
|
|||
, description = Text.fields.versions.unstableFeatures
|
||||
, coder = Json.set Json.string
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = Json.encode (Json.set Json.string) >> E.encode 0
|
||||
}
|
||||
)
|
||||
|
|
|
@ -82,6 +82,7 @@ type EnvelopeUpdate a
|
|||
| SetAccessToken AccessToken
|
||||
| SetBaseUrl String
|
||||
| SetDeviceId String
|
||||
| SetNextBatch String
|
||||
| SetNow Timestamp
|
||||
| SetRefreshToken String
|
||||
| SetVersions Versions
|
||||
|
@ -124,7 +125,6 @@ coder c1 =
|
|||
, description = Text.fields.envelope.settings
|
||||
, coder = Settings.coder
|
||||
, default = Tuple.pair Settings.init []
|
||||
, defaultToString = always "<Default settings>"
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -328,6 +328,9 @@ update updateContent eu ({ context } as data) =
|
|||
SetDeviceId d ->
|
||||
{ data | context = { context | deviceId = Just d } }
|
||||
|
||||
SetNextBatch nextBatch ->
|
||||
{ data | context = { context | nextBatch = Just nextBatch } }
|
||||
|
||||
SetNow n ->
|
||||
{ data | context = { context | now = Just n } }
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ import Internal.Config.Text as Text
|
|||
import Internal.Filter.Timeline as Filter exposing (Filter)
|
||||
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
||||
import Internal.Tools.Json as Json
|
||||
import Internal.Tools.StrippedEvent as StrippedEvent exposing (StrippedEvent)
|
||||
import Internal.Values.Event as Event exposing (Event)
|
||||
import Internal.Values.StateManager as StateManager exposing (StateManager)
|
||||
import Internal.Values.Timeline as Timeline exposing (Timeline)
|
||||
|
@ -71,6 +72,7 @@ homeserver.
|
|||
-}
|
||||
type alias Room =
|
||||
{ accountData : Dict String Json.Value
|
||||
, ephemeral : List StrippedEvent
|
||||
, events : Hashdict Event
|
||||
, roomId : String
|
||||
, state : StateManager
|
||||
|
@ -86,7 +88,9 @@ type RoomUpdate
|
|||
| AddSync Batch
|
||||
| Invite User
|
||||
| More (List RoomUpdate)
|
||||
| Optional (Maybe RoomUpdate)
|
||||
| SetAccountData String Json.Value
|
||||
| SetEphemeral (List { eventType : String, content : Json.Value })
|
||||
|
||||
|
||||
{-| Add new events to the Room's event directory + Room's timeline.
|
||||
|
@ -140,7 +144,7 @@ addSync =
|
|||
-}
|
||||
coder : Json.Coder Room
|
||||
coder =
|
||||
Json.object5
|
||||
Json.object6
|
||||
{ name = Text.docs.room.name
|
||||
, description = Text.docs.room.description
|
||||
, init = Room
|
||||
|
@ -151,7 +155,14 @@ coder =
|
|||
, description = Text.fields.room.accountData
|
||||
, coder = Json.fastDict Json.value
|
||||
, default = ( Dict.empty, [] )
|
||||
, defaultToString = Json.encode (Json.fastDict Json.value) >> E.encode 0
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
{ fieldName = "ephemeral"
|
||||
, toField = .ephemeral
|
||||
, description = Text.fields.room.ephemeral
|
||||
, coder = Json.list StrippedEvent.coder
|
||||
, default = ( [], [] )
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -160,7 +171,6 @@ coder =
|
|||
, description = Text.fields.room.events
|
||||
, coder = Hashdict.coder .eventId Event.coder
|
||||
, default = ( Hashdict.empty .eventId, [ log.warn "Found a room with no known events! Is it empty?" ] )
|
||||
, defaultToString = Json.encode (Hashdict.coder .eventId Event.coder) >> E.encode 0
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
|
@ -176,7 +186,6 @@ coder =
|
|||
, description = Text.fields.room.state
|
||||
, coder = StateManager.coder
|
||||
, default = ( StateManager.empty, [] )
|
||||
, defaultToString = Json.encode StateManager.coder >> E.encode 0
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -185,7 +194,6 @@ coder =
|
|||
, description = Text.fields.room.timeline
|
||||
, coder = Timeline.coder
|
||||
, default = ( Timeline.empty, [] )
|
||||
, defaultToString = Json.encode Timeline.coder >> E.encode 0
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -216,6 +224,7 @@ getAccountData key room =
|
|||
init : String -> Room
|
||||
init roomId =
|
||||
{ accountData = Dict.empty
|
||||
, ephemeral = []
|
||||
, events = Hashdict.empty .eventId
|
||||
, roomId = roomId
|
||||
, state = StateManager.empty
|
||||
|
@ -262,5 +271,14 @@ update ru room =
|
|||
More items ->
|
||||
List.foldl update room items
|
||||
|
||||
Optional (Just u) ->
|
||||
update u room
|
||||
|
||||
Optional Nothing ->
|
||||
room
|
||||
|
||||
SetAccountData key value ->
|
||||
setAccountData key value room
|
||||
|
||||
SetEphemeral eph ->
|
||||
{ room | ephemeral = eph }
|
||||
|
|
|
@ -55,7 +55,6 @@ coder =
|
|||
, description = Text.fields.settings.currentVersion
|
||||
, coder = Json.string
|
||||
, default = Tuple.pair Default.currentVersion []
|
||||
, defaultToString = identity
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -64,7 +63,6 @@ coder =
|
|||
, description = Text.fields.settings.deviceName
|
||||
, coder = Json.string
|
||||
, default = Tuple.pair Default.deviceName []
|
||||
, defaultToString = identity
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -73,13 +71,6 @@ coder =
|
|||
, description = Text.fields.settings.removePasswordOnLogin
|
||||
, coder = Json.bool
|
||||
, default = Tuple.pair Default.removePasswordOnLogin []
|
||||
, defaultToString =
|
||||
\b ->
|
||||
if b then
|
||||
"true"
|
||||
|
||||
else
|
||||
"false"
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -88,7 +79,6 @@ coder =
|
|||
, description = Text.fields.settings.syncTime
|
||||
, coder = Json.int
|
||||
, default = Tuple.pair Default.syncTime []
|
||||
, defaultToString = String.fromInt
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -226,7 +226,6 @@ coder =
|
|||
, description = Text.fields.timeline.filledBatches
|
||||
, coder = Json.int
|
||||
, default = ( 0, [] )
|
||||
, defaultToString = String.fromInt
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
|
@ -326,7 +325,6 @@ coderIToken =
|
|||
, description = Text.fields.itoken.starts
|
||||
, coder = Json.set coderIBatchPTRValue
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -335,7 +333,6 @@ coderIToken =
|
|||
, description = Text.fields.itoken.ends
|
||||
, coder = Json.set coderIBatchPTRValue
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -344,7 +341,6 @@ coderIToken =
|
|||
, description = Text.fields.itoken.inFrontOf
|
||||
, coder = Json.set coderITokenPTRValue
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
)
|
||||
(Json.field.optional.withDefault
|
||||
|
@ -353,7 +349,6 @@ coderIToken =
|
|||
, description = Text.fields.itoken.behind
|
||||
, coder = Json.set coderITokenPTRValue
|
||||
, default = ( Set.empty, [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ import Internal.Values.User as User exposing (User)
|
|||
-}
|
||||
type alias Vault =
|
||||
{ accountData : Dict String Json.Value
|
||||
, nextBatch : Maybe String
|
||||
, rooms : Hashdict Room
|
||||
, user : Maybe User
|
||||
}
|
||||
|
@ -56,13 +57,15 @@ type VaultUpdate
|
|||
= CreateRoomIfNotExists String
|
||||
| MapRoom String Room.RoomUpdate
|
||||
| More (List VaultUpdate)
|
||||
| Optional (Maybe VaultUpdate)
|
||||
| SetAccountData String Json.Value
|
||||
| SetNextBatch String
|
||||
| SetUser User
|
||||
|
||||
|
||||
coder : Json.Coder Vault
|
||||
coder =
|
||||
Json.object3
|
||||
Json.object4
|
||||
{ name = Text.docs.vault.name
|
||||
, description = Text.docs.vault.description
|
||||
, init = Vault
|
||||
|
@ -74,6 +77,13 @@ coder =
|
|||
, coder = Json.fastDict Json.value
|
||||
}
|
||||
)
|
||||
(Json.field.optional.value
|
||||
{ fieldName = "nextBatch"
|
||||
, toField = .nextBatch
|
||||
, description = Text.fields.vault.nextBatch
|
||||
, coder = Json.string
|
||||
}
|
||||
)
|
||||
(Json.field.required
|
||||
{ fieldName = "rooms"
|
||||
, toField = .rooms
|
||||
|
@ -109,6 +119,7 @@ getAccountData key vault =
|
|||
init : Maybe User -> Vault
|
||||
init mUser =
|
||||
{ accountData = Dict.empty
|
||||
, nextBatch = Nothing
|
||||
, rooms = Hashdict.empty .roomId
|
||||
, user = mUser
|
||||
}
|
||||
|
@ -152,8 +163,17 @@ update vu vault =
|
|||
More items ->
|
||||
List.foldl update vault items
|
||||
|
||||
Optional (Just u) ->
|
||||
update u vault
|
||||
|
||||
Optional Nothing ->
|
||||
vault
|
||||
|
||||
SetAccountData key value ->
|
||||
setAccountData key value vault
|
||||
|
||||
SetNextBatch nb ->
|
||||
{ vault | nextBatch = Just nb }
|
||||
|
||||
SetUser user ->
|
||||
{ vault | user = Just user }
|
||||
|
|
|
@ -100,7 +100,6 @@ gridField =
|
|||
, description = []
|
||||
, coder = Json.list (Json.list Json.int)
|
||||
, default = ( [], [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
|
||||
|
||||
|
@ -132,7 +131,6 @@ hobbiesField =
|
|||
, description = []
|
||||
, coder = Json.list Json.string
|
||||
, default = ( [], [] )
|
||||
, defaultToString = always "[]"
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,13 +147,6 @@ invitedToPartyField =
|
|||
, description = []
|
||||
, coder = Json.bool
|
||||
, default = ( False, [] )
|
||||
, defaultToString =
|
||||
\b ->
|
||||
if b then
|
||||
"True"
|
||||
|
||||
else
|
||||
"False"
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,11 +12,12 @@ import Test.Values.User as TestUser
|
|||
|
||||
vault : Fuzzer Vault
|
||||
vault =
|
||||
Fuzz.map3 Vault
|
||||
Fuzz.map4 Vault
|
||||
(Fuzz.string
|
||||
|> Fuzz.map (\k -> ( k, Json.encode Json.int 0 ))
|
||||
|> Fuzz.list
|
||||
|> Fuzz.map Dict.fromList
|
||||
)
|
||||
(Fuzz.maybe Fuzz.string)
|
||||
(TestHashdict.fuzzer .roomId TestRoom.fuzzer)
|
||||
(Fuzz.maybe TestUser.fuzzer)
|
||||
|
|
Loading…
Reference in New Issue