From caab0ae0bbb56b9fe75958861cd2296aa4737c0f Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Thu, 2 Mar 2023 14:55:08 +0100 Subject: [PATCH] Finish 1st version of open types The code is very messy and there are a few optimizations to be made before the code is both user- and developer-friendly, but at least it works. --- src/Internal/Api/PreApi/Main.elm | 6 +- src/Internal/Credentials.elm | 135 ++++++++++++++++++++-------- src/Internal/Event.elm | 4 +- src/Internal/Room.elm | 71 +++++++++++++++ src/Internal/Values/Credentials.elm | 30 ++++++- src/Internal/Values/Room.elm | 7 ++ 6 files changed, 210 insertions(+), 43 deletions(-) diff --git a/src/Internal/Api/PreApi/Main.elm b/src/Internal/Api/PreApi/Main.elm index 0835077..837abfa 100644 --- a/src/Internal/Api/PreApi/Main.elm +++ b/src/Internal/Api/PreApi/Main.elm @@ -17,7 +17,7 @@ import Task import Time -accessToken : String -> AccessToken -> ValueGetter String +accessToken : String -> AccessToken -> ValueGetter X.Error String accessToken baseUrl t = { value = case t of @@ -37,7 +37,7 @@ accessToken baseUrl t = } -transactionId : (Int -> String) -> ValueGetter String +transactionId : (Int -> String) -> ValueGetter X.Error String transactionId seeder = { value = Nothing , getValue = @@ -47,7 +47,7 @@ transactionId seeder = } -versions : String -> Maybe V.Versions -> ValueGetter V.Versions +versions : String -> Maybe V.Versions -> ValueGetter X.Error V.Versions versions baseUrl mVersions = { value = mVersions , getValue = diff --git a/src/Internal/Credentials.elm b/src/Internal/Credentials.elm index cee77bf..bcdab2b 100644 --- a/src/Internal/Credentials.elm +++ b/src/Internal/Credentials.elm @@ -9,11 +9,14 @@ This file combines the internal functions with the API endpoints to create a ful import Dict import Internal.Api.All as Api -import Internal.Room as Room import Internal.Event as Event +import Internal.Room as Room +import Internal.Tools.Exceptions as X import Internal.Values.Credentials as Internal import Internal.Values.Event as IEvent import Internal.Values.Room as IRoom +import Internal.Values.StateManager as StateManager +import Task exposing (Task) {-| You can consider the `Credentials` type as a large ring of keys, @@ -56,70 +59,128 @@ getRoomById roomId credentials = } ) + {-| Insert an internal room type into the credentials. -} insertInternalRoom : IRoom.Room -> Credentials -> Credentials -insertInternalRoom = Internal.insertRoom +insertInternalRoom = + Internal.insertRoom -{-| Internal a full room type into the credentials. -} + +{-| Internal a full room type into the credentials. +-} insertRoom : Room.Room -> Credentials -> Credentials -insertRoom = Room.internalValue >> insertInternalRoom +insertRoom = + Room.internalValue >> insertInternalRoom -{-| Update the Credentials type with new values -} + +{-| Update the Credentials type with new values +-} updateWith : Api.CredUpdate -> Credentials -> Credentials updateWith credUpdate credentials = case credUpdate of Api.MultipleUpdates updates -> List.foldl updateWith credentials updates - + Api.GetEvent input output -> case getRoomById input.roomId credentials of Just room -> output - |> IEvent.initFromGetEvent + |> Event.initFromGetEvent |> Room.addInternalEvent |> (|>) room |> insertRoom |> (|>) credentials - + Nothing -> credentials - - Api.JoinedMembersToRoom _ _ -> - credentials -- TODO - - Api.MessageEventSent _ _ -> - credentials -- TODO - - Api.StateEventSent _ _ -> - credentials -- TODO + Api.JoinedMembersToRoom _ _ -> + credentials + + -- TODO + Api.MessageEventSent _ _ -> + credentials + + -- TODO + Api.StateEventSent _ _ -> + credentials + + -- TODO Api.SyncUpdate input output -> let - rooms = + jRooms = output.rooms - |> Maybe.map .join - |> Maybe.withDefault Dict.empty - |> Dict.toList - |> List.map - (\(roomId, jroom)-> - case getRoomById roomId credentials of - -- Update existing room - Just room -> - room - |> Room.internalValue - |> IRoom.addEvents - + |> Maybe.map .join + |> Maybe.withDefault Dict.empty + |> Dict.toList + |> List.map + (\( roomId, jroom ) -> + case getRoomById roomId credentials of + -- Update existing room + Just room -> + room + |> Room.internalValue + |> IRoom.addEvents + { events = + jroom.timeline + |> Maybe.map .events + |> Maybe.withDefault [] + |> List.map (Event.initFromClientEventWithoutRoomId roomId) + , nextBatch = output.nextBatch + , prevBatch = + jroom.timeline + |> Maybe.andThen .prevBatch + |> Maybe.withDefault (Maybe.withDefault "" input.since) + , stateDelta = + jroom.state + |> Maybe.map + (.events + >> List.map (Event.initFromClientEventWithoutRoomId roomId) + >> StateManager.fromEventList + ) + } - -- Add new room - Nothing -> - jroom - ) + -- Add new room + Nothing -> + Room.initFromJoinedRoom { nextBatch = output.nextBatch, roomId = roomId } jroom + ) in - credentials - + List.foldl Internal.insertRoom (Internal.addSince output.nextBatch credentials) jRooms + Api.UpdateAccessToken token -> Internal.addAccessToken token credentials - + Api.UpdateVersions versions -> Internal.addVersions versions credentials + + +{-| Synchronize credentials +-} +sync : Credentials -> Task X.Error Api.CredUpdate +sync credentials = + Api.syncCredentials + { accessToken = Internal.getAccessTokenType credentials + , baseUrl = Internal.getBaseUrl credentials + , filter = Nothing + , fullState = Nothing + , setPresence = Nothing + , since = Internal.getSince credentials + , timeout = Just 30 + , versions = Internal.getVersions credentials + } + + +{-| Get a list of all synchronised rooms. +-} +rooms : Credentials -> List Room.Room +rooms credentials = + credentials + |> Internal.getRooms + |> ({ accessToken = Internal.getAccessTokenType credentials + , baseUrl = Internal.getBaseUrl credentials + , versions = Internal.getVersions credentials + } + |> Room.init + |> List.map + ) diff --git a/src/Internal/Event.elm b/src/Internal/Event.elm index ffe9a5d..fe831cc 100644 --- a/src/Internal/Event.elm +++ b/src/Internal/Event.elm @@ -10,7 +10,7 @@ resend other events or forward them elsewhere. import Internal.Api.GetEvent.Main as GetEvent import Internal.Api.GetEvent.V1.SpecObjects as GetEventSO import Internal.Api.PreApi.Objects.Versions as V -import Internal.Api.Sync.V1.SpecObjects as SyncSO +import Internal.Api.Sync.V2.SpecObjects as SyncSO import Internal.Tools.LoginValues exposing (AccessToken) import Internal.Tools.Timestamp exposing (Timestamp) import Internal.Values.Event as Internal @@ -86,7 +86,7 @@ initFromClientEventWithoutRoomId rId output = (\(SyncSO.UnsignedData data) -> { age = data.age , prevContent = data.prevContent - , redactedBecause = Maybe.map (initFromClientEventWithoutRoomId roomId) data.redactedBecause + , redactedBecause = Maybe.map (initFromClientEventWithoutRoomId rId) data.redactedBecause , transactionId = data.transactionId } ) diff --git a/src/Internal/Room.elm b/src/Internal/Room.elm index 44c510a..e835116 100644 --- a/src/Internal/Room.elm +++ b/src/Internal/Room.elm @@ -3,13 +3,18 @@ module Internal.Room exposing (..) {-| The `Room` type represents a Matrix Room. In here, you will find utilities to ask information about a room. -} +import Dict import Internal.Api.All as Api import Internal.Api.PreApi.Objects.Versions as V +import Internal.Api.Sync.V2.SpecObjects as Sync import Internal.Event as Event exposing (Event) import Internal.Tools.Exceptions as X +import Internal.Tools.Hashdict as Hashdict import Internal.Tools.LoginValues exposing (AccessToken) import Internal.Values.Event as IEvent import Internal.Values.Room as Internal +import Internal.Values.StateManager as StateManager +import Internal.Values.Timeline as Timeline import Json.Encode as E import Task exposing (Task) @@ -31,6 +36,57 @@ type Room } +{-| Create a new object from a joined room. +-} +initFromJoinedRoom : { roomId : String, nextBatch : String } -> Sync.JoinedRoom -> Internal.Room +initFromJoinedRoom data jroom = + Internal.Room + { accountData = + jroom.accountData + |> Maybe.map .events + |> Maybe.withDefault [] + |> List.map (\{ contentType, content } -> ( contentType, content )) + |> Dict.fromList + , ephemeral = + jroom.ephemeral + |> Maybe.map .events + |> Maybe.withDefault [] + |> List.map IEvent.BlindEvent + , events = + jroom.timeline + |> Maybe.map .events + |> Maybe.withDefault [] + |> List.map (Event.initFromClientEventWithoutRoomId data.roomId) + |> Hashdict.fromList IEvent.eventId + , roomId = data.roomId + , timeline = + jroom.timeline + |> Maybe.map + (\timeline -> + Timeline.newFromEvents + { events = List.map (Event.initFromClientEventWithoutRoomId data.roomId) timeline.events + , nextBatch = data.nextBatch + , prevBatch = timeline.prevBatch + , stateDelta = + jroom.state + |> Maybe.map + (.events + >> List.map (Event.initFromClientEventWithoutRoomId data.roomId) + >> StateManager.fromEventList + ) + } + ) + |> Maybe.withDefault + (Timeline.newFromEvents + { events = [] + , nextBatch = data.nextBatch + , prevBatch = Nothing + , stateDelta = Nothing + } + ) + } + + {-| Adds an internal event to the `Room`. An internal event is a custom event that has been generated by the client. -} @@ -66,6 +122,21 @@ internalValue (Room { room }) = room +{-| Get the most recent events. +-} +mostRecentEvents : Room -> List Event +mostRecentEvents (Room data) = + data.room + |> Internal.mostRecentEvents + |> List.map + (Event.init + { accessToken = data.accessToken + , baseUrl = data.baseUrl + , versions = data.versions + } + ) + + {-| Retrieves the ID of the Matrix room associated with the given `Room`. -} roomId : Room -> String diff --git a/src/Internal/Values/Credentials.elm b/src/Internal/Values/Credentials.elm index 9b46466..bb74cc9 100644 --- a/src/Internal/Values/Credentials.elm +++ b/src/Internal/Values/Credentials.elm @@ -11,7 +11,13 @@ import Internal.Values.Room as Room exposing (Room) type Credentials - = Credentials { access : AccessToken, baseUrl : String, rooms : Hashdict Room, versions : Maybe V.Versions } + = Credentials + { access : AccessToken + , baseUrl : String + , rooms : Hashdict Room + , since : Maybe String + , versions : Maybe V.Versions + } {-| Add a new access token based on prior information. @@ -28,6 +34,13 @@ addVersions versions (Credentials data) = Credentials { data | versions = Just versions } +{-| Add a new `since` token to sync from. +-} +addSince : String -> Credentials -> Credentials +addSince since (Credentials data) = + Credentials { data | since = Just since } + + {-| Get the stringed access token the Credentials type is using, if any. -} getAccessToken : Credentials -> Maybe String @@ -64,10 +77,18 @@ defaultCredentials homeserver = { access = NoAccess , baseUrl = homeserver , rooms = Hashdict.empty Room.roomId + , since = Nothing , versions = Nothing } +{-| Get the latest `since` token. +-} +getSince : Credentials -> Maybe String +getSince (Credentials { since }) = + since + + {-| Create a Credentials type using an unknown access token. -} fromAccessToken : { accessToken : String, homeserver : String } -> Credentials @@ -102,3 +123,10 @@ insertRoom : Room -> Credentials -> Credentials insertRoom room (Credentials cred) = Credentials { cred | rooms = Hashdict.insert room cred.rooms } + + +{-| Get a list of all synchronised rooms. +-} +getRooms : Credentials -> List Room +getRooms (Credentials { rooms }) = + Hashdict.values rooms diff --git a/src/Internal/Values/Room.elm b/src/Internal/Values/Room.elm index 4686b24..5e8ec6e 100644 --- a/src/Internal/Values/Room.elm +++ b/src/Internal/Values/Room.elm @@ -56,3 +56,10 @@ getEventById eventId (Room room) = roomId : Room -> String roomId (Room room) = room.roomId + + +{-| Get the most recent events. +-} +mostRecentEvents : Room -> List Event +mostRecentEvents (Room room) = + Timeline.mostRecentEvents room.timeline