Refactor Credentials to Snackbar

The Snackbar type is a type of candies (tokens) that is brought along with any data type that may be passed on to the user.
pull/1/head
Bram van den Heuvel 2023-04-12 15:36:56 +02:00
parent 66383551d1
commit 619cd53a3a
10 changed files with 312 additions and 281 deletions

View File

@ -0,0 +1,110 @@
module Internal.Api.Snackbar exposing (..)
{-| The snackbar module helps wraps relevant credentials, access tokens, refresh tokens and more around internal types.
Vault, Room and Event types don't need access to API tokens,
but a user may way to redact an event, leave a room or reject an invite.
In such a case, the `Snackbar` type is a bowl of token candies that you can wrap
around any data type.
That way, you can both access the type within AND carry the tokens on every type
without needing to update every data type whenever any of the tokens change.
-}
import Internal.Api.Versions.V1.Versions as V
import Internal.Tools.LoginValues as Login exposing (AccessToken(..))
type Snackbar a
= Snackbar
{ access : AccessToken
, content : a
, homeserver : String
, vs : Maybe V.Versions
}
accessToken : Snackbar a -> AccessToken
accessToken (Snackbar { access }) =
access
addToken : String -> Snackbar a -> Snackbar a
addToken token (Snackbar ({ access } as data)) =
Snackbar { data | access = Login.addToken token access }
addUsernameAndPassword : { username : String, password : String } -> Snackbar a -> Snackbar a
addUsernameAndPassword uap (Snackbar ({ access } as data)) =
Snackbar { data | access = Login.addUsernameAndPassword uap access }
addVersions : V.Versions -> Snackbar a -> Snackbar a
addVersions vs (Snackbar data) =
Snackbar { data | vs = Just vs }
addWhoAmI : { w | userId : String, deviceId : Maybe String } -> Snackbar a -> Snackbar a
addWhoAmI whoami (Snackbar ({ access } as data)) =
Snackbar { data | access = Login.addWhoAmI whoami access }
baseUrl : Snackbar a -> String
baseUrl (Snackbar { homeserver }) =
homeserver
init : { baseUrl : String, content : a } -> Snackbar a
init data =
Snackbar
{ access = NoAccess
, content = data.content
, homeserver = data.baseUrl
, vs = Nothing
}
map : (a -> b) -> Snackbar a -> Snackbar b
map f (Snackbar data) =
Snackbar
{ access = data.access
, content = f data.content
, homeserver = data.homeserver
, vs = data.vs
}
mapList : (a -> List b) -> Snackbar a -> List (Snackbar b)
mapList f (Snackbar data) =
List.map (withCandyFrom (Snackbar data)) (f data.content)
mapMaybe : (a -> Maybe b) -> Snackbar a -> Maybe (Snackbar b)
mapMaybe f (Snackbar data) =
Maybe.map (withCandyFrom (Snackbar data)) (f data.content)
removedAccessToken : Snackbar a -> AccessToken
removedAccessToken (Snackbar { access }) =
Login.removeToken access
userId : Snackbar a -> Maybe String
userId (Snackbar { access }) =
Login.getUserId access
versions : Snackbar a -> Maybe V.Versions
versions (Snackbar { vs }) =
vs
withCandyFrom : Snackbar b -> a -> Snackbar a
withCandyFrom snackbar x =
map (always x) snackbar
withoutCandy : Snackbar a -> a
withoutCandy (Snackbar { content }) =
content

View File

