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)
|
||||
|
||||
|
||||
sendMessageEvent : Context (VBAT a) -> SendMessageEventInput -> Task X.Error SendMessageEventOutput
|
||||
sendMessageEvent : Context (VBAT { a | timestamp : () }) -> SendMessageEventInput -> Task X.Error SendMessageEventOutput
|
||||
sendMessageEvent context input =
|
||||
VC.withBottomLayer
|
||||
{ current = Api.sendMessageEventV1
|
||||
|
|
|
@ -7,7 +7,7 @@ import Internal.Tools.VersionControl as VC
|
|||
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 =
|
||||
VC.withBottomLayer
|
||||
{ current = Api.sendStateKeyV1
|
||||
|
|
|
@ -123,6 +123,7 @@ sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred =
|
|||
|> List.foldl Hash.independent (Hash.fromString "send message")
|
||||
|> Hash.toString
|
||||
)
|
||||
|> Chain.andThen C.getTimestamp
|
||||
|> Chain.andThen (C.sendMessageEvent { content = content, eventType = eventType, roomId = roomId })
|
||||
|> Chain.andThen
|
||||
(Chain.maybe <| C.getEvent { roomId = roomId })
|
||||
|
@ -132,6 +133,7 @@ sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred =
|
|||
sendStateEvent : SendStateKeyInput -> Credentials -> FutureTask
|
||||
sendStateEvent data cred =
|
||||
C.makeVBA cred
|
||||
|> Chain.andThen C.getTimestamp
|
||||
|> Chain.andThen (C.sendStateEvent data)
|
||||
|> Chain.andThen
|
||||
(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.Exceptions as X
|
||||
import Internal.Tools.LoginValues exposing (AccessToken(..))
|
||||
import Internal.Tools.Timestamp exposing (Timestamp)
|
||||
import Task exposing (Task)
|
||||
import Time
|
||||
|
||||
|
@ -30,6 +31,7 @@ type VaultUpdate
|
|||
-- Updates as a result of API calls
|
||||
| AccountDataSet SetAccountData.SetAccountInput SetAccountData.SetAccountOutput
|
||||
| BanUser Ban.BanInput Ban.BanOutput
|
||||
| CurrentTimestamp Timestamp
|
||||
| GetEvent GetEvent.EventInput GetEvent.EventOutput
|
||||
| GetMessages GetMessages.GetMessagesInput GetMessages.GetMessagesOutput
|
||||
| InviteSent Invite.InviteInput Invite.InviteOutput
|
||||
|
@ -178,6 +180,19 @@ getMessages 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.
|
||||
-}
|
||||
getVersions : TaskChain VaultUpdate { a | baseUrl : () } (VB a)
|
||||
|
@ -325,7 +340,7 @@ redact input =
|
|||
|
||||
{-| 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 =
|
||||
toChain
|
||||
(\output ->
|
||||
|
@ -341,7 +356,7 @@ sendMessageEvent input =
|
|||
|
||||
{-| 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 =
|
||||
toChain
|
||||
(\output ->
|
||||
|
|
|
@ -58,6 +58,7 @@ initFromJoinedRoom data jroom =
|
|||
|> List.map (Event.initFromClientEventWithoutRoomId data.roomId)
|
||||
|> Hashdict.fromList IEvent.eventId
|
||||
, roomId = data.roomId
|
||||
, tempEvents = []
|
||||
, timeline =
|
||||
jroom.timeline
|
||||
|> 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.Tools.LoginValues exposing (AccessToken(..))
|
||||
import Internal.Tools.Timestamp exposing (Timestamp)
|
||||
|
||||
|
||||
type Context a
|
||||
|
@ -24,6 +25,7 @@ type Context a
|
|||
, baseUrl : String
|
||||
, loginParts : Maybe LoginParts
|
||||
, sentEvent : String
|
||||
, timestamp : Timestamp
|
||||
, transactionId : String
|
||||
, userId : String
|
||||
, versions : List String
|
||||
|
@ -59,6 +61,7 @@ init =
|
|||
, baseUrl = L.baseUrl
|
||||
, loginParts = Nothing
|
||||
, sentEvent = L.eventId
|
||||
, timestamp = L.originServerTs
|
||||
, transactionId = L.transactionId
|
||||
, userId = L.sender
|
||||
, versions = L.versions
|
||||
|
@ -93,6 +96,13 @@ getSentEvent (Context { 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.
|
||||
-}
|
||||
getTransactionId : Context { a | transactionId : () } -> String
|
||||
|
@ -135,6 +145,13 @@ setSentEvent sentEvent (Context data) =
|
|||
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.
|
||||
-}
|
||||
setTransactionId : String -> Context a -> Context { a | transactionId : () }
|
||||
|
@ -177,6 +194,13 @@ removeSentEvent (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
|
||||
-}
|
||||
removeTransactionId : Context { a | transactionId : () } -> Context a
|
||||
|
|
|
@ -3,7 +3,8 @@ module Internal.Values.Room exposing (..)
|
|||
import Dict exposing (Dict)
|
||||
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
||||
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.Timeline as Timeline exposing (Timeline)
|
||||
import Json.Encode as E
|
||||
|
@ -15,6 +16,7 @@ type IRoom
|
|||
, ephemeral : List BlindEvent
|
||||
, events : Hashdict IEvent
|
||||
, roomId : String
|
||||
, tempEvents : List IEvent
|
||||
, timeline : Timeline
|
||||
}
|
||||
|
||||
|
@ -33,6 +35,35 @@ addEvent event (IRoom ({ events } as room)) =
|
|||
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.
|
||||
-}
|
||||
addEvents :
|
||||
|
@ -49,6 +80,7 @@ addEvents ({ events } as data) (IRoom room) =
|
|||
{ room
|
||||
| events = List.foldl Hashdict.insert room.events events
|
||||
, timeline = Timeline.addNewEvents data room.timeline
|
||||
, tempEvents = []
|
||||
}
|
||||
|
||||
|
||||
|
@ -101,7 +133,9 @@ latestGap (IRoom room) =
|
|||
-}
|
||||
mostRecentEvents : IRoom -> List IEvent
|
||||
mostRecentEvents (IRoom room) =
|
||||
Timeline.mostRecentEvents room.timeline
|
||||
List.append
|
||||
(Timeline.mostRecentEvents room.timeline)
|
||||
room.tempEvents
|
||||
|
||||
|
||||
{-| Get the room's id.
|
||||
|
|
|
@ -5,7 +5,9 @@ It handles all communication with the homeserver.
|
|||
-}
|
||||
|
||||
import Dict exposing (Dict)
|
||||
import Internal.Config.Leaking as L
|
||||
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.RoomInvite as Invite exposing (IRoomInvite)
|
||||
import Json.Encode as E
|
||||
|
@ -15,6 +17,7 @@ type IVault
|
|||
= IVault
|
||||
{ accountData : Dict String E.Value
|
||||
, invites : List IRoomInvite
|
||||
, latestUpdate : Timestamp
|
||||
, rooms : Hashdict IRoom
|
||||
, since : Maybe String
|
||||
}
|
||||
|
@ -76,6 +79,7 @@ init =
|
|||
IVault
|
||||
{ accountData = Dict.empty
|
||||
, invites = []
|
||||
, latestUpdate = L.originServerTs
|
||||
, rooms = Hashdict.empty Room.roomId
|
||||
, since = Nothing
|
||||
}
|
||||
|
@ -109,6 +113,20 @@ insertRoom room (IVault data) =
|
|||
{ 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.
|
||||
-}
|
||||
removeInvite : String -> IVault -> IVault
|
||||
|
|
|
@ -128,6 +128,9 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
|
|||
BanUser input () ->
|
||||
vault
|
||||
|
||||
CurrentTimestamp t ->
|
||||
Vault { cred = Internal.insertTimestamp t cred, context = context }
|
||||
|
||||
GetEvent input output ->
|
||||
case getRoomById input.roomId vault of
|
||||
Just room ->
|
||||
|
@ -215,23 +218,53 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
|
|||
|> Vault
|
||||
|
||||
-- TODO
|
||||
LeftRoom input _ ->
|
||||
LeftRoom input () ->
|
||||
cred
|
||||
|> Internal.removeInvite input.roomId
|
||||
|> (\x -> { cred = x, context = context })
|
||||
|> Vault
|
||||
|
||||
-- TODO
|
||||
MessageEventSent _ _ ->
|
||||
vault
|
||||
MessageEventSent { content, eventType, roomId } { eventId } ->
|
||||
Maybe.map2
|
||||
(\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
|
||||
RedactedEvent _ _ ->
|
||||
vault
|
||||
|
||||
-- TODO
|
||||
StateEventSent _ _ ->
|
||||
vault
|
||||
StateEventSent { content, eventType, roomId, stateKey } { eventId } ->
|
||||
Maybe.map2
|
||||
(\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 ->
|
||||
let
|
||||
|
|
Loading…
Reference in New Issue