Merge branch 'develop' into 4-transfer-api

4-transfer-api
Bram 2024-07-16 12:34:11 +02:00
commit fee68f7e0f
22 changed files with 238 additions and 134 deletions

View File

@ -3,7 +3,7 @@
"name": "noordstar/elm-matrix-sdk-beta", "name": "noordstar/elm-matrix-sdk-beta",
"summary": "Matrix SDK for instant communication. Unstable beta version for testing only.", "summary": "Matrix SDK for instant communication. Unstable beta version for testing only.",
"license": "EUPL-1.1", "license": "EUPL-1.1",
"version": "3.3.1", "version": "3.4.0",
"exposed-modules": [ "exposed-modules": [
"Matrix", "Matrix",
"Matrix.Event", "Matrix.Event",

View File

@ -13,7 +13,6 @@ This module looks for the right homeserver address.
import Internal.Api.Chain as C import Internal.Api.Chain as C
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Config.Leaks as L
import Internal.Config.Log exposing (log) import Internal.Config.Log exposing (log)
import Internal.Config.Text as Text import Internal.Config.Text as Text
import Internal.Tools.Json as Json import Internal.Tools.Json as Json

View File

@ -204,7 +204,7 @@ getEventCoderV1 =
[ "UnsignedData as described by the Matrix spec" [ "UnsignedData as described by the Matrix spec"
, "https://spec.matrix.org/v1.10/client-server-api/#get_matrixclientv3roomsroomideventeventid" , "https://spec.matrix.org/v1.10/client-server-api/#get_matrixclientv3roomsroomideventeventid"
] ]
, init = \a b c d -> Event.UnsignedData { age = a, prevContent = b, redactedBecause = c, transactionId = d } , init = \a b c d -> Event.UnsignedData { age = a, membership = Nothing, prevContent = b, redactedBecause = c, transactionId = d }
} }
(Json.field.optional.value (Json.field.optional.value
{ fieldName = "age" { fieldName = "age"

View File

@ -13,7 +13,6 @@ This module allows the user to log in using a username and password.
import Internal.Api.Api as A import Internal.Api.Api as A
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Config.Leaks as L
import Internal.Config.Log exposing (log) import Internal.Config.Log exposing (log)
import Internal.Config.Text as Text import Internal.Config.Text as Text
import Internal.Tools.Json as Json import Internal.Tools.Json as Json

View File

@ -13,7 +13,6 @@ This module helps send message events to rooms on the Matrix API.
import Internal.Api.Api as A import Internal.Api.Api as A
import Internal.Api.Request as R import Internal.Api.Request as R
import Internal.Config.Leaks as L
import Internal.Config.Log exposing (log) import Internal.Config.Log exposing (log)
import Internal.Config.Text as Text import Internal.Config.Text as Text
import Internal.Tools.Json as Json import Internal.Tools.Json as Json

View File

@ -12,7 +12,6 @@ This API module represents the /sync endpoint on Matrix spec version v1.11.
-} -}
import FastDict as Dict exposing (Dict) import FastDict as Dict exposing (Dict)
import Internal.Api.Sync.V3 as PV
import Internal.Config.Log exposing (Log, log) import Internal.Config.Log exposing (Log, log)
import Internal.Config.Text as Text import Internal.Config.Text as Text
import Internal.Filter.Timeline exposing (Filter) import Internal.Filter.Timeline exposing (Filter)

View File

@ -29,7 +29,7 @@ will assume until overriden by the user.
-} -}
currentVersion : String currentVersion : String
currentVersion = currentVersion =
"beta 3.3.1" "beta 3.4.0"
{-| The default device name that is being communicated with the Matrix API. {-| The default device name that is being communicated with the Matrix API.

View File

@ -189,21 +189,20 @@ ipv6RightParser n =
|. P.symbol ":" |. P.symbol ":"
{-| Convert an IPv6 address to a readable string format
-}
ipv6ToString : IPv6Address -> String
ipv6ToString { front, back } =
(if List.length front == 8 then
front
else if List.length back == 8 then -- {-| Convert an IPv6 address to a readable string format
back -- -}
-- ipv6ToString : IPv6Address -> String
else -- ipv6ToString { front, back } =
List.concat [ front, [ "" ], back ] -- (if List.length front == 8 then
) -- front
|> List.intersperse ":" -- else if List.length back == 8 then
|> String.concat -- back
-- else
-- List.concat [ front, [ "" ], back ]
-- )
-- |> List.intersperse ":"
-- |> String.concat
portParser : Parser Int portParser : Parser Int

View File

@ -477,13 +477,14 @@ iddict (Coder old) =
Coder Coder
{ encoder = Iddict.encode old.encoder { encoder = Iddict.encode old.encoder
, decoder = , decoder =
D.andThen Iddict.decoder old.decoder
(\( out, logs ) -> |> D.map
D.succeed out (\out ->
|> Iddict.decoder ( Iddict.map (always Tuple.first) out
|> D.map (\o -> ( o, logs )) , Iddict.values out
) |> List.concatMap Tuple.second
old.decoder )
)
, docs = DocsIddict old.docs , docs = DocsIddict old.docs
} }

View File

@ -71,7 +71,6 @@ import Internal.Config.Text as Text
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict) import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
import Internal.Tools.Json as Json import Internal.Tools.Json as Json
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp) import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
import Json.Encode as E
import Set exposing (Set) import Set exposing (Set)
import Time import Time