@ -5,7 +5,6 @@ module Internal.Api.Task exposing (..)
import Hash import Hash
import Internal.Api.Chain as Chain import Internal.Api.Chain as Chain
import Internal.Api.Credentials as Cred exposing (Credentials)
import Internal.Api.GetEvent.Main exposing (EventInput) import Internal.Api.GetEvent.Main exposing (EventInput)
import Internal.Api.GetMessages.Main exposing (GetMessagesInput) import Internal.Api.GetMessages.Main exposing (GetMessagesInput)
import Internal.Api.Invite.Main exposing (InviteInput) import Internal.Api.Invite.Main exposing (InviteInput)
@ -14,6 +13,7 @@ import Internal.Api.JoinedMembers.Main exposing (JoinedMembersInput)
import Internal.Api.Leave.Main exposing (LeaveInput) import Internal.Api.Leave.Main exposing (LeaveInput)
import Internal.Api.SendStateKey.Main exposing (SendStateKeyInput) import Internal.Api.SendStateKey.Main exposing (SendStateKeyInput)
import Internal.Api.SetAccountData.Main exposing (SetAccountInput) import Internal.Api.SetAccountData.Main exposing (SetAccountInput)
import Internal.Api.Snackbar as Snackbar exposing (Snackbar)
import Internal.Api.Sync.Main exposing (SyncInput) import Internal.Api.Sync.Main exposing (SyncInput)
import Internal.Api.VaultUpdate as C import Internal.Api.VaultUpdate as C
import Json.Encode as E import Json.Encode as E
@ -29,7 +29,7 @@ type alias EventInput =
} }
getEvent : EventInput -> Credentials -> FutureTask getEvent : EventInput -> Snackbar a -> FutureTask
getEvent { eventId, roomId } cred = getEvent { eventId, roomId } cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.withSentEvent eventId) |> Chain.andThen (C.withSentEvent eventId)
@ -37,35 +37,35 @@ getEvent { eventId, roomId } cred =
|> C.toTask |> C.toTask
getMessages : GetMessagesInput -> Credentials -> FutureTask getMessages : GetMessagesInput -> Snackbar a -> FutureTask
getMessages data cred = getMessages data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.getMessages data) |> Chain.andThen (C.getMessages data)
|> C.toTask |> C.toTask
invite : InviteInput -> Credentials -> FutureTask invite : InviteInput -> Snackbar a -> FutureTask
invite data cred = invite data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.invite data) |> Chain.andThen (C.invite data)
|> C.toTask |> C.toTask
joinedMembers : JoinedMembersInput -> Credentials -> FutureTask joinedMembers : JoinedMembersInput -> Snackbar a -> FutureTask
joinedMembers data cred = joinedMembers data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.joinedMembers data) |> Chain.andThen (C.joinedMembers data)
|> C.toTask |> C.toTask
joinRoomById : JoinRoomByIdInput -> Credentials -> FutureTask joinRoomById : JoinRoomByIdInput -> Snackbar a -> FutureTask
joinRoomById data cred = joinRoomById data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.joinRoomById data) |> Chain.andThen (C.joinRoomById data)
|> C.toTask |> C.toTask
leave : LeaveInput -> Credentials -> FutureTask leave : LeaveInput -> Snackbar a -> FutureTask
leave data cred = leave data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.leave data) |> Chain.andThen (C.leave data)
@ -80,7 +80,7 @@ type alias RedactInput =
} }
redact : RedactInput -> Credentials -> FutureTask redact : RedactInput -> Snackbar a -> FutureTask
redact { eventId, extraTransactionNoise, reason, roomId } cred = redact { eventId, extraTransactionNoise, reason, roomId } cred =
cred cred
|> C.makeVBAT |> C.makeVBAT
@ -109,7 +109,7 @@ type alias SendMessageEventInput =
} }
sendMessageEvent : SendMessageEventInput -> Credentials -> FutureTask sendMessageEvent : SendMessageEventInput -> Snackbar a -> FutureTask
sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred = sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred =
cred cred
|> C.makeVBAT |> C.makeVBAT
@ -130,7 +130,7 @@ sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred =
|> C.toTask |> C.toTask
sendStateEvent : SendStateKeyInput -> Credentials -> FutureTask sendStateEvent : SendStateKeyInput -> Snackbar a -> FutureTask
sendStateEvent data cred = sendStateEvent data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen C.getTimestamp |> Chain.andThen C.getTimestamp
@ -140,24 +140,24 @@ sendStateEvent data cred =
|> C.toTask |> C.toTask
setAccountData : SetAccountInput -> Credentials -> FutureTask setAccountData : SetAccountInput -> Snackbar a -> FutureTask
setAccountData data cred = setAccountData data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.setAccountData data) |> Chain.andThen (C.setAccountData data)
|> C.toTask |> C.toTask
sync : SyncInput -> Credentials -> FutureTask sync : SyncInput -> Snackbar a -> FutureTask
sync data cred = sync data cred =
C.makeVBA cred C.makeVBA cred
|> Chain.andThen (C.sync data) |> Chain.andThen (C.sync data)
|> C.toTask |> C.toTask
loginMaybeSync : SyncInput -> Credentials -> FutureTask loginMaybeSync : SyncInput -> Snackbar a -> FutureTask
loginMaybeSync data cred = loginMaybeSync data cred =
C.makeVB cred C.makeVB cred
|> Chain.andThen (C.accessToken (Cred.refreshedAccessToken cred)) |> Chain.andThen (C.accessToken (Snackbar.removedAccessToken cred))
|> Chain.andThen |> Chain.andThen
(Chain.maybe <| C.sync data) (Chain.maybe <| C.sync data)
|> C.toTask |> C.toTask

View File

@ -2,7 +2,6 @@ module Internal.Api.VaultUpdate exposing (..)
import Internal.Api.Ban.Main as Ban import Internal.Api.Ban.Main as Ban
import Internal.Api.Chain as Chain exposing (IdemChain, TaskChain) import Internal.Api.Chain as Chain exposing (IdemChain, TaskChain)
import Internal.Api.Credentials as Credentials exposing (Credentials)
import Internal.Api.GetEvent.Main as GetEvent import Internal.Api.GetEvent.Main as GetEvent
import Internal.Api.GetMessages.Main as GetMessages import Internal.Api.GetMessages.Main as GetMessages
import Internal.Api.Invite.Main as Invite import Internal.Api.Invite.Main as Invite
@ -14,6 +13,7 @@ import Internal.Api.Redact.Main as Redact
import Internal.Api.SendMessageEvent.Main as SendMessageEvent import Internal.Api.SendMessageEvent.Main as SendMessageEvent
import Internal.Api.SendStateKey.Main as SendStateKey import Internal.Api.SendStateKey.Main as SendStateKey
import Internal.Api.SetAccountData.Main as SetAccountData import Internal.Api.SetAccountData.Main as SetAccountData
import Internal.Api.Snackbar as Snackbar exposing (Snackbar)
import Internal.Api.Sync.Main as Sync import Internal.Api.Sync.Main as Sync
import Internal.Api.Versions.Main as Versions import Internal.Api.Versions.Main as Versions
import Internal.Api.Versions.V1.Versions as V import Internal.Api.Versions.V1.Versions as V
@ -296,28 +296,28 @@ loginWithUsernameAndPassword input =
{-| Make a VB-context based chain. {-| Make a VB-context based chain.
-} -}
makeVB : Credentials -> TaskChain VaultUpdate {} (VB {}) makeVB : Snackbar a -> TaskChain VaultUpdate {} (VB {})
makeVB cred = makeVB snackbar =
cred snackbar
|> Credentials.baseUrl |> Snackbar.baseUrl
|> withBaseUrl |> withBaseUrl
|> Chain.andThen (versions (Credentials.versions cred)) |> Chain.andThen (versions (Snackbar.versions snackbar))
{-| Make a VBA-context based chain. {-| Make a VBA-context based chain.
-} -}
makeVBA : Credentials -> TaskChain VaultUpdate {} (VBA { userId : () }) makeVBA : Snackbar a -> TaskChain VaultUpdate {} (VBA { userId : () })
makeVBA cred = makeVBA snackbar =
cred snackbar
|> makeVB |> makeVB
|> Chain.andThen (accessToken (Credentials.accessToken cred)) |> Chain.andThen (accessToken (Snackbar.accessToken snackbar))
{-| Make a VBAT-context based chain. {-| Make a VBAT-context based chain.
-} -}
makeVBAT : (Int -> String) -> Credentials -> TaskChain VaultUpdate {} (VBAT { userId : () }) makeVBAT : (Int -> String) -> Snackbar a -> TaskChain VaultUpdate {} (VBAT { userId : () })
makeVBAT toString cred = makeVBAT toString snackbar =
cred snackbar
|> makeVBA |> makeVBA
|> Chain.andThen (withTransactionId toString) |> Chain.andThen (withTransactionId toString)

