Add temporary events
The SDK now supports temporarily showing events before getting them from sync. One example is to let users show the messages they sent themselves before the sync confirms that their events are on the timeline.pull/1/head
parent
70cbe5b682
commit
75971fec66
|
@ -7,7 +7,7 @@ import Internal.Tools.VersionControl as VC
|
||||||
import Task exposing (Task)
|
import Task exposing (Task)
|
||||||
|
|
||||||
|
|
||||||
sendMessageEvent : Context (VBAT a) -> SendMessageEventInput -> Task X.Error SendMessageEventOutput
|
sendMessageEvent : Context (VBAT { a | timestamp : () }) -> SendMessageEventInput -> Task X.Error SendMessageEventOutput
|
||||||
sendMessageEvent context input =
|
sendMessageEvent context input =
|
||||||
VC.withBottomLayer
|
VC.withBottomLayer
|
||||||
{ current = Api.sendMessageEventV1
|
{ current = Api.sendMessageEventV1
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Internal.Tools.VersionControl as VC
|
||||||
import Task exposing (Task)
|
import Task exposing (Task)
|
||||||
|
|
||||||
|
|
||||||
sendStateKey : Context (VBA a) -> SendStateKeyInput -> Task X.Error SendStateKeyOutput
|
sendStateKey : Context (VBA { a | timestamp : () }) -> SendStateKeyInput -> Task X.Error SendStateKeyOutput
|
||||||
sendStateKey context input =
|
sendStateKey context input =
|
||||||
VC.withBottomLayer
|
VC.withBottomLayer
|
||||||
{ current = Api.sendStateKeyV1
|
{ current = Api.sendStateKeyV1
|
||||||
|
|
|
@ -123,6 +123,7 @@ sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred =
|
||||||
|> List.foldl Hash.independent (Hash.fromString "send message")
|
|> List.foldl Hash.independent (Hash.fromString "send message")
|
||||||
|> Hash.toString
|
|> Hash.toString
|
||||||
)
|
)
|
||||||
|
|> Chain.andThen C.getTimestamp
|
||||||
|> Chain.andThen (C.sendMessageEvent { content = content, eventType = eventType, roomId = roomId })
|
|> Chain.andThen (C.sendMessageEvent { content = content, eventType = eventType, roomId = roomId })
|
||||||
|> Chain.andThen
|
|> Chain.andThen
|
||||||
(Chain.maybe <| C.getEvent { roomId = roomId })
|
(Chain.maybe <| C.getEvent { roomId = roomId })
|
||||||
|
@ -132,6 +133,7 @@ sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred =
|
||||||
sendStateEvent : SendStateKeyInput -> Credentials -> FutureTask
|
sendStateEvent : SendStateKeyInput -> Credentials -> FutureTask
|
||||||
sendStateEvent data cred =
|
sendStateEvent data cred =
|
||||||
C.makeVBA cred
|
C.makeVBA cred
|
||||||
|
|> Chain.andThen C.getTimestamp
|
||||||
|> Chain.andThen (C.sendStateEvent data)
|
|> Chain.andThen (C.sendStateEvent data)
|
||||||
|> Chain.andThen
|
|> Chain.andThen
|
||||||
(Chain.maybe <| C.getEvent { roomId = data.roomId })
|
(Chain.maybe <| C.getEvent { roomId = data.roomId })
|
||||||
|
|
|
@ -21,6 +21,7 @@ import Internal.Api.WhoAmI.Main as WhoAmI
|
||||||
import Internal.Tools.Context as Context exposing (VB, VBA, VBAT)
|
import Internal.Tools.Context as Context exposing (VB, VBA, VBAT)
|
||||||
import Internal.Tools.Exceptions as X
|
import Internal.Tools.Exceptions as X
|
||||||
import Internal.Tools.LoginValues exposing (AccessToken(..))
|
import Internal.Tools.LoginValues exposing (AccessToken(..))
|
||||||
|
import Internal.Tools.Timestamp exposing (Timestamp)
|
||||||
import Task exposing (Task)
|
import Task exposing (Task)
|
||||||
import Time
|
import Time
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ type VaultUpdate
|
||||||
-- Updates as a result of API calls
|
-- Updates as a result of API calls
|
||||||
| AccountDataSet SetAccountData.SetAccountInput SetAccountData.SetAccountOutput
|
| AccountDataSet SetAccountData.SetAccountInput SetAccountData.SetAccountOutput
|
||||||
| BanUser Ban.BanInput Ban.BanOutput
|
| BanUser Ban.BanInput Ban.BanOutput
|
||||||
|
| CurrentTimestamp Timestamp
|
||||||
| GetEvent GetEvent.EventInput GetEvent.EventOutput
|
| GetEvent GetEvent.EventInput GetEvent.EventOutput
|
||||||
| GetMessages GetMessages.GetMessagesInput GetMessages.GetMessagesOutput
|
| GetMessages GetMessages.GetMessagesInput GetMessages.GetMessagesOutput
|
||||||
| InviteSent Invite.InviteInput Invite.InviteOutput
|
| InviteSent Invite.InviteInput Invite.InviteOutput
|
||||||
|
@ -178,6 +180,19 @@ getMessages input =
|
||||||
input
|
input
|
||||||
|
|
||||||
|
|
||||||
|
getTimestamp : TaskChain VaultUpdate a { a | timestamp : () }
|
||||||
|
getTimestamp =
|
||||||
|
toChain
|
||||||
|
(\output ->
|
||||||
|
Chain.TaskChainPiece
|
||||||
|
{ contextChange = Context.setTimestamp output
|
||||||
|
, messages = [ CurrentTimestamp output ]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
(always <| always Time.now)
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
{-| Get the supported spec versions from the homeserver.
|
{-| Get the supported spec versions from the homeserver.
|
||||||
-}
|
-}
|
||||||
getVersions : TaskChain VaultUpdate { a | baseUrl : () } (VB a)
|
getVersions : TaskChain VaultUpdate { a | baseUrl : () } (VB a)
|
||||||
|
@ -325,7 +340,7 @@ redact input =
|
||||||
|
|
||||||
{-| Send a message event to a room.
|
{-| Send a message event to a room.
|
||||||
-}
|
-}
|
||||||
sendMessageEvent : SendMessageEvent.SendMessageEventInput -> TaskChain VaultUpdate (VBAT a) (VBA { a | sentEvent : () })
|
sendMessageEvent : SendMessageEvent.SendMessageEventInput -> TaskChain VaultUpdate (VBAT { a | timestamp : () }) (VBA { a | sentEvent : (), timestamp : () })
|
||||||
sendMessageEvent input =
|
sendMessageEvent input =
|
||||||
toChain
|
toChain
|
||||||
(\output ->
|
(\output ->
|
||||||
|
@ -341,7 +356,7 @@ sendMessageEvent input =
|
||||||
|
|
||||||
{-| Send a state key event to a room.
|
{-| Send a state key event to a room.
|
||||||
-}
|
-}
|
||||||
sendStateEvent : SendStateKey.SendStateKeyInput -> TaskChain VaultUpdate (VBA a) (VBA { a | sentEvent : () })
|
sendStateEvent : SendStateKey.SendStateKeyInput -> TaskChain VaultUpdate (VBA { a | timestamp : () }) (VBA { a | sentEvent : (), timestamp : () })
|
||||||
sendStateEvent input =
|
sendStateEvent input =
|
||||||
toChain
|
toChain
|
||||||
(\output ->
|
(\output ->
|
||||||
|
|
|
@ -58,6 +58,7 @@ initFromJoinedRoom data jroom =
|
||||||
|> List.map (Event.initFromClientEventWithoutRoomId data.roomId)
|
|> List.map (Event.initFromClientEventWithoutRoomId data.roomId)
|
||||||
|> Hashdict.fromList IEvent.eventId
|
|> Hashdict.fromList IEvent.eventId
|
||||||
, roomId = data.roomId
|
, roomId = data.roomId
|
||||||
|
, tempEvents = []
|
||||||
, timeline =
|
, timeline =
|
||||||
jroom.timeline
|
jroom.timeline
|
||||||
|> Maybe.map
|
|> Maybe.map
|
||||||
|
|
|
@ -16,6 +16,7 @@ Additionaly, there are remove functions which are intended to tell the compiler
|
||||||
|
|
||||||
import Internal.Config.Leaking as L
|
import Internal.Config.Leaking as L
|
||||||
import Internal.Tools.LoginValues exposing (AccessToken(..))
|
import Internal.Tools.LoginValues exposing (AccessToken(..))
|
||||||
|
import Internal.Tools.Timestamp exposing (Timestamp)
|
||||||
|
|
||||||
|
|
||||||
type Context a
|
type Context a
|
||||||
|
@ -24,6 +25,7 @@ type Context a
|
||||||
, baseUrl : String
|
, baseUrl : String
|
||||||
, loginParts : Maybe LoginParts
|
, loginParts : Maybe LoginParts
|
||||||
, sentEvent : String
|
, sentEvent : String
|
||||||
|
, timestamp : Timestamp
|
||||||
, transactionId : String
|
, transactionId : String
|
||||||
, userId : String
|
, userId : String
|
||||||
, versions : List String
|
, versions : List String
|
||||||
|
@ -59,6 +61,7 @@ init =
|
||||||
, baseUrl = L.baseUrl
|
, baseUrl = L.baseUrl
|
||||||
, loginParts = Nothing
|
, loginParts = Nothing
|
||||||
, sentEvent = L.eventId
|
, sentEvent = L.eventId
|
||||||
|
, timestamp = L.originServerTs
|
||||||
, transactionId = L.transactionId
|
, transactionId = L.transactionId
|
||||||
, userId = L.sender
|
, userId = L.sender
|
||||||
, versions = L.versions
|
, versions = L.versions
|
||||||
|
@ -93,6 +96,13 @@ getSentEvent (Context { sentEvent }) =
|
||||||
sentEvent
|
sentEvent
|
||||||
|
|
||||||
|
|
||||||
|
{-| Get the event that has been sent to the API recently.
|
||||||
|
-}
|
||||||
|
getTimestamp : Context { a | timestamp : () } -> Timestamp
|
||||||
|
getTimestamp (Context { timestamp }) =
|
||||||
|
timestamp
|
||||||
|
|
||||||
|
|
||||||
{-| Get the transaction id from the Context.
|
{-| Get the transaction id from the Context.
|
||||||
-}
|
-}
|
||||||
getTransactionId : Context { a | transactionId : () } -> String
|
getTransactionId : Context { a | transactionId : () } -> String
|
||||||
|
@ -135,6 +145,13 @@ setSentEvent sentEvent (Context data) =
|
||||||
Context { data | sentEvent = sentEvent }
|
Context { data | sentEvent = sentEvent }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert a sent event id into the context.
|
||||||
|
-}
|
||||||
|
setTimestamp : Timestamp -> Context a -> Context { a | timestamp : () }
|
||||||
|
setTimestamp timestamp (Context data) =
|
||||||
|
Context { data | timestamp = timestamp }
|
||||||
|
|
||||||
|
|
||||||
{-| Insert a transaction id into the context.
|
{-| Insert a transaction id into the context.
|
||||||
-}
|
-}
|
||||||
setTransactionId : String -> Context a -> Context { a | transactionId : () }
|
setTransactionId : String -> Context a -> Context { a | transactionId : () }
|
||||||
|
@ -177,6 +194,13 @@ removeSentEvent (Context data) =
|
||||||
Context data
|
Context data
|
||||||
|
|
||||||
|
|
||||||
|
{-| Remove the sent event's id from the Context
|
||||||
|
-}
|
||||||
|
removeTimestamp : Context { a | timestamp : () } -> Context a
|
||||||
|
removeTimestamp (Context data) =
|
||||||
|
Context data
|
||||||
|
|
||||||
|
|
||||||
{-| Remove the transaction id from the Context
|
{-| Remove the transaction id from the Context
|
||||||
-}
|
-}
|
||||||
removeTransactionId : Context { a | transactionId : () } -> Context a
|
removeTransactionId : Context { a | transactionId : () } -> Context a
|
||||||
|
|
|
@ -3,7 +3,8 @@ module Internal.Values.Room exposing (..)
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
||||||
import Internal.Tools.SpecEnums exposing (SessionDescriptionType(..))
|
import Internal.Tools.SpecEnums exposing (SessionDescriptionType(..))
|
||||||
import Internal.Values.Event exposing (BlindEvent, IEvent)
|
import Internal.Tools.Timestamp exposing (Timestamp)
|
||||||
|
import Internal.Values.Event as IEvent exposing (BlindEvent, IEvent)
|
||||||
import Internal.Values.StateManager as StateManager exposing (StateManager)
|
import Internal.Values.StateManager as StateManager exposing (StateManager)
|
||||||
import Internal.Values.Timeline as Timeline exposing (Timeline)
|
import Internal.Values.Timeline as Timeline exposing (Timeline)
|
||||||
import Json.Encode as E
|
import Json.Encode as E
|
||||||
|
@ -15,6 +16,7 @@ type IRoom
|
||||||
, ephemeral : List BlindEvent
|
, ephemeral : List BlindEvent
|
||||||
, events : Hashdict IEvent
|
, events : Hashdict IEvent
|
||||||
, roomId : String
|
, roomId : String
|
||||||
|
, tempEvents : List IEvent
|
||||||
, timeline : Timeline
|
, timeline : Timeline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +35,35 @@ addEvent event (IRoom ({ events } as room)) =
|
||||||
IRoom { room | events = Hashdict.insert event events }
|
IRoom { room | events = Hashdict.insert event events }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Sometimes, we know that an event exists before the API has told us.
|
||||||
|
For example, when we send an event to a room but we haven't synced up yet.
|
||||||
|
|
||||||
|
In such a case, it is better to "temporarily" store the event until the next sync -
|
||||||
|
this prevents temporary jittering for a user where events can sometimes disappear and reappear
|
||||||
|
back and forth for a few seconds.
|
||||||
|
|
||||||
|
-}
|
||||||
|
addTemporaryEvent : { content : E.Value, eventId : String, eventType : String, originServerTs : Timestamp, sender : String, stateKey : Maybe String } -> IRoom -> IRoom
|
||||||
|
addTemporaryEvent data (IRoom ({ tempEvents } as room)) =
|
||||||
|
IRoom
|
||||||
|
{ room
|
||||||
|
| tempEvents =
|
||||||
|
List.append tempEvents
|
||||||
|
({ content = data.content
|
||||||
|
, eventId = data.eventId
|
||||||
|
, originServerTs = data.originServerTs
|
||||||
|
, roomId = room.roomId
|
||||||
|
, sender = data.sender
|
||||||
|
, stateKey = data.stateKey
|
||||||
|
, eventType = data.eventType
|
||||||
|
, unsigned = Nothing
|
||||||
|
}
|
||||||
|
|> IEvent.init
|
||||||
|
|> List.singleton
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Add new events as the most recent events.
|
{-| Add new events as the most recent events.
|
||||||
-}
|
-}
|
||||||
addEvents :
|
addEvents :
|
||||||
|
@ -49,6 +80,7 @@ addEvents ({ events } as data) (IRoom room) =
|
||||||
{ room
|
{ room
|
||||||
| events = List.foldl Hashdict.insert room.events events
|
| events = List.foldl Hashdict.insert room.events events
|
||||||
, timeline = Timeline.addNewEvents data room.timeline
|
, timeline = Timeline.addNewEvents data room.timeline
|
||||||
|
, tempEvents = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,7 +133,9 @@ latestGap (IRoom room) =
|
||||||
-}
|
-}
|
||||||
mostRecentEvents : IRoom -> List IEvent
|
mostRecentEvents : IRoom -> List IEvent
|
||||||
mostRecentEvents (IRoom room) =
|
mostRecentEvents (IRoom room) =
|
||||||
Timeline.mostRecentEvents room.timeline
|
List.append
|
||||||
|
(Timeline.mostRecentEvents room.timeline)
|
||||||
|
room.tempEvents
|
||||||
|
|
||||||
|
|
||||||
{-| Get the room's id.
|
{-| Get the room's id.
|
||||||
|
|
|
@ -5,7 +5,9 @@ It handles all communication with the homeserver.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
|
import Internal.Config.Leaking as L
|
||||||
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
||||||
|
import Internal.Tools.Timestamp exposing (Timestamp)
|
||||||
import Internal.Values.Room as Room exposing (IRoom)
|
import Internal.Values.Room as Room exposing (IRoom)
|
||||||
import Internal.Values.RoomInvite as Invite exposing (IRoomInvite)
|
import Internal.Values.RoomInvite as Invite exposing (IRoomInvite)
|
||||||
import Json.Encode as E
|
import Json.Encode as E
|
||||||
|
@ -15,6 +17,7 @@ type IVault
|
||||||
= IVault
|
= IVault
|
||||||
{ accountData : Dict String E.Value
|
{ accountData : Dict String E.Value
|
||||||
, invites : List IRoomInvite
|
, invites : List IRoomInvite
|
||||||
|
, latestUpdate : Timestamp
|
||||||
, rooms : Hashdict IRoom
|
, rooms : Hashdict IRoom
|
||||||
, since : Maybe String
|
, since : Maybe String
|
||||||
}
|
}
|
||||||
|
@ -76,6 +79,7 @@ init =
|
||||||
IVault
|
IVault
|
||||||
{ accountData = Dict.empty
|
{ accountData = Dict.empty
|
||||||
, invites = []
|
, invites = []
|
||||||
|
, latestUpdate = L.originServerTs
|
||||||
, rooms = Hashdict.empty Room.roomId
|
, rooms = Hashdict.empty Room.roomId
|
||||||
, since = Nothing
|
, since = Nothing
|
||||||
}
|
}
|
||||||
|
@ -109,6 +113,20 @@ insertRoom room (IVault data) =
|
||||||
{ data | rooms = Hashdict.insert room data.rooms }
|
{ data | rooms = Hashdict.insert room data.rooms }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert a timestamp of when a timestamp was last delivered.
|
||||||
|
-}
|
||||||
|
insertTimestamp : Timestamp -> IVault -> IVault
|
||||||
|
insertTimestamp time (IVault data) =
|
||||||
|
IVault { data | latestUpdate = time }
|
||||||
|
|
||||||
|
|
||||||
|
{-| Last time the vault was updated. Often used as an approximation.
|
||||||
|
-}
|
||||||
|
lastUpdate : IVault -> Timestamp
|
||||||
|
lastUpdate (IVault { latestUpdate }) =
|
||||||
|
latestUpdate
|
||||||
|
|
||||||
|
|
||||||
{-| Remove an invite. This is usually done when the invite has been accepted or rejected.
|
{-| Remove an invite. This is usually done when the invite has been accepted or rejected.
|
||||||
-}
|
-}
|
||||||
removeInvite : String -> IVault -> IVault
|
removeInvite : String -> IVault -> IVault
|
||||||
|
|
|
@ -128,6 +128,9 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
|
||||||
BanUser input () ->
|
BanUser input () ->
|
||||||
vault
|
vault
|
||||||
|
|
||||||
|
CurrentTimestamp t ->
|
||||||
|
Vault { cred = Internal.insertTimestamp t cred, context = context }
|
||||||
|
|
||||||
GetEvent input output ->
|
GetEvent input output ->
|
||||||
case getRoomById input.roomId vault of
|
case getRoomById input.roomId vault of
|
||||||
Just room ->
|
Just room ->
|
||||||
|
@ -215,23 +218,53 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
|
||||||
|> Vault
|
|> Vault
|
||||||
|
|
||||||
-- TODO
|
-- TODO
|
||||||
LeftRoom input _ ->
|
LeftRoom input () ->
|
||||||
cred
|
cred
|
||||||
|> Internal.removeInvite input.roomId
|
|> Internal.removeInvite input.roomId
|
||||||
|> (\x -> { cred = x, context = context })
|
|> (\x -> { cred = x, context = context })
|
||||||
|> Vault
|
|> Vault
|
||||||
|
|
||||||
-- TODO
|
MessageEventSent { content, eventType, roomId } { eventId } ->
|
||||||
MessageEventSent _ _ ->
|
Maybe.map2
|
||||||
vault
|
(\room sender ->
|
||||||
|
room
|
||||||
|
|> Room.withoutCredentials
|
||||||
|
|> IRoom.addTemporaryEvent
|
||||||
|
{ content = content
|
||||||
|
, eventType = eventType
|
||||||
|
, eventId = eventId
|
||||||
|
, originServerTs = Internal.lastUpdate cred
|
||||||
|
, sender = sender
|
||||||
|
, stateKey = Nothing
|
||||||
|
}
|
||||||
|
)
|
||||||
|
(getRoomById roomId vault)
|
||||||
|
(getUsername vault)
|
||||||
|
|> Maybe.map (Room.withCredentials context >> insertRoom >> (|>) vault)
|
||||||
|
|> Maybe.withDefault vault
|
||||||
|
|
||||||
-- TODO
|
-- TODO
|
||||||
RedactedEvent _ _ ->
|
RedactedEvent _ _ ->
|
||||||
vault
|
vault
|
||||||
|
|
||||||
-- TODO
|
StateEventSent { content, eventType, roomId, stateKey } { eventId } ->
|
||||||
StateEventSent _ _ ->
|
Maybe.map2
|
||||||
vault
|
(\room sender ->
|
||||||
|
room
|
||||||
|
|> Room.withoutCredentials
|
||||||
|
|> IRoom.addTemporaryEvent
|
||||||
|
{ content = content
|
||||||
|
, eventType = eventType
|
||||||
|
, eventId = eventId
|
||||||
|
, originServerTs = Internal.lastUpdate cred
|
||||||
|
, sender = sender
|
||||||
|
, stateKey = Just stateKey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
(getRoomById roomId vault)
|
||||||
|
(getUsername vault)
|
||||||
|
|> Maybe.map (Room.withCredentials context >> insertRoom >> (|>) vault)
|
||||||
|
|> Maybe.withDefault vault
|
||||||
|
|
||||||
SyncUpdate input output ->
|
SyncUpdate input output ->
|
||||||
let
|
let
|
||||||
|
|
Loading…
Reference in New Issue