From 619cd53a3af4768a6b2ca6072205f3b8c4a9fac1 Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Wed, 12 Apr 2023 15:36:56 +0200 Subject: [PATCH] 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. --- src/Internal/Api/Snackbar.elm | 110 ++++++++++++++++++++ src/Internal/Api/Task.elm | 28 ++--- src/Internal/Api/VaultUpdate.elm | 26 ++--- src/Internal/Event.elm | 55 +++------- src/Internal/Invite.elm | 77 ++++++-------- src/Internal/Room.elm | 103 +++++++----------- src/Internal/Vault.elm | 172 ++++++++++++++----------------- src/Matrix.elm | 2 +- src/Matrix/Room.elm | 2 +- src/Matrix/RoomInvite.elm | 18 ++-- 10 files changed, 312 insertions(+), 281 deletions(-) create mode 100644 src/Internal/Api/Snackbar.elm diff --git a/src/Internal/Api/Snackbar.elm b/src/Internal/Api/Snackbar.elm new file mode 100644 index 0000000..53af49f --- /dev/null +++ b/src/Internal/Api/Snackbar.elm @@ -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 diff --git a/src/Internal/Api/Task.elm b/src/Internal/Api/Task.elm index e13db66..fc41b4c 100644 --- a/src/Internal/Api/Task.elm +++ b/src/Internal/Api/Task.elm @@ -5,7 +5,6 @@ module Internal.Api.Task exposing (..) import Hash import Internal.Api.Chain as Chain -import Internal.Api.Credentials as Cred exposing (Credentials) import Internal.Api.GetEvent.Main exposing (EventInput) import Internal.Api.GetMessages.Main exposing (GetMessagesInput) 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.SendStateKey.Main exposing (SendStateKeyInput) 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.VaultUpdate as C 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 = C.makeVBA cred |> Chain.andThen (C.withSentEvent eventId) @@ -37,35 +37,35 @@ getEvent { eventId, roomId } cred = |> C.toTask -getMessages : GetMessagesInput -> Credentials -> FutureTask +getMessages : GetMessagesInput -> Snackbar a -> FutureTask getMessages data cred = C.makeVBA cred |> Chain.andThen (C.getMessages data) |> C.toTask -invite : InviteInput -> Credentials -> FutureTask +invite : InviteInput -> Snackbar a -> FutureTask invite data cred = C.makeVBA cred |> Chain.andThen (C.invite data) |> C.toTask -joinedMembers : JoinedMembersInput -> Credentials -> FutureTask +joinedMembers : JoinedMembersInput -> Snackbar a -> FutureTask joinedMembers data cred = C.makeVBA cred |> Chain.andThen (C.joinedMembers data) |> C.toTask -joinRoomById : JoinRoomByIdInput -> Credentials -> FutureTask +joinRoomById : JoinRoomByIdInput -> Snackbar a -> FutureTask joinRoomById data cred = C.makeVBA cred |> Chain.andThen (C.joinRoomById data) |> C.toTask -leave : LeaveInput -> Credentials -> FutureTask +leave : LeaveInput -> Snackbar a -> FutureTask leave data cred = C.makeVBA cred |> 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 = cred |> C.makeVBAT @@ -109,7 +109,7 @@ type alias SendMessageEventInput = } -sendMessageEvent : SendMessageEventInput -> Credentials -> FutureTask +sendMessageEvent : SendMessageEventInput -> Snackbar a -> FutureTask sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred = cred |> C.makeVBAT @@ -130,7 +130,7 @@ sendMessageEvent { content, eventType, extraTransactionNoise, roomId } cred = |> C.toTask -sendStateEvent : SendStateKeyInput -> Credentials -> FutureTask +sendStateEvent : SendStateKeyInput -> Snackbar a -> FutureTask sendStateEvent data cred = C.makeVBA cred |> Chain.andThen C.getTimestamp @@ -140,24 +140,24 @@ sendStateEvent data cred = |> C.toTask -setAccountData : SetAccountInput -> Credentials -> FutureTask +setAccountData : SetAccountInput -> Snackbar a -> FutureTask setAccountData data cred = C.makeVBA cred |> Chain.andThen (C.setAccountData data) |> C.toTask -sync : SyncInput -> Credentials -> FutureTask +sync : SyncInput -> Snackbar a -> FutureTask sync data cred = C.makeVBA cred |> Chain.andThen (C.sync data) |> C.toTask -loginMaybeSync : SyncInput -> Credentials -> FutureTask +loginMaybeSync : SyncInput -> Snackbar a -> FutureTask loginMaybeSync data cred = C.makeVB cred - |> Chain.andThen (C.accessToken (Cred.refreshedAccessToken cred)) + |> Chain.andThen (C.accessToken (Snackbar.removedAccessToken cred)) |> Chain.andThen (Chain.maybe <| C.sync data) |> C.toTask diff --git a/src/Internal/Api/VaultUpdate.elm b/src/Internal/Api/VaultUpdate.elm index f12420a..422c9cd 100644 --- a/src/Internal/Api/VaultUpdate.elm +++ b/src/Internal/Api/VaultUpdate.elm @@ -2,7 +2,6 @@ module Internal.Api.VaultUpdate exposing (..) import Internal.Api.Ban.Main as Ban 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.GetMessages.Main as GetMessages 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.SendStateKey.Main as SendStateKey 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.Versions.Main as Versions import Internal.Api.Versions.V1.Versions as V @@ -296,28 +296,28 @@ loginWithUsernameAndPassword input = {-| Make a VB-context based chain. -} -makeVB : Credentials -> TaskChain VaultUpdate {} (VB {}) -makeVB cred = - cred - |> Credentials.baseUrl +makeVB : Snackbar a -> TaskChain VaultUpdate {} (VB {}) +makeVB snackbar = + snackbar + |> Snackbar.baseUrl |> withBaseUrl - |> Chain.andThen (versions (Credentials.versions cred)) + |> Chain.andThen (versions (Snackbar.versions snackbar)) {-| Make a VBA-context based chain. -} -makeVBA : Credentials -> TaskChain VaultUpdate {} (VBA { userId : () }) -makeVBA cred = - cred +makeVBA : Snackbar a -> TaskChain VaultUpdate {} (VBA { userId : () }) +makeVBA snackbar = + snackbar |> makeVB - |> Chain.andThen (accessToken (Credentials.accessToken cred)) + |> Chain.andThen (accessToken (Snackbar.accessToken snackbar)) {-| Make a VBAT-context based chain. -} -makeVBAT : (Int -> String) -> Credentials -> TaskChain VaultUpdate {} (VBAT { userId : () }) -makeVBAT toString cred = - cred +makeVBAT : (Int -> String) -> Snackbar a -> TaskChain VaultUpdate {} (VBAT { userId : () }) +makeVBAT toString snackbar = + snackbar |> makeVBA |> Chain.andThen (withTransactionId toString) diff --git a/src/Internal/Event.elm b/src/Internal/Event.elm index fb189e9..00d4e79 100644 --- a/src/Internal/Event.elm +++ b/src/Internal/Event.elm @@ -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.V1.SpecObjects as GetEventSO 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.Tools.Timestamp exposing (Timestamp) 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. -} -type Event - = Event - { 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 - } +type alias Event = + Snackbar Internal.IEvent {-| 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 -} content : Event -> E.Value content = - withoutCredentials >> Internal.content + Snackbar.withoutCandy >> Internal.content eventId : Event -> String eventId = - withoutCredentials >> Internal.eventId + Snackbar.withoutCandy >> Internal.eventId originServerTs : Event -> Timestamp originServerTs = - withoutCredentials >> Internal.originServerTs + Snackbar.withoutCandy >> Internal.originServerTs roomId : Event -> String roomId = - withoutCredentials >> Internal.roomId + Snackbar.withoutCandy >> Internal.roomId sender : Event -> String sender = - withoutCredentials >> Internal.sender + Snackbar.withoutCandy >> Internal.sender stateKey : Event -> Maybe String stateKey = - withoutCredentials >> Internal.stateKey + Snackbar.withoutCandy >> Internal.stateKey eventType : Event -> String eventType = - withoutCredentials >> Internal.eventType + Snackbar.withoutCandy >> Internal.eventType age : Event -> Maybe Int age = - withoutCredentials >> Internal.age + Snackbar.withoutCandy >> Internal.age redactedBecause : Event -> Maybe Event -redactedBecause (Event data) = - data.event +redactedBecause event = + event + |> Snackbar.withoutCandy |> Internal.redactedBecause - |> Maybe.map - (\event -> - Event { data | event = event } - ) + |> Maybe.map (Snackbar.withCandyFrom event) transactionId : Event -> Maybe String transactionId = - withoutCredentials >> Internal.transactionId + Snackbar.withoutCandy >> Internal.transactionId diff --git a/src/Internal/Invite.elm b/src/Internal/Invite.elm index f1335f2..bc05c72 100644 --- a/src/Internal/Invite.elm +++ b/src/Internal/Invite.elm @@ -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. -} -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.Task as Api import Internal.Api.VaultUpdate exposing (VaultUpdate(..)) @@ -12,16 +12,32 @@ import Internal.Values.RoomInvite as Internal import Task exposing (Task) -type RoomInvite - = RoomInvite - { invite : Internal.IRoomInvite - , context : Credentials +type alias RoomInvite = + Snackbar Internal.IRoomInvite + + +accept : { invite : RoomInvite, reason : Maybe String } -> Task X.Error VaultUpdate +accept { invite, reason } = + Api.joinRoomById + { roomId = roomId invite + , reason = reason } + invite -getRoomId : RoomInvite -> String -getRoomId = - withoutCredentials >> Internal.roomId +roomId : RoomInvite -> String +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 @@ -29,47 +45,12 @@ initFromStrippedStateEvent = 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 : { invite : RoomInvite, reason : Maybe String } -> Task X.Error VaultUpdate reject { invite, reason } = - case invite of - RoomInvite data -> - Api.leave - { roomId = Internal.roomId data.invite - , reason = reason - } - data.context + Api.leave + { roomId = roomId invite + , reason = reason + } + invite diff --git a/src/Internal/Room.elm b/src/Internal/Room.elm index 32ad1c5..b0ec237 100644 --- a/src/Internal/Room.elm +++ b/src/Internal/Room.elm @@ -4,7 +4,7 @@ module Internal.Room exposing (..) -} 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.Task as Api 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. -} -type Room - = Room - { room : Internal.IRoom - , context : Credentials - } +type alias Room = + Snackbar Internal.IRoom {-| Create a new object from a joined room. @@ -89,24 +86,22 @@ initFromJoinedRoom data jroom = accountData : String -> Room -> Maybe E.Value accountData key = - withoutCredentials >> Internal.accountData key + Snackbar.withoutCandy >> Internal.accountData key {-| Add account data to the room. -} addAccountData : String -> E.Value -> Room -> Room -addAccountData eventType content (Room { room, context }) = - room - |> Internal.addAccountData eventType content - |> withCredentials context +addAccountData eventType content = + Snackbar.map (Internal.addAccountData eventType content) {-| Adds an internal event to the `Room`. An internal event is a custom event that has been generated by the client. -} addInternalEvent : IEvent.IEvent -> Room -> Room -addInternalEvent ievent (Room ({ room } as data)) = - Room { data | room = Internal.addEvent ievent room } +addInternalEvent ievent = + Snackbar.map (Internal.addEvent ievent) {-| 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.withoutCredentials >> 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 + Snackbar.withoutCandy >> addInternalEvent {-| Get a given state event. -} getStateEvent : { eventType : String, stateKey : String } -> Room -> Maybe Event -getStateEvent data (Room { room, context }) = - Internal.getStateEvent data room - |> Maybe.map (Event.withCredentials context) +getStateEvent data = + Snackbar.mapMaybe (Internal.getStateEvent data) {-| Get older events from the Matrix API. -} -getOlderEvents : { limit : Maybe Int } -> Room -> Task X.Error VaultUpdate -getOlderEvents { limit } (Room { context, room }) = - case Internal.latestGap room of +findOlderEvents : { limit : Maybe Int } -> Room -> Task X.Error VaultUpdate +findOlderEvents { limit } room = + case Internal.latestGap (Snackbar.withoutCandy room) of Nothing -> Task.succeed (MultipleUpdates []) @@ -156,54 +133,52 @@ getOlderEvents { limit } (Room { context, room }) = , filter = Nothing , from = Just to , limit = limit - , roomId = Internal.roomId room + , roomId = roomId room , to = from } - context + room {-| Get the most recent events. -} mostRecentEvents : Room -> List Event -mostRecentEvents (Room { context, room }) = - room - |> Internal.mostRecentEvents - |> List.map (Event.withCredentials context) +mostRecentEvents = + Snackbar.mapList Internal.mostRecentEvents {-| Retrieves the ID of the Matrix room associated with the given `Room`. -} roomId : Room -> String roomId = - withoutCredentials >> Internal.roomId + Snackbar.withoutCandy >> Internal.roomId {-| 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 { eventType, content, stateKey } (Room { context, room }) = +sendEvent { eventType, content, stateKey } room = case stateKey of Nothing -> Api.sendMessageEvent { content = content , eventType = eventType , extraTransactionNoise = "send-one-message" - , roomId = Internal.roomId room + , roomId = roomId room } - context + room Just s -> Api.sendStateEvent { content = content , eventType = eventType , 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 events (Room { context, room }) = +sendEvents events room = List.indexedMap Tuple.pair events |> List.map (\( i, { eventType, content, stateKey } ) -> @@ -213,25 +188,25 @@ sendEvents events (Room { context, room }) = { content = content , eventType = eventType , extraTransactionNoise = "send-message-" ++ String.fromInt i - , roomId = Internal.roomId room + , roomId = roomId room } - context + room Just s -> Api.sendStateEvent { content = content , eventType = eventType , 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`. -} sendMessage : String -> Room -> Task X.Error VaultUpdate -sendMessage text (Room { context, room }) = +sendMessage text room = Api.sendMessageEvent { content = E.object @@ -240,13 +215,13 @@ sendMessage text (Room { context, room }) = ] , eventType = "m.room.message" , extraTransactionNoise = "literal-message:" ++ text - , roomId = Internal.roomId room + , roomId = roomId room } - context + room sendMessages : List String -> Room -> List (Task X.Error VaultUpdate) -sendMessages pieces (Room { context, room }) = +sendMessages pieces room = pieces |> List.indexedMap Tuple.pair |> List.map @@ -259,21 +234,21 @@ sendMessages pieces (Room { context, room }) = ] , eventType = "m.room.message" , extraTransactionNoise = "literal-message-" ++ String.fromInt i ++ ":" ++ piece - , roomId = Internal.roomId room + , roomId = roomId room } - context + room ) {-| Leave this room. -} leave : Room -> Task X.Error VaultUpdate -leave ((Room { context }) as r) = - Api.leave { roomId = roomId r, reason = Nothing } context +leave room = + Api.leave { roomId = roomId room, reason = Nothing } room {-| Set account data. -} setAccountData : String -> E.Value -> Room -> Task X.Error VaultUpdate -setAccountData key value ((Room { context }) as r) = - Api.setAccountData { content = value, eventType = key, roomId = Just (roomId r) } context +setAccountData key value room = + Api.setAccountData { content = value, eventType = key, roomId = Just (roomId room) } room diff --git a/src/Internal/Vault.elm b/src/Internal/Vault.elm index 0067cc6..08dca1a 100644 --- a/src/Internal/Vault.elm +++ b/src/Internal/Vault.elm @@ -8,7 +8,7 @@ This file combines the internal functions with the API endpoints to create a ful -} 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.Task as Api 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 the right keys and tokens to get the right information. -} -type Vault - = Vault - { cred : Internal.IVault - , context : Credentials - } +type alias Vault = + Snackbar Internal.IVault + + +{-| 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. @@ -45,77 +49,66 @@ when the access token expires, is revoked or something else happens. -} fromAccessToken : { baseUrl : String, accessToken : String } -> Vault fromAccessToken { baseUrl, accessToken } = - Credentials.fromBaseUrl baseUrl - |> Credentials.addToken accessToken - |> (\context -> - { cred = Internal.init, context = context } - ) - |> Vault + Snackbar.init + { baseUrl = baseUrl + , content = Internal.init + } + |> Snackbar.addToken accessToken {-| Get a Vault type using a username and password. -} fromLoginVault : { username : String, password : String, baseUrl : String } -> Vault fromLoginVault { username, password, baseUrl } = - Credentials.fromBaseUrl baseUrl - |> Credentials.addUsernameAndPassword + Snackbar.init + { baseUrl = baseUrl + , content = Internal.init + } + |> Snackbar.addUsernameAndPassword { username = username , 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. -} -getInvites : Vault -> List Invite.RoomInvite -getInvites (Vault { cred, context }) = - Internal.getInvites cred - |> List.map (Invite.withCredentials context) +invites : Vault -> List Invite.RoomInvite +invites = + Snackbar.mapList Internal.getInvites {-| Get a room based on its id. -} getRoomById : String -> Vault -> Maybe Room.Room -getRoomById roomId (Vault { cred, context }) = - Internal.getRoomById roomId cred - |> Maybe.map (Room.withCredentials context) +getRoomById roomId = + Snackbar.mapMaybe (Internal.getRoomById roomId) -{-| Insert an internal room type into the credentials. +{-| Insert an internal room type into the vault. -} insertInternalRoom : IRoom.IRoom -> Vault -> Vault -insertInternalRoom iroom (Vault data) = - Vault { data | cred = Internal.insertRoom iroom data.cred } +insertInternalRoom iroom = + 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.withoutCredentials >> insertInternalRoom + Snackbar.withoutCandy >> insertInternalRoom {-| Join a Matrix room by its id. -} joinRoomById : String -> Vault -> Task X.Error VaultUpdate -joinRoomById roomId (Vault { context }) = - Api.joinRoomById { roomId = roomId, reason = Nothing } context +joinRoomById roomId vault = + Api.joinRoomById { roomId = roomId, reason = Nothing } vault {-| Update the Vault type with new values -} updateWith : VaultUpdate -> Vault -> Vault -updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = +updateWith vaultUpdate vault = case vaultUpdate of MultipleUpdates updates -> List.foldl updateWith vault updates @@ -143,7 +136,7 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = vault CurrentTimestamp t -> - Vault { cred = Internal.insertTimestamp t cred, context = context } + Snackbar.map (Internal.insertTimestamp t) vault GetEvent input output -> 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 ( Just room, Just nb ) -> room - |> Room.withoutCredentials + |> Snackbar.withoutCandy |> IRoom.insertEvents { events = 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) } |> Internal.insertRoom - |> (|>) cred - |> (\v -> Vault { cred = v, context = context }) + |> Snackbar.map + |> (|>) vault _ -> vault @@ -226,35 +219,29 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = -- TODO JoinedRoom input _ -> - cred - |> Internal.removeInvite input.roomId - |> (\x -> { cred = x, context = context }) - |> Vault + Snackbar.map (Internal.removeInvite input.roomId) vault -- TODO LeftRoom input () -> - cred - |> Internal.removeInvite input.roomId - |> (\x -> { cred = x, context = context }) - |> Vault + Snackbar.map (Internal.removeInvite input.roomId) vault MessageEventSent { content, eventType, roomId } { eventId } -> Maybe.map2 (\room sender -> room - |> Room.withoutCredentials + |> Snackbar.withoutCandy |> IRoom.addTemporaryEvent { content = content , eventType = eventType , eventId = eventId - , originServerTs = Internal.lastUpdate cred + , originServerTs = Internal.lastUpdate (Snackbar.withoutCandy vault) , sender = sender , stateKey = Nothing } ) (getRoomById roomId vault) (getUsername vault) - |> Maybe.map (Room.withCredentials context >> insertRoom >> (|>) vault) + |> Maybe.map (Snackbar.withCandyFrom vault >> insertRoom >> (|>) vault) |> Maybe.withDefault vault -- TODO @@ -265,19 +252,19 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = Maybe.map2 (\room sender -> room - |> Room.withoutCredentials + |> Snackbar.withoutCandy |> IRoom.addTemporaryEvent { content = content , eventType = eventType , eventId = eventId - , originServerTs = Internal.lastUpdate cred + , originServerTs = Internal.lastUpdate (Snackbar.withoutCandy vault) , sender = sender , stateKey = Just stateKey } ) (getRoomById roomId vault) (getUsername vault) - |> Maybe.map (Room.withCredentials context >> insertRoom >> (|>) vault) + |> Maybe.map (Snackbar.withCandyFrom vault >> insertRoom >> (|>) vault) |> Maybe.withDefault vault SyncUpdate input output -> @@ -303,7 +290,7 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = (case jroom.timeline of Just timeline -> room - |> Room.withoutCredentials + |> Snackbar.withoutCandy |> IRoom.addEvents { events = List.map @@ -325,7 +312,7 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = } Nothing -> - Room.withoutCredentials room + Snackbar.withoutCandy room ) |> (\r -> jroom.accountData @@ -342,8 +329,8 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = |> Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId } ) - invites : List IRoomInvite - invites = + inviteList : List IRoomInvite + inviteList = output.rooms |> Maybe.map .invite |> Maybe.withDefault Dict.empty @@ -354,61 +341,62 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) = |> List.map (\( roomId, events ) -> { roomId = roomId, events = events }) |> List.map Invite.initFromStrippedStateEvent in - cred - -- Add global account data - |> (\c -> List.foldl Internal.insertAccountData c accData) - -- Add new since token - |> Internal.addSince output.nextBatch - -- Add joined rooms - |> List.foldl Internal.insertRoom - |> (|>) jRooms - -- Add invites - |> List.foldl Internal.addInvite - |> (|>) invites - |> (\x -> { cred = x, context = context }) - |> Vault + Snackbar.map + (\ivault -> + ivault + -- Add global account data + |> (\c -> List.foldl Internal.insertAccountData c accData) + -- Add new since token + |> Internal.addSince output.nextBatch + -- Add joined rooms + |> List.foldl Internal.insertRoom + |> (|>) jRooms + -- Add invites + |> List.foldl Internal.addInvite + |> (|>) inviteList + ) + vault UpdateAccessToken token -> - Vault { data | context = Credentials.addToken token context } + Snackbar.addToken token vault UpdateVersions versions -> - Vault { data | context = Credentials.addVersions versions context } + Snackbar.addVersions versions vault UpdateWhoAmI whoami -> - Vault { data | context = Credentials.addWhoAmI whoami context } + Snackbar.addWhoAmI whoami vault - -- TODO: Save ALL info LoggedInWithUsernameAndPassword _ output -> - Vault { data | context = Credentials.addToken output.accessToken context } + Snackbar.addToken output.accessToken vault getUsername : Vault -> Maybe String -getUsername (Vault { context }) = - Credentials.getUserId context +getUsername = + Snackbar.userId {-| Set personal account data -} setAccountData : String -> E.Value -> Vault -> Task X.Error VaultUpdate -setAccountData key value (Vault { context }) = - Api.setAccountData { content = value, eventType = key, roomId = Nothing } context +setAccountData key value vault = + Api.setAccountData { content = value, eventType = key, roomId = Nothing } vault {-| Synchronize vault -} sync : Vault -> Task X.Error VaultUpdate -sync (Vault { cred, context }) = +sync vault = let syncInput : SyncInput syncInput = { filter = Nothing , fullState = Nothing , setPresence = Nothing - , since = Internal.getSince cred + , since = Internal.getSince (Snackbar.withoutCandy vault) , timeout = Just 30 } in - Api.sync syncInput context + Api.sync syncInput vault -- TODO: The sync function is described as "updating all the tokens". -- TODO: For this reason, (only) the sync function should handle errors -- 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: Add support for refresh token. X.ServerException (X.M_UNKNOWN_TOKEN _) -> - Api.loginMaybeSync syncInput context + Api.loginMaybeSync syncInput vault X.ServerException (X.M_MISSING_TOKEN _) -> - Api.loginMaybeSync syncInput context + Api.loginMaybeSync syncInput vault X.ServerException _ -> Task.fail err @@ -441,7 +429,5 @@ sync (Vault { cred, context }) = {-| Get a list of all synchronised rooms. -} rooms : Vault -> List Room.Room -rooms (Vault { cred, context }) = - cred - |> Internal.getRooms - |> List.map (Room.withCredentials context) +rooms = + Snackbar.mapList Internal.getRooms diff --git a/src/Matrix.elm b/src/Matrix.elm index e791d07..d55ad63 100644 --- a/src/Matrix.elm +++ b/src/Matrix.elm @@ -119,7 +119,7 @@ getRoomById = -} getInvites : Vault -> List RoomInvite getInvites = - Internal.Vault.getInvites + Internal.Vault.invites {-| Account data is personal information that the homeserver will remember for you. diff --git a/src/Matrix/Room.elm b/src/Matrix/Room.elm index 93cfaef..27ea48d 100644 --- a/src/Matrix/Room.elm +++ b/src/Matrix/Room.elm @@ -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, room } = - Internal.getOlderEvents { limit = limit } room + Internal.findOlderEvents { limit = limit } room {-| This function will always display the most recent events from the Matrix room. diff --git a/src/Matrix/RoomInvite.elm b/src/Matrix/RoomInvite.elm index 49e6021..70d76bf 100644 --- a/src/Matrix/RoomInvite.elm +++ b/src/Matrix/RoomInvite.elm @@ -76,7 +76,7 @@ rejectWithReason reason invite = -} roomId : RoomInvite -> String 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. @@ -117,13 +117,15 @@ stateKey = IR.stateKey -{-| Get a specific event with a specific event content type and state key, if it exists. --} -getEvent : { eventType : String, stateKey : String } -> RoomInvite -> Maybe RoomInviteEvent -getEvent data invite = - invite - |> Internal.withoutCredentials - |> IR.getEvent data + +-- -- TODO: Fix this +-- {-| Get a specific event with a specific event content type and state key, if it exists. +-- -} +-- getEvent : { eventType : String, stateKey : String } -> RoomInvite -> Maybe RoomInviteEvent +-- getEvent data invite = +-- invite +-- |> Internal.withoutCredentials +-- |> IR.getEvent data {-| Instead of looking at just one event, get all events in a list.