View File

@ -7,10 +7,10 @@ resend other events or forward them elsewhere.
-} -}
import Internal.Api.Credentials exposing (Credentials)
import Internal.Api.GetEvent.Main as GetEvent import Internal.Api.GetEvent.Main as GetEvent
import Internal.Api.GetEvent.V1.SpecObjects as GetEventSO import Internal.Api.GetEvent.V1.SpecObjects as GetEventSO
import Internal.Api.GetMessages.V4.SpecObjects as GetMessagesSO import Internal.Api.GetMessages.V4.SpecObjects as GetMessagesSO
import Internal.Api.Snackbar as Snackbar exposing (Snackbar)
import Internal.Api.Sync.V2.SpecObjects as SyncSO import Internal.Api.Sync.V2.SpecObjects as SyncSO
import Internal.Tools.Timestamp exposing (Timestamp) import Internal.Tools.Timestamp exposing (Timestamp)
import Internal.Values.Event as Internal import Internal.Values.Event as Internal
@ -19,22 +19,8 @@ import Json.Encode as E
{-| The central event type. This type will be used by the user and will be directly interacted with. {-| The central event type. This type will be used by the user and will be directly interacted with.
-} -}
type Event type alias Event =
= Event Snackbar Internal.IEvent
{ event : Internal.IEvent
, context : Credentials
}
{-| Using the credentials' background information and an internal event type,
create an interactive event type.
-}
withCredentials : Credentials -> Internal.IEvent -> Event
withCredentials context event =
Event
{ event = event
, context = context
}
{-| Create an internal event type from an API endpoint event object. {-| Create an internal event type from an API endpoint event object.
@ -115,67 +101,58 @@ initFromClientEventWithoutRoomId rId output =
} }
{-| Get the internal event type that is hidden in the interactive event type.
-}
withoutCredentials : Event -> Internal.IEvent
withoutCredentials (Event { event }) =
event
{- GETTER FUNCTIONS -} {- GETTER FUNCTIONS -}
content : Event -> E.Value content : Event -> E.Value
content = content =
withoutCredentials >> Internal.content Snackbar.withoutCandy >> Internal.content
eventId : Event -> String eventId : Event -> String
eventId = eventId =
withoutCredentials >> Internal.eventId Snackbar.withoutCandy >> Internal.eventId
originServerTs : Event -> Timestamp originServerTs : Event -> Timestamp
originServerTs = originServerTs =
withoutCredentials >> Internal.originServerTs Snackbar.withoutCandy >> Internal.originServerTs
roomId : Event -> String roomId : Event -> String
roomId = roomId =
withoutCredentials >> Internal.roomId Snackbar.withoutCandy >> Internal.roomId
sender : Event -> String sender : Event -> String
sender = sender =
withoutCredentials >> Internal.sender Snackbar.withoutCandy >> Internal.sender
stateKey : Event -> Maybe String stateKey : Event -> Maybe String
stateKey = stateKey =
withoutCredentials >> Internal.stateKey Snackbar.withoutCandy >> Internal.stateKey
eventType : Event -> String eventType : Event -> String
eventType = eventType =
withoutCredentials >> Internal.eventType Snackbar.withoutCandy >> Internal.eventType
age : Event -> Maybe Int age : Event -> Maybe Int
age = age =
withoutCredentials >> Internal.age Snackbar.withoutCandy >> Internal.age
redactedBecause : Event -> Maybe Event redactedBecause : Event -> Maybe Event
redactedBecause (Event data) = redactedBecause event =
data.event event
|> Snackbar.withoutCandy
|> Internal.redactedBecause |> Internal.redactedBecause
|> Maybe.map |> Maybe.map (Snackbar.withCandyFrom event)
(\event ->
Event { data | event = event }
)
transactionId : Event -> Maybe String transactionId : Event -> Maybe String
transactionId = transactionId =
withoutCredentials >> Internal.transactionId Snackbar.withoutCandy >> Internal.transactionId

View File