View File

@ -56,6 +56,8 @@ import Internal.Tools.Json as Json
import Internal.Tools.Timestamp exposing (Timestamp) import Internal.Tools.Timestamp exposing (Timestamp)
import Internal.Values.Context as Context exposing (AccessToken, Context, Versions) import Internal.Values.Context as Context exposing (AccessToken, Context, Versions)
import Internal.Values.Settings as Settings import Internal.Values.Settings as Settings
import Recursion
import Recursion.Fold
{-| There are lots of different data types in the Elm SDK, and many of them {-| There are lots of different data types in the Elm SDK, and many of them
@ -292,50 +294,91 @@ toMaybe data =
{-| Updates the Envelope with a given EnvelopeUpdate value. {-| Updates the Envelope with a given EnvelopeUpdate value.
-} -}
update : (au -> a -> a) -> EnvelopeUpdate au -> Envelope a -> Envelope a update : (au -> a -> a) -> EnvelopeUpdate au -> Envelope a -> Envelope a
update updateContent eu ({ context } as data) = update updateContent eu startData =
case eu of Recursion.runRecursion
ContentUpdate v -> (\updt ->
{ data | content = updateContent v data.content } case updt of
ContentUpdate v ->
Recursion.base
(\data ->
{ data | content = updateContent v data.content }
)
HttpRequest _ -> HttpRequest _ ->
data Recursion.base identity
More items -> More items ->
List.foldl (update updateContent) data items Recursion.Fold.foldList (<<) identity items
Optional (Just u) -> Optional (Just u) ->
update updateContent u data Recursion.recurse u
Optional Nothing -> Optional Nothing ->
data Recursion.base identity
RemoveAccessToken token -> RemoveAccessToken token ->
{ data | context = { context | accessTokens = Hashdict.removeKey token context.accessTokens } } Recursion.base
(\({ context } as data) ->
{ data
| context =
{ context
| accessTokens =
Hashdict.removeKey token context.accessTokens
}
}
)
RemovePasswordIfNecessary -> RemovePasswordIfNecessary ->
if data.settings.removePasswordOnLogin then Recursion.base
{ data | context = { context | password = Nothing } } (\({ context } as data) ->
if data.settings.removePasswordOnLogin then
{ data | context = { context | password = Nothing } }
else else
data data
)
SetAccessToken a -> SetAccessToken a ->
{ data | context = { context | accessTokens = Hashdict.insert a context.accessTokens } } Recursion.base
(\({ context } as data) ->
{ data | context = { context | accessTokens = Hashdict.insert a context.accessTokens } }
)
SetBaseUrl b -> SetBaseUrl b ->
{ data | context = { context | baseUrl = Just b } } Recursion.base
(\({ context } as data) ->
{ data | context = { context | baseUrl = Just b } }
)
SetDeviceId d -> SetDeviceId d ->
{ data | context = { context | deviceId = Just d } } Recursion.base
(\({ context } as data) ->
{ data | context = { context | deviceId = Just d } }
)
SetNextBatch nextBatch -> SetNextBatch nextBatch ->
{ data | context = { context | nextBatch = Just nextBatch } } Recursion.base
(\({ context } as data) ->
{ data | context = { context | nextBatch = Just nextBatch } }
)
SetNow n -> SetNow n ->
{ data | context = { context | now = Just n } } Recursion.base
(\({ context } as data) ->
{ data | context = { context | now = Just n } }
)
SetRefreshToken r -> SetRefreshToken r ->
{ data | context = { context | refreshToken = Just r } } Recursion.base
(\({ context } as data) ->
{ data | context = { context | refreshToken = Just r } }
)
SetVersions vs -> SetVersions vs ->
{ data | context = { context | versions = Just vs } } Recursion.base
(\({ context } as data) ->
{ data | context = { context | versions = Just vs } }
)
)
eu
startData

View File

@ -58,7 +58,8 @@ import Internal.Values.Event as Event exposing (Event)
import Internal.Values.StateManager as StateManager exposing (StateManager) import Internal.Values.StateManager as StateManager exposing (StateManager)
import Internal.Values.Timeline as Timeline exposing (Timeline) import Internal.Values.Timeline as Timeline exposing (Timeline)
import Internal.Values.User exposing (User) import Internal.Values.User exposing (User)
import Json.Encode as E import Recursion
import Recursion.Fold
{-| The Batch is a group of new events from somewhere in the timeline. {-| The Batch is a group of new events from somewhere in the timeline.
@ -255,30 +256,35 @@ setAccountData key value room =
{-| Update the Room based on given instructions. {-| Update the Room based on given instructions.
-} -}
update : RoomUpdate -> Room -> Room update : RoomUpdate -> Room -> Room
update ru room = update roomUpdate startRoom =
case ru of Recursion.runRecursion
AddEvent _ -> (\ru ->
-- TODO: Add event case ru of
room AddEvent _ ->
-- TODO: Add event
Recursion.base identity
AddSync batch -> AddSync batch ->
addSync batch room Recursion.base (addSync batch)
Invite _ -> Invite _ ->
-- TODO: Invite user -- TODO: Invite user
room Recursion.base identity
More items -> More items ->
List.foldl update room items Recursion.Fold.foldList (<<) identity items
Optional (Just u) -> Optional (Just u) ->
update u room Recursion.recurse u
Optional Nothing -> Optional Nothing ->
room Recursion.base identity
SetAccountData key value -> SetAccountData key value ->
setAccountData key value room Recursion.base (setAccountData key value)
SetEphemeral eph -> SetEphemeral eph ->
{ room | ephemeral = eph } Recursion.base (\room -> { room | ephemeral = eph })
)
roomUpdate
startRoom

View File

@ -678,20 +678,21 @@ mostRecentFrom filter timeline ptr =
{ ptr = ptr, visited = Set.empty } { ptr = ptr, visited = Set.empty }
{-| Recount the Timeline's amount of filled batches. Since the Timeline
automatically tracks the count on itself, this is generally exclusively used in -- {-| Recount the Timeline's amount of filled batches. Since the Timeline
specific scenarios like decoding JSON values. -- automatically tracks the count on itself, this is generally exclusively used in
-} -- specific scenarios like decoding JSON values.
recountFilledBatches : Timeline -> Timeline -- -}
recountFilledBatches (Timeline tl) = -- recountFilledBatches : Timeline -> Timeline
Timeline -- recountFilledBatches (Timeline tl) =
{ tl -- Timeline
| filledBatches = -- { tl
tl.batches -- | filledBatches =
|> Iddict.values -- tl.batches
|> List.filter (\v -> v.events /= []) -- |> Iddict.values
|> List.length -- |> List.filter (\v -> v.events /= [])
} -- |> List.length
-- }
{-| Create a timeline with a single batch inserted. This batch is considered the {-| Create a timeline with a single batch inserted. This batch is considered the

View File

@ -1,8 +1,9 @@
module Internal.Values.Vault exposing module Internal.Values.Vault exposing
( Vault, init ( Vault, init
, VaultUpdate(..), update , VaultUpdate(..), update
, fromRoomId, mapRoom, updateRoom , rooms, fromRoomId, mapRoom, updateRoom
, getAccountData, setAccountData , getAccountData, setAccountData
, coder
) )
{-| This module hosts the Vault module. The Vault is the data type storing all {-| This module hosts the Vault module. The Vault is the data type storing all
@ -23,13 +24,18 @@ To update the Vault, one uses VaultUpdate types.
Rooms are environments where people can have a conversation with each other. Rooms are environments where people can have a conversation with each other.
@docs fromRoomId, mapRoom, updateRoom @docs rooms, fromRoomId, mapRoom, updateRoom
## Account data ## Account data
@docs getAccountData, setAccountData @docs getAccountData, setAccountData
## JSON
@docs coder
-} -}
import FastDict as Dict exposing (Dict) import FastDict as Dict exposing (Dict)
@ -38,6 +44,8 @@ import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
import Internal.Tools.Json as Json import Internal.Tools.Json as Json
import Internal.Values.Room as Room exposing (Room) import Internal.Values.Room as Room exposing (Room)
import Internal.Values.User as User exposing (User) import Internal.Values.User as User exposing (User)
import Recursion
import Recursion.Fold
{-| This is the Vault type. {-| This is the Vault type.
@ -63,6 +71,8 @@ type VaultUpdate
| SetUser User | SetUser User
{-| Convert a Vault to and from a JSON object.
-}
coder : Json.Coder Vault coder : Json.Coder Vault
coder = coder =
Json.object4 Json.object4
@ -133,6 +143,13 @@ mapRoom roomId f vault =
{ vault | rooms = Hashdict.map roomId f vault.rooms } { vault | rooms = Hashdict.map roomId f vault.rooms }
{-| Get a list of all joined rooms present in the vault.
-}
rooms : Vault -> List Room
rooms vault =
Hashdict.values vault.rooms
{-| Set a piece of account data as information in the global vault data. {-| Set a piece of account data as information in the global vault data.
-} -}
setAccountData : String -> Json.Value -> Vault -> Vault setAccountData : String -> Json.Value -> Vault -> Vault
@ -150,30 +167,41 @@ updateRoom roomId f vault =
{-| Update the Vault using a VaultUpdate type. {-| Update the Vault using a VaultUpdate type.
-} -}
update : VaultUpdate -> Vault -> Vault update : VaultUpdate -> Vault -> Vault
update vu vault = update vaultUpdate startVault =
case vu of Recursion.runRecursion
CreateRoomIfNotExists roomId -> (\vu ->
updateRoom roomId case vu of
(Maybe.withDefault (Room.init roomId) >> Maybe.Just) CreateRoomIfNotExists roomId ->
vault (Maybe.withDefault (Room.init roomId) >> Maybe.Just)
|> updateRoom roomId
|> Recursion.base
MapRoom roomId ru -> MapRoom roomId ru ->
mapRoom roomId (Room.update ru) vault Recursion.base (mapRoom roomId (Room.update ru))
More items -> More items ->
List.foldl update vault items Recursion.Fold.foldList (<<) identity items
Optional (Just u) -> Optional (Just u) ->
update u vault Recursion.recurse u
Optional Nothing -> Optional Nothing ->
vault Recursion.base identity
SetAccountData key value -> SetAccountData key value ->
setAccountData key value vault Recursion.base (setAccountData key value)
SetNextBatch nb -> SetNextBatch nb ->
{ vault | nextBatch = Just nb } Recursion.base
(\vault ->
{ vault | nextBatch = Just nb }
)
SetUser user -> SetUser user ->
{ vault | user = Just user } Recursion.base
(\vault ->
{ vault | user = Just user }
)
)
vaultUpdate
startVault

View File

@ -1,6 +1,7 @@
module Matrix exposing module Matrix exposing
( Vault, fromUserId, fromUsername ( Vault, fromUserId, fromUsername
, VaultUpdate, update, sync, logs , VaultUpdate, update, sync, logs
, rooms, fromRoomId
, addAccessToken, sendMessageEvent , addAccessToken, sendMessageEvent
) )
@ -27,6 +28,11 @@ support a monolithic public registry. (:
@docs VaultUpdate, update, sync, logs @docs VaultUpdate, update, sync, logs
## Exploring the Vault
@docs rooms, fromRoomId
## Debugging ## Debugging
@docs addAccessToken, sendMessageEvent @docs addAccessToken, sendMessageEvent
@ -66,6 +72,14 @@ addAccessToken token (Vault vault) =
|> Vault |> Vault
{-| Get a room based on its room ID, if the user is a member of that room.
-}
fromRoomId : String -> Vault -> Maybe Types.Room
fromRoomId roomId (Vault vault) =
Envelope.mapMaybe (Internal.fromRoomId roomId) vault
|> Maybe.map Types.Room
{-| Use a fully-fledged Matrix ID to connect. {-| Use a fully-fledged Matrix ID to connect.
case Matrix.fromUserId "@alice:example.org" of case Matrix.fromUserId "@alice:example.org" of
@ -112,6 +126,14 @@ fromUsername { username, host, port_ } =
|> Vault |> Vault
{-| Get a list of all the rooms that the user has joined.
-}
rooms : Vault -> List Types.Room
rooms (Vault vault) =
Envelope.mapList Internal.rooms vault
|> List.map Types.Room
{-| The VaultUpdate is a complex type that helps update the Vault. However, {-| The VaultUpdate is a complex type that helps update the Vault. However,
it also contains a human output! it also contains a human output!

View File

@ -1,5 +1,5 @@
module Matrix.Room exposing module Matrix.Room exposing
( Room, mostRecentEvents ( Room, mostRecentEvents, roomId
, getAccountData , getAccountData
) )
@ -12,7 +12,7 @@ What is usually called a chat, a channel, a conversation or a group chat on
other platforms, the term used in Matrix is a "room". A room is a conversation other platforms, the term used in Matrix is a "room". A room is a conversation
where a group of users talk to each other. where a group of users talk to each other.
@docs Room, mostRecentEvents @docs Room, mostRecentEvents, roomId
This module exposes various functions that help you inspect various aspects of This module exposes various functions that help you inspect various aspects of
a room. a room.
@ -56,6 +56,14 @@ getAccountData key (Room room) =
Envelope.extract (Internal.getAccountData key) room Envelope.extract (Internal.getAccountData key) room
{-| Get a room's room id. This is an opaque string that distinguishes rooms from
each other.
-}
roomId : Room -> String
roomId (Room room) =
Envelope.extract .roomId room
{-| Get a list of the most recent events sent in the room. {-| Get a list of the most recent events sent in the room.
-} -}
mostRecentEvents : Room -> List Types.Event mostRecentEvents : Room -> List Types.Event

View File

@ -5,8 +5,6 @@ import Fuzz exposing (Fuzzer)
import Internal.Config.Leaks as Leaks import Internal.Config.Leaks as Leaks
import Internal.Tools.Hashdict as Hashdict import Internal.Tools.Hashdict as Hashdict
import Internal.Values.Context as Context exposing (Context, Versions) import Internal.Values.Context as Context exposing (Context, Versions)
import Json.Decode as D
import Json.Encode as E
import Set import Set
import Test exposing (..) import Test exposing (..)
import Test.Tools.Timestamp as TestTimestamp import Test.Tools.Timestamp as TestTimestamp
@ -19,12 +17,15 @@ fuzzer =
maybeString = maybeString =
Fuzz.maybe Fuzz.string Fuzz.maybe Fuzz.string
in in
Fuzz.map8 (\a b c d e ( f, g ) ( h, i ) ( j, k ) -> Context a b c d e f g h i j k) Fuzz.map8 (\a b c d ( e, f ) ( g, h ) ( i, j ) ( k, l ) -> Context a b c d e f g h i j k l)
(Fuzz.constant <| Hashdict.empty .value) (Fuzz.constant <| Hashdict.empty .value)
maybeString maybeString
maybeString maybeString
(Fuzz.maybe TestTimestamp.fuzzer)
maybeString maybeString
(Fuzz.pair
(Fuzz.maybe TestTimestamp.fuzzer)
maybeString
)
(Fuzz.pair (Fuzz.pair
maybeString maybeString
Fuzz.string Fuzz.string

View File

@ -3,10 +3,7 @@ module Test.Values.Envelope exposing (..)
import Expect import Expect
import Fuzz exposing (Fuzzer) import Fuzz exposing (Fuzzer)
import Internal.Config.Default as Default import Internal.Config.Default as Default
import Internal.Tools.Json as Json
import Internal.Values.Envelope as Envelope exposing (Envelope) import Internal.Values.Envelope as Envelope exposing (Envelope)
import Json.Decode as D
import Json.Encode as E
import Test exposing (..) import Test exposing (..)
import Test.Values.Context as TestContext import Test.Values.Context as TestContext
import Test.Values.Settings as TestSettings import Test.Values.Settings as TestSettings

View File

@ -41,16 +41,18 @@ fuzzerState =
unsignedDataFuzzer : Fuzzer Event.UnsignedData unsignedDataFuzzer : Fuzzer Event.UnsignedData
unsignedDataFuzzer = unsignedDataFuzzer =
Fuzz.map4 Fuzz.map5
(\age prev redact trans -> (\age memb prev redact trans ->
Event.UnsignedData Event.UnsignedData
{ age = age { age = age
, membership = memb
, prevContent = prev , prevContent = prev
, redactedBecause = redact , redactedBecause = redact
, transactionId = trans , transactionId = trans
} }
) )
(Fuzz.maybe Fuzz.int) (Fuzz.maybe Fuzz.int)
(Fuzz.maybe Fuzz.string)
(Fuzz.maybe valueFuzzer) (Fuzz.maybe valueFuzzer)
(Fuzz.maybe <| Fuzz.lazy (\_ -> fuzzer)) (Fuzz.maybe <| Fuzz.lazy (\_ -> fuzzer))
(Fuzz.maybe Fuzz.string) (Fuzz.maybe Fuzz.string)

View File

@ -4,8 +4,6 @@ import Fuzz exposing (Fuzzer)
import Internal.Values.Room as Room exposing (Room) import Internal.Values.Room as Room exposing (Room)
import Json.Encode as E import Json.Encode as E
import Test exposing (..) import Test exposing (..)
import Test.Filter.Timeline as TestFilter
import Test.Values.Event as TestEvent
placeholderValue : E.Value placeholderValue : E.Value

View File

@ -11,7 +11,7 @@ import Test exposing (..)
fuzzer : Fuzzer Settings fuzzer : Fuzzer Settings
fuzzer = fuzzer =
Fuzz.map4 Settings Fuzz.map5 Settings
(Fuzz.oneOf (Fuzz.oneOf
[ Fuzz.constant Default.currentVersion [ Fuzz.constant Default.currentVersion
, Fuzz.string , Fuzz.string
@ -22,6 +22,7 @@ fuzzer =
, Fuzz.string , Fuzz.string
] ]
) )
(Fuzz.maybe Fuzz.string)
(Fuzz.oneOf (Fuzz.oneOf
[ Fuzz.constant Default.removePasswordOnLogin [ Fuzz.constant Default.removePasswordOnLogin
, Fuzz.bool , Fuzz.bool

View File

@ -6,6 +6,7 @@ import Internal.Filter.Timeline as Filter
import Internal.Tools.Json as Json import Internal.Tools.Json as Json
import Internal.Values.Timeline as Timeline exposing (Batch, Timeline) import Internal.Values.Timeline as Timeline exposing (Batch, Timeline)
import Json.Decode as D import Json.Decode as D
import Json.Encode as E
import Test exposing (..) import Test exposing (..)
import Test.Filter.Timeline as TestFilter import Test.Filter.Timeline as TestFilter
@ -250,7 +251,8 @@ suite =
(\timeline -> (\timeline ->
timeline timeline
|> Json.encode Timeline.coder |> Json.encode Timeline.coder
|> D.decodeValue (Json.decode Timeline.coder) |> E.encode 0
|> D.decodeString (Json.decode Timeline.coder)
|> Result.map Tuple.first |> Result.map Tuple.first
|> Result.map (Timeline.mostRecentEvents Filter.pass) |> Result.map (Timeline.mostRecentEvents Filter.pass)
|> Expect.equal (Ok <| Timeline.mostRecentEvents Filter.pass timeline) |> Expect.equal (Ok <| Timeline.mostRecentEvents Filter.pass timeline)