@ -3,7 +3,7 @@ module Internal.Invite exposing (..)
{-| An invite is an Elm type that informs the user they've been invited to a room. {-| An invite is an Elm type that informs the user they've been invited to a room.
-} -}
import Internal.Api.Credentials exposing (Credentials) import Internal.Api.Snackbar as Snackbar exposing (Snackbar)
import Internal.Api.Sync.V2.SpecObjects exposing (StrippedStateEvent) import Internal.Api.Sync.V2.SpecObjects exposing (StrippedStateEvent)
import Internal.Api.Task as Api import Internal.Api.Task as Api
import Internal.Api.VaultUpdate exposing (VaultUpdate(..)) import Internal.Api.VaultUpdate exposing (VaultUpdate(..))
@ -12,16 +12,32 @@ import Internal.Values.RoomInvite as Internal
import Task exposing (Task) import Task exposing (Task)
type RoomInvite type alias RoomInvite =
= RoomInvite Snackbar Internal.IRoomInvite
{ invite : Internal.IRoomInvite
, context : Credentials
accept : { invite : RoomInvite, reason : Maybe String } -> Task X.Error VaultUpdate
accept { invite, reason } =
Api.joinRoomById
{ roomId = roomId invite
, reason = reason
} }
invite
getRoomId : RoomInvite -> String roomId : RoomInvite -> String
getRoomId = roomId =
withoutCredentials >> Internal.roomId Snackbar.withoutCandy >> Internal.roomId
getEvent : { eventType : String, stateKey : String } -> RoomInvite -> Maybe Internal.RoomInviteEvent
getEvent data =
Snackbar.withoutCandy >> Internal.getEvent data
getAllEvents : RoomInvite -> List Internal.RoomInviteEvent
getAllEvents =
Snackbar.withoutCandy >> Internal.getAllEvents
initFromStrippedStateEvent : { roomId : String, events : List StrippedStateEvent } -> Internal.IRoomInvite initFromStrippedStateEvent : { roomId : String, events : List StrippedStateEvent } -> Internal.IRoomInvite
@ -29,47 +45,12 @@ initFromStrippedStateEvent =
Internal.init Internal.init
withCredentials : Credentials -> Internal.IRoomInvite -> RoomInvite
withCredentials context invite =
RoomInvite { context = context, invite = invite }
withoutCredentials : RoomInvite -> Internal.IRoomInvite
withoutCredentials (RoomInvite { invite }) =
invite
getEvent : { eventType : String, stateKey : String } -> RoomInvite -> Maybe Internal.RoomInviteEvent
getEvent data =
withoutCredentials >> Internal.getEvent data
getAllEvents : RoomInvite -> List Internal.RoomInviteEvent
getAllEvents =
withoutCredentials >> Internal.getAllEvents
{-| Accept an invite and join the room.
-}
accept : { invite : RoomInvite, reason : Maybe String } -> Task X.Error VaultUpdate
accept { invite, reason } =
case invite of
RoomInvite data ->
Api.joinRoomById
{ roomId = Internal.roomId data.invite
, reason = reason
}
data.context
{-| Reject the invite and do not join the room. {-| Reject the invite and do not join the room.
-} -}
reject : { invite : RoomInvite, reason : Maybe String } -> Task X.Error VaultUpdate reject : { invite : RoomInvite, reason : Maybe String } -> Task X.Error VaultUpdate
reject { invite, reason } = reject { invite, reason } =
case invite of
RoomInvite data ->
Api.leave Api.leave
{ roomId = Internal.roomId data.invite { roomId = roomId invite
, reason = reason , reason = reason
} }
data.context invite

View File

@ -4,7 +4,7 @@ module Internal.Room exposing (..)
-} -}
import Dict import Dict
import Internal.Api.Credentials exposing (Credentials) import Internal.Api.Snackbar as Snackbar exposing (Snackbar)
import Internal.Api.Sync.V2.SpecObjects as Sync import Internal.Api.Sync.V2.SpecObjects as Sync
import Internal.Api.Task as Api import Internal.Api.Task as Api
import Internal.Api.VaultUpdate exposing (VaultUpdate(..)) import Internal.Api.VaultUpdate exposing (VaultUpdate(..))
@ -28,11 +28,8 @@ The `Room` type contains utilities to inquire about the room and send messages
to it. to it.
-} -}
type Room type alias Room =
= Room Snackbar Internal.IRoom
{ room : Internal.IRoom
, context : Credentials
}
{-| Create a new object from a joined room. {-| Create a new object from a joined room.
@ -89,24 +86,22 @@ initFromJoinedRoom data jroom =
accountData : String -> Room -> Maybe E.Value accountData : String -> Room -> Maybe E.Value
accountData key = accountData key =
withoutCredentials >> Internal.accountData key Snackbar.withoutCandy >> Internal.accountData key
{-| Add account data to the room. {-| Add account data to the room.
-} -}
addAccountData : String -> E.Value -> Room -> Room addAccountData : String -> E.Value -> Room -> Room
addAccountData eventType content (Room { room, context }) = addAccountData eventType content =
room Snackbar.map (Internal.addAccountData eventType content)
|> Internal.addAccountData eventType content
|> withCredentials context
{-| Adds an internal event to the `Room`. An internal event is a custom event {-| Adds an internal event to the `Room`. An internal event is a custom event
that has been generated by the client. that has been generated by the client.
-} -}
addInternalEvent : IEvent.IEvent -> Room -> Room addInternalEvent : IEvent.IEvent -> Room -> Room
addInternalEvent ievent (Room ({ room } as data)) = addInternalEvent ievent =
Room { data | room = Internal.addEvent ievent room } Snackbar.map (Internal.addEvent ievent)
{-| Adds an `Event` object to the `Room`. An `Event` is a value from the {-| Adds an `Event` object to the `Room`. An `Event` is a value from the
@ -114,39 +109,21 @@ addInternalEvent ievent (Room ({ room } as data)) =
-} -}
addEvent : Event -> Room -> Room addEvent : Event -> Room -> Room
addEvent = addEvent =
Event.withoutCredentials >> addInternalEvent Snackbar.withoutCandy >> addInternalEvent
{-| Creates a new `Room` object with the given parameters.
-}
withCredentials : Credentials -> Internal.IRoom -> Room
withCredentials context room =
Room
{ context = context
, room = room
}
{-| Retrieves the `Internal.IRoom` type contained within the given `Room`.
-}
withoutCredentials : Room -> Internal.IRoom
withoutCredentials (Room { room }) =
room
{-| Get a given state event. {-| Get a given state event.
-} -}
getStateEvent : { eventType : String, stateKey : String } -> Room -> Maybe Event getStateEvent : { eventType : String, stateKey : String } -> Room -> Maybe Event
getStateEvent data (Room { room, context }) = getStateEvent data =
Internal.getStateEvent data room Snackbar.mapMaybe (Internal.getStateEvent data)
|> Maybe.map (Event.withCredentials context)
{-| Get older events from the Matrix API. {-| Get older events from the Matrix API.
-} -}
getOlderEvents : { limit : Maybe Int } -> Room -> Task X.Error VaultUpdate findOlderEvents : { limit : Maybe Int } -> Room -> Task X.Error VaultUpdate
getOlderEvents { limit } (Room { context, room }) = findOlderEvents { limit } room =
case Internal.latestGap room of case Internal.latestGap (Snackbar.withoutCandy room) of
Nothing -> Nothing ->
Task.succeed (MultipleUpdates []) Task.succeed (MultipleUpdates [])
@ -156,54 +133,52 @@ getOlderEvents { limit } (Room { context, room }) =
, filter = Nothing , filter = Nothing
, from = Just to , from = Just to
, limit = limit , limit = limit
, roomId = Internal.roomId room , roomId = roomId room
, to = from , to = from
} }
context room
{-| Get the most recent events. {-| Get the most recent events.
-} -}
mostRecentEvents : Room -> List Event mostRecentEvents : Room -> List Event
mostRecentEvents (Room { context, room }) = mostRecentEvents =
room Snackbar.mapList Internal.mostRecentEvents
|> Internal.mostRecentEvents
|> List.map (Event.withCredentials context)
{-| Retrieves the ID of the Matrix room associated with the given `Room`. {-| Retrieves the ID of the Matrix room associated with the given `Room`.
-} -}
roomId : Room -> String roomId : Room -> String
roomId = roomId =
withoutCredentials >> Internal.roomId Snackbar.withoutCandy >> Internal.roomId
{-| Sends a new event to the Matrix room associated with the given `Room`. {-| Sends a new event to the Matrix room associated with the given `Room`.
-} -}
sendEvent : { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> Task X.Error VaultUpdate sendEvent : { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> Task X.Error VaultUpdate
sendEvent { eventType, content, stateKey } (Room { context, room }) = sendEvent { eventType, content, stateKey } room =
case stateKey of case stateKey of
Nothing -> Nothing ->
Api.sendMessageEvent Api.sendMessageEvent
{ content = content { content = content
, eventType = eventType , eventType = eventType
, extraTransactionNoise = "send-one-message" , extraTransactionNoise = "send-one-message"
, roomId = Internal.roomId room , roomId = roomId room
} }
context room
Just s -> Just s ->
Api.sendStateEvent Api.sendStateEvent
{ content = content { content = content
, eventType = eventType , eventType = eventType
, stateKey = s , stateKey = s
, roomId = Internal.roomId room , roomId = roomId room
} }
context room
sendEvents : List { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> List (Task X.Error VaultUpdate) sendEvents : List { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> List (Task X.Error VaultUpdate)
sendEvents events (Room { context, room }) = sendEvents events room =
List.indexedMap Tuple.pair events List.indexedMap Tuple.pair events
|> List.map |> List.map
(\( i, { eventType, content, stateKey } ) -> (\( i, { eventType, content, stateKey } ) ->
@ -213,25 +188,25 @@ sendEvents events (Room { context, room }) =
{ content = content { content = content
, eventType = eventType , eventType = eventType
, extraTransactionNoise = "send-message-" ++ String.fromInt i , extraTransactionNoise = "send-message-" ++ String.fromInt i
, roomId = Internal.roomId room , roomId = roomId room
} }
context room
Just s -> Just s ->
Api.sendStateEvent Api.sendStateEvent
{ content = content { content = content
, eventType = eventType , eventType = eventType
, stateKey = s , stateKey = s
, roomId = Internal.roomId room , roomId = roomId room
} }
context room
) )
{-| Sends a new text message to the Matrix room associated with the given `Room`. {-| Sends a new text message to the Matrix room associated with the given `Room`.
-} -}
sendMessage : String -> Room -> Task X.Error VaultUpdate sendMessage : String -> Room -> Task X.Error VaultUpdate
sendMessage text (Room { context, room }) = sendMessage text room =
Api.sendMessageEvent Api.sendMessageEvent
{ content = { content =
E.object E.object
@ -240,13 +215,13 @@ sendMessage text (Room { context, room }) =
] ]
, eventType = "m.room.message" , eventType = "m.room.message"
, extraTransactionNoise = "literal-message:" ++ text , extraTransactionNoise = "literal-message:" ++ text
, roomId = Internal.roomId room , roomId = roomId room
} }
context room
sendMessages : List String -> Room -> List (Task X.Error VaultUpdate) sendMessages : List String -> Room -> List (Task X.Error VaultUpdate)
sendMessages pieces (Room { context, room }) = sendMessages pieces room =
pieces pieces
|> List.indexedMap Tuple.pair |> List.indexedMap Tuple.pair
|> List.map |> List.map
@ -259,21 +234,21 @@ sendMessages pieces (Room { context, room }) =
] ]
, eventType = "m.room.message" , eventType = "m.room.message"
, extraTransactionNoise = "literal-message-" ++ String.fromInt i ++ ":" ++ piece , extraTransactionNoise = "literal-message-" ++ String.fromInt i ++ ":" ++ piece
, roomId = Internal.roomId room , roomId = roomId room
} }
context room
) )
{-| Leave this room. {-| Leave this room.
-} -}
leave : Room -> Task X.Error VaultUpdate leave : Room -> Task X.Error VaultUpdate
leave ((Room { context }) as r) = leave room =
Api.leave { roomId = roomId r, reason = Nothing } context Api.leave { roomId = roomId room, reason = Nothing } room
{-| Set account data. {-| Set account data.
-} -}
setAccountData : String -> E.Value -> Room -> Task X.Error VaultUpdate setAccountData : String -> E.Value -> Room -> Task X.Error VaultUpdate
setAccountData key value ((Room { context }) as r) = setAccountData key value room =
Api.setAccountData { content = value, eventType = key, roomId = Just (roomId r) } context Api.setAccountData { content = value, eventType = key, roomId = Just (roomId room) } room

View File

@ -8,7 +8,7 @@ This file combines the internal functions with the API endpoints to create a ful
-} -}
import Dict import Dict
import Internal.Api.Credentials as Credentials exposing (Credentials) import Internal.Api.Snackbar as Snackbar exposing (Snackbar)
import Internal.Api.Sync.Main exposing (SyncInput) import Internal.Api.Sync.Main exposing (SyncInput)
import Internal.Api.Task as Api import Internal.Api.Task as Api
import Internal.Api.VaultUpdate exposing (VaultUpdate(..)) import Internal.Api.VaultUpdate exposing (VaultUpdate(..))
@ -30,11 +30,15 @@ and Elm will figure out which key to use.
If you pass the `Vault` into any function, then the library will look for If you pass the `Vault` into any function, then the library will look for
the right keys and tokens to get the right information. the right keys and tokens to get the right information.
-} -}
type Vault type alias Vault =
= Vault Snackbar Internal.IVault
{ cred : Internal.IVault
, context : Credentials
} {-| Get personal account data linked to an account.
-}
accountData : String -> Vault -> Maybe E.Value
accountData key =
Snackbar.withoutCandy >> Internal.accountData key
{-| Get a Vault type based on an unknown access token. {-| Get a Vault type based on an unknown access token.
@ -45,77 +49,66 @@ when the access token expires, is revoked or something else happens.
-} -}
fromAccessToken : { baseUrl : String, accessToken : String } -> Vault fromAccessToken : { baseUrl : String, accessToken : String } -> Vault
fromAccessToken { baseUrl, accessToken } = fromAccessToken { baseUrl, accessToken } =
Credentials.fromBaseUrl baseUrl Snackbar.init
|> Credentials.addToken accessToken { baseUrl = baseUrl
|> (\context -> , content = Internal.init
{ cred = Internal.init, context = context } }
) |> Snackbar.addToken accessToken
|> Vault
{-| Get a Vault type using a username and password. {-| Get a Vault type using a username and password.
-} -}
fromLoginVault : { username : String, password : String, baseUrl : String } -> Vault fromLoginVault : { username : String, password : String, baseUrl : String } -> Vault
fromLoginVault { username, password, baseUrl } = fromLoginVault { username, password, baseUrl } =
Credentials.fromBaseUrl baseUrl Snackbar.init
|> Credentials.addUsernameAndPassword { baseUrl = baseUrl
, content = Internal.init
}
|> Snackbar.addUsernameAndPassword
{ username = username { username = username
, password = password , password = password
} }
|> (\context ->
{ cred = Internal.init, context = context }
)
|> Vault
{-| Get personal account data linked to an account.
-}
accountData : String -> Vault -> Maybe E.Value
accountData key (Vault { cred }) =
Internal.accountData key cred
{-| Get a user's invited rooms. {-| Get a user's invited rooms.
-} -}
getInvites : Vault -> List Invite.RoomInvite invites : Vault -> List Invite.RoomInvite
getInvites (Vault { cred, context }) = invites =
Internal.getInvites cred Snackbar.mapList Internal.getInvites
|> List.map (Invite.withCredentials context)
{-| Get a room based on its id. {-| Get a room based on its id.
-} -}
getRoomById : String -> Vault -> Maybe Room.Room getRoomById : String -> Vault -> Maybe Room.Room
getRoomById roomId (Vault { cred, context }) = getRoomById roomId =
Internal.getRoomById roomId cred Snackbar.mapMaybe (Internal.getRoomById roomId)
|> Maybe.map (Room.withCredentials context)
{-| Insert an internal room type into the credentials. {-| Insert an internal room type into the vault.
-} -}
insertInternalRoom : IRoom.IRoom -> Vault -> Vault insertInternalRoom : IRoom.IRoom -> Vault -> Vault
insertInternalRoom iroom (Vault data) = insertInternalRoom iroom =
Vault { data | cred = Internal.insertRoom iroom data.cred } Snackbar.map (Internal.insertRoom iroom)
{-| Internal a full room type into the credentials. {-| Internal a full room type into the vault.
-} -}
insertRoom : Room.Room -> Vault -> Vault insertRoom : Room.Room -> Vault -> Vault
insertRoom = insertRoom =
Room.withoutCredentials >> insertInternalRoom Snackbar.withoutCandy >> insertInternalRoom
{-| Join a Matrix room by its id. {-| Join a Matrix room by its id.
-} -}
joinRoomById : String -> Vault -> Task X.Error VaultUpdate joinRoomById : String -> Vault -> Task X.Error VaultUpdate
joinRoomById roomId (Vault { context }) = joinRoomById roomId vault =
Api.joinRoomById { roomId = roomId, reason = Nothing } context Api.joinRoomById { roomId = roomId, reason = Nothing } vault
{-| Update the Vault type with new values {-| Update the Vault type with new values
-} -}
updateWith : VaultUpdate -> Vault -> Vault updateWith : VaultUpdate -> Vault -> Vault
updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = updateWith vaultUpdate vault =
case vaultUpdate of case vaultUpdate of
MultipleUpdates updates -> MultipleUpdates updates ->
List.foldl updateWith vault updates List.foldl updateWith vault updates
@ -143,7 +136,7 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
vault vault
CurrentTimestamp t -> CurrentTimestamp t ->
Vault { cred = Internal.insertTimestamp t cred, context = context } Snackbar.map (Internal.insertTimestamp t) vault
GetEvent input output -> GetEvent input output ->
case getRoomById input.roomId vault of case getRoomById input.roomId vault of
@ -192,7 +185,7 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
case ( getRoomById input.roomId vault, nextBatch ) of case ( getRoomById input.roomId vault, nextBatch ) of
( Just room, Just nb ) -> ( Just room, Just nb ) ->
room room
|> Room.withoutCredentials |> Snackbar.withoutCandy
|> IRoom.insertEvents |> IRoom.insertEvents
{ events = { events =
output.chunk output.chunk
@ -210,8 +203,8 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
, stateDelta = Just <| StateManager.fromEventList (List.map Event.initFromGetMessages output.state) , stateDelta = Just <| StateManager.fromEventList (List.map Event.initFromGetMessages output.state)
} }
|> Internal.insertRoom |> Internal.insertRoom
|> (|>) cred |> Snackbar.map
|> (\v -> Vault { cred = v, context = context }) |> (|>) vault
_ -> _ ->
vault vault
@ -226,35 +219,29 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
-- TODO -- TODO
JoinedRoom input _ -> JoinedRoom input _ ->
cred Snackbar.map (Internal.removeInvite input.roomId) vault
|> Internal.removeInvite input.roomId
|> (\x -> { cred = x, context = context })
|> Vault
-- TODO -- TODO
LeftRoom input () -> LeftRoom input () ->
cred Snackbar.map (Internal.removeInvite input.roomId) vault
|> Internal.removeInvite input.roomId
|> (\x -> { cred = x, context = context })
|> Vault
MessageEventSent { content, eventType, roomId } { eventId } -> MessageEventSent { content, eventType, roomId } { eventId } ->
Maybe.map2 Maybe.map2
(\room sender -> (\room sender ->
room room
|> Room.withoutCredentials |> Snackbar.withoutCandy
|> IRoom.addTemporaryEvent |> IRoom.addTemporaryEvent
{ content = content { content = content
, eventType = eventType , eventType = eventType
, eventId = eventId , eventId = eventId
, originServerTs = Internal.lastUpdate cred , originServerTs = Internal.lastUpdate (Snackbar.withoutCandy vault)
, sender = sender , sender = sender
, stateKey = Nothing , stateKey = Nothing
} }
) )
(getRoomById roomId vault) (getRoomById roomId vault)
(getUsername vault) (getUsername vault)
|> Maybe.map (Room.withCredentials context >> insertRoom >> (|>) vault) |> Maybe.map (Snackbar.withCandyFrom vault >> insertRoom >> (|>) vault)
|> Maybe.withDefault vault |> Maybe.withDefault vault
-- TODO -- TODO
@ -265,19 +252,19 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
Maybe.map2 Maybe.map2
(\room sender -> (\room sender ->
room room
|> Room.withoutCredentials |> Snackbar.withoutCandy
|> IRoom.addTemporaryEvent |> IRoom.addTemporaryEvent
{ content = content { content = content
, eventType = eventType , eventType = eventType
, eventId = eventId , eventId = eventId
, originServerTs = Internal.lastUpdate cred , originServerTs = Internal.lastUpdate (Snackbar.withoutCandy vault)
, sender = sender , sender = sender
, stateKey = Just stateKey , stateKey = Just stateKey
} }
) )
(getRoomById roomId vault) (getRoomById roomId vault)
(getUsername vault) (getUsername vault)
|> Maybe.map (Room.withCredentials context >> insertRoom >> (|>) vault) |> Maybe.map (Snackbar.withCandyFrom vault >> insertRoom >> (|>) vault)
|> Maybe.withDefault vault |> Maybe.withDefault vault
SyncUpdate input output -> SyncUpdate input output ->
@ -303,7 +290,7 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
(case jroom.timeline of (case jroom.timeline of
Just timeline -> Just timeline ->
room room
|> Room.withoutCredentials |> Snackbar.withoutCandy
|> IRoom.addEvents |> IRoom.addEvents
{ events = { events =
List.map List.map
@ -325,7 +312,7 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
} }
Nothing -> Nothing ->
Room.withoutCredentials room Snackbar.withoutCandy room
) )
|> (\r -> |> (\r ->
jroom.accountData jroom.accountData
@ -342,8 +329,8 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
|> Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId } |> Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId }
) )
invites : List IRoomInvite inviteList : List IRoomInvite
invites = inviteList =
output.rooms output.rooms
|> Maybe.map .invite |> Maybe.map .invite
|> Maybe.withDefault Dict.empty |> Maybe.withDefault Dict.empty
@ -354,7 +341,9 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
|> List.map (\( roomId, events ) -> { roomId = roomId, events = events }) |> List.map (\( roomId, events ) -> { roomId = roomId, events = events })
|> List.map Invite.initFromStrippedStateEvent |> List.map Invite.initFromStrippedStateEvent
in in
cred Snackbar.map
(\ivault ->
ivault
-- Add global account data -- Add global account data
|> (\c -> List.foldl Internal.insertAccountData c accData) |> (\c -> List.foldl Internal.insertAccountData c accData)
-- Add new since token -- Add new since token
@ -364,51 +353,50 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
|> (|>) jRooms |> (|>) jRooms
-- Add invites -- Add invites
|> List.foldl Internal.addInvite |> List.foldl Internal.addInvite
|> (|>) invites |> (|>) inviteList
|> (\x -> { cred = x, context = context }) )
|> Vault vault
UpdateAccessToken token -> UpdateAccessToken token ->
Vault { data | context = Credentials.addToken token context } Snackbar.addToken token vault
UpdateVersions versions -> UpdateVersions versions ->
Vault { data | context = Credentials.addVersions versions context } Snackbar.addVersions versions vault
UpdateWhoAmI whoami -> UpdateWhoAmI whoami ->
Vault { data | context = Credentials.addWhoAmI whoami context } Snackbar.addWhoAmI whoami vault
-- TODO: Save ALL info
LoggedInWithUsernameAndPassword _ output -> LoggedInWithUsernameAndPassword _ output ->
Vault { data | context = Credentials.addToken output.accessToken context } Snackbar.addToken output.accessToken vault
getUsername : Vault -> Maybe String getUsername : Vault -> Maybe String
getUsername (Vault { context }) = getUsername =
Credentials.getUserId context Snackbar.userId
{-| Set personal account data {-| Set personal account data
-} -}
setAccountData : String -> E.Value -> Vault -> Task X.Error VaultUpdate setAccountData : String -> E.Value -> Vault -> Task X.Error VaultUpdate
setAccountData key value (Vault { context }) = setAccountData key value vault =
Api.setAccountData { content = value, eventType = key, roomId = Nothing } context Api.setAccountData { content = value, eventType = key, roomId = Nothing } vault
{-| Synchronize vault {-| Synchronize vault
-} -}
sync : Vault -> Task X.Error VaultUpdate sync : Vault -> Task X.Error VaultUpdate
sync (Vault { cred, context }) = sync vault =
let let
syncInput : SyncInput syncInput : SyncInput
syncInput = syncInput =
{ filter = Nothing { filter = Nothing
, fullState = Nothing , fullState = Nothing
, setPresence = Nothing , setPresence = Nothing
, since = Internal.getSince cred , since = Internal.getSince (Snackbar.withoutCandy vault)
, timeout = Just 30 , timeout = Just 30
} }
in in
Api.sync syncInput context Api.sync syncInput vault
-- TODO: The sync function is described as "updating all the tokens". -- TODO: The sync function is described as "updating all the tokens".
-- TODO: For this reason, (only) the sync function should handle errors -- TODO: For this reason, (only) the sync function should handle errors
-- TODO: that indicate that the user's access tokens have expired. -- TODO: that indicate that the user's access tokens have expired.
@ -428,10 +416,10 @@ sync (Vault { cred, context }) =
-- TODO: The login should be different when soft_logout. -- TODO: The login should be different when soft_logout.
-- TODO: Add support for refresh token. -- TODO: Add support for refresh token.
X.ServerException (X.M_UNKNOWN_TOKEN _) -> X.ServerException (X.M_UNKNOWN_TOKEN _) ->
Api.loginMaybeSync syncInput context Api.loginMaybeSync syncInput vault
X.ServerException (X.M_MISSING_TOKEN _) -> X.ServerException (X.M_MISSING_TOKEN _) ->
Api.loginMaybeSync syncInput context Api.loginMaybeSync syncInput vault
X.ServerException _ -> X.ServerException _ ->
Task.fail err Task.fail err
@ -441,7 +429,5 @@ sync (Vault { cred, context }) =
{-| Get a list of all synchronised rooms. {-| Get a list of all synchronised rooms.
-} -}
rooms : Vault -> List Room.Room rooms : Vault -> List Room.Room
rooms (Vault { cred, context }) = rooms =
cred Snackbar.mapList Internal.getRooms
|> Internal.getRooms
|> List.map (Room.withCredentials context)

View File

@ -119,7 +119,7 @@ getRoomById =
-} -}
getInvites : Vault -> List RoomInvite getInvites : Vault -> List RoomInvite
getInvites = getInvites =
Internal.Vault.getInvites Internal.Vault.invites
{-| Account data is personal information that the homeserver will remember for you. {-| Account data is personal information that the homeserver will remember for you.

View File

@ -107,7 +107,7 @@ this inserts more events at the start of the `[mostRecentEvents](#mostRecentEven
-} -}
findOlderEvents : { limit : Maybe Int, room : Room } -> Task X.Error VaultUpdate findOlderEvents : { limit : Maybe Int, room : Room } -> Task X.Error VaultUpdate
findOlderEvents { limit, room } = findOlderEvents { limit, room } =
Internal.getOlderEvents { limit = limit } room Internal.findOlderEvents { limit = limit } room
{-| This function will always display the most recent events from the Matrix room. {-| This function will always display the most recent events from the Matrix room.

View File

@ -76,7 +76,7 @@ rejectWithReason reason invite =
-} -}
roomId : RoomInvite -> String roomId : RoomInvite -> String
roomId = roomId =
Internal.getRoomId Internal.roomId
{-| The `RoomInviteEvent` type represents a stripped event that your user can see while they haven't joined the group yet. {-| The `RoomInviteEvent` type represents a stripped event that your user can see while they haven't joined the group yet.
@ -117,13 +117,15 @@ stateKey =
IR.stateKey IR.stateKey
{-| Get a specific event with a specific event content type and state key, if it exists.
-} -- -- TODO: Fix this
getEvent : { eventType : String, stateKey : String } -> RoomInvite -> Maybe RoomInviteEvent -- {-| Get a specific event with a specific event content type and state key, if it exists.
getEvent data invite = -- -}
invite -- getEvent : { eventType : String, stateKey : String } -> RoomInvite -> Maybe RoomInviteEvent
|> Internal.withoutCredentials -- getEvent data invite =
|> IR.getEvent data -- invite
-- |> Internal.withoutCredentials
-- |> IR.getEvent data
{-| Instead of looking at just one event, get all events in a list. {-| Instead of looking at just one event, get all events in a list.