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",
"summary": "Matrix SDK for instant communication. Unstable beta version for testing only.",
"license": "EUPL-1.1",
"version": "3.3.1",
"version": "3.4.0",
"exposed-modules": [
"Matrix",
"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.Request as R
import Internal.Config.Leaks as L
import Internal.Config.Log exposing (log)
import Internal.Config.Text as Text
import Internal.Tools.Json as Json

View File

@ -204,7 +204,7 @@ getEventCoderV1 =
[ "UnsignedData as described by the Matrix spec"
, "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
{ 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.Request as R
import Internal.Config.Leaks as L
import Internal.Config.Log exposing (log)
import Internal.Config.Text as Text
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.Request as R
import Internal.Config.Leaks as L
import Internal.Config.Log exposing (log)
import Internal.Config.Text as Text
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 Internal.Api.Sync.V3 as PV
import Internal.Config.Log exposing (Log, log)
import Internal.Config.Text as Text
import Internal.Filter.Timeline exposing (Filter)

View File

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

View File

@ -189,21 +189,20 @@ ipv6RightParser n =
|. 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
back
else
List.concat [ front, [ "" ], back ]
)
|> List.intersperse ":"
|> String.concat
-- {-| 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
-- back
-- else
-- List.concat [ front, [ "" ], back ]
-- )
-- |> List.intersperse ":"
-- |> String.concat
portParser : Parser Int

View File

@ -477,13 +477,14 @@ iddict (Coder old) =
Coder
{ encoder = Iddict.encode old.encoder
, decoder =
D.andThen
(\( out, logs ) ->
D.succeed out
|> Iddict.decoder
|> D.map (\o -> ( o, logs ))
)
old.decoder
Iddict.decoder old.decoder
|> D.map
(\out ->
( Iddict.map (always Tuple.first) out
, Iddict.values out
|> List.concatMap Tuple.second
)
)
, 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.Json as Json
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
import Json.Encode as E
import Set exposing (Set)
import Time

View File

@ -56,6 +56,8 @@ import Internal.Tools.Json as Json
import Internal.Tools.Timestamp exposing (Timestamp)
import Internal.Values.Context as Context exposing (AccessToken, Context, Versions)
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
@ -292,50 +294,91 @@ toMaybe data =
{-| Updates the Envelope with a given EnvelopeUpdate value.
-}
update : (au -> a -> a) -> EnvelopeUpdate au -> Envelope a -> Envelope a
update updateContent eu ({ context } as data) =
case eu of
ContentUpdate v ->
{ data | content = updateContent v data.content }
update updateContent eu startData =
Recursion.runRecursion
(\updt ->
case updt of
ContentUpdate v ->
Recursion.base
(\data ->
{ data | content = updateContent v data.content }
)
HttpRequest _ ->
data
HttpRequest _ ->
Recursion.base identity
More items ->
List.foldl (update updateContent) data items
More items ->
Recursion.Fold.foldList (<<) identity items
Optional (Just u) ->
update updateContent u data
Optional (Just u) ->
Recursion.recurse u
Optional Nothing ->
data
Optional Nothing ->
Recursion.base identity
RemoveAccessToken token ->
{ data | context = { context | accessTokens = Hashdict.removeKey token context.accessTokens } }
RemoveAccessToken token ->
Recursion.base
(\({ context } as data) ->
{ data
| context =
{ context
| accessTokens =
Hashdict.removeKey token context.accessTokens
}
}
)
RemovePasswordIfNecessary ->
if data.settings.removePasswordOnLogin then
{ data | context = { context | password = Nothing } }
RemovePasswordIfNecessary ->
Recursion.base
(\({ context } as data) ->
if data.settings.removePasswordOnLogin then
{ data | context = { context | password = Nothing } }
else
data
else
data
)
SetAccessToken a ->
{ data | context = { context | accessTokens = Hashdict.insert a context.accessTokens } }
SetAccessToken a ->
Recursion.base
(\({ context } as data) ->
{ data | context = { context | accessTokens = Hashdict.insert a context.accessTokens } }
)
SetBaseUrl b ->
{ data | context = { context | baseUrl = Just b } }
SetBaseUrl b ->
Recursion.base
(\({ context } as data) ->
{ data | context = { context | baseUrl = Just b } }
)
SetDeviceId d ->
{ data | context = { context | deviceId = Just d } }
SetDeviceId d ->
Recursion.base
(\({ context } as data) ->
{ data | context = { context | deviceId = Just d } }
)
SetNextBatch nextBatch ->
{ data | context = { context | nextBatch = Just nextBatch } }
SetNextBatch nextBatch ->
Recursion.base
(\({ context } as data) ->
{ data | context = { context | nextBatch = Just nextBatch } }
)
SetNow n ->
{ data | context = { context | now = Just n } }
SetNow n ->
Recursion.base
(\({ context } as data) ->
{ data | context = { context | now = Just n } }
)
SetRefreshToken r ->
{ data | context = { context | refreshToken = Just r } }
SetRefreshToken r ->
Recursion.base
(\({ context } as data) ->
{ data | context = { context | refreshToken = Just r } }
)
SetVersions vs ->
{ data | context = { context | versions = Just vs } }
SetVersions 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.Timeline as Timeline exposing (Timeline)
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.
@ -255,30 +256,35 @@ setAccountData key value room =
{-| Update the Room based on given instructions.
-}
update : RoomUpdate -> Room -> Room
update ru room =
case ru of
AddEvent _ ->
-- TODO: Add event
room
update roomUpdate startRoom =
Recursion.runRecursion
(\ru ->
case ru of
AddEvent _ ->
-- TODO: Add event
Recursion.base identity
AddSync batch ->
addSync batch room
AddSync batch ->
Recursion.base (addSync batch)
Invite _ ->
-- TODO: Invite user
room
Invite _ ->
-- TODO: Invite user
Recursion.base identity
More items ->
List.foldl update room items
More items ->
Recursion.Fold.foldList (<<) identity items
Optional (Just u) ->
update u room
Optional (Just u) ->
Recursion.recurse u
Optional Nothing ->
room
Optional Nothing ->
Recursion.base identity
SetAccountData key value ->
setAccountData key value room
SetAccountData key value ->
Recursion.base (setAccountData key value)
SetEphemeral eph ->
{ room | ephemeral = eph }
SetEphemeral eph ->
Recursion.base (\room -> { room | ephemeral = eph })
)
roomUpdate
startRoom

View File

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

View File

@ -1,8 +1,9 @@
module Internal.Values.Vault exposing
( Vault, init
, VaultUpdate(..), update
, fromRoomId, mapRoom, updateRoom
, rooms, fromRoomId, mapRoom, updateRoom
, getAccountData, setAccountData
, coder
)
{-| 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.
@docs fromRoomId, mapRoom, updateRoom
@docs rooms, fromRoomId, mapRoom, updateRoom
## Account data
@docs getAccountData, setAccountData
## JSON
@docs coder
-}
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.Values.Room as Room exposing (Room)
import Internal.Values.User as User exposing (User)
import Recursion
import Recursion.Fold
{-| This is the Vault type.
@ -63,6 +71,8 @@ type VaultUpdate
| SetUser User
{-| Convert a Vault to and from a JSON object.
-}
coder : Json.Coder Vault
coder =
Json.object4
@ -133,6 +143,13 @@ mapRoom roomId f vault =
{ 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.
-}
setAccountData : String -> Json.Value -> Vault -> Vault
@ -150,30 +167,41 @@ updateRoom roomId f vault =
{-| Update the Vault using a VaultUpdate type.
-}
update : VaultUpdate -> Vault -> Vault
update vu vault =
case vu of
CreateRoomIfNotExists roomId ->
updateRoom roomId
(Maybe.withDefault (Room.init roomId) >> Maybe.Just)
vault
update vaultUpdate startVault =
Recursion.runRecursion
(\vu ->
case vu of
CreateRoomIfNotExists roomId ->
(Maybe.withDefault (Room.init roomId) >> Maybe.Just)
|> updateRoom roomId
|> Recursion.base
MapRoom roomId ru ->
mapRoom roomId (Room.update ru) vault
MapRoom roomId ru ->
Recursion.base (mapRoom roomId (Room.update ru))
More items ->
List.foldl update vault items
More items ->
Recursion.Fold.foldList (<<) identity items
Optional (Just u) ->
update u vault
Optional (Just u) ->
Recursion.recurse u
Optional Nothing ->
vault
Optional Nothing ->
Recursion.base identity
SetAccountData key value ->
setAccountData key value vault
SetAccountData key value ->
Recursion.base (setAccountData key value)
SetNextBatch nb ->
{ vault | nextBatch = Just nb }
SetNextBatch nb ->
Recursion.base
(\vault ->
{ vault | nextBatch = Just nb }
)
SetUser user ->
{ vault | user = Just user }
SetUser user ->
Recursion.base
(\vault ->
{ vault | user = Just user }
)
)
vaultUpdate
startVault

View File

@ -1,6 +1,7 @@
module Matrix exposing
( Vault, fromUserId, fromUsername
, VaultUpdate, update, sync, logs
, rooms, fromRoomId
, addAccessToken, sendMessageEvent
)
@ -27,6 +28,11 @@ support a monolithic public registry. (:
@docs VaultUpdate, update, sync, logs
## Exploring the Vault
@docs rooms, fromRoomId
## Debugging
@docs addAccessToken, sendMessageEvent
@ -66,6 +72,14 @@ addAccessToken token (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.
case Matrix.fromUserId "@alice:example.org" of
@ -112,6 +126,14 @@ fromUsername { username, host, port_ } =
|> 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,
it also contains a human output!

View File

@ -1,5 +1,5 @@
module Matrix.Room exposing
( Room, mostRecentEvents
( Room, mostRecentEvents, roomId
, 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
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
a room.
@ -56,6 +56,14 @@ getAccountData key (Room 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.
-}
mostRecentEvents : Room -> List Types.Event

View File

@ -5,8 +5,6 @@ import Fuzz exposing (Fuzzer)
import Internal.Config.Leaks as Leaks
import Internal.Tools.Hashdict as Hashdict
import Internal.Values.Context as Context exposing (Context, Versions)
import Json.Decode as D
import Json.Encode as E
import Set
import Test exposing (..)
import Test.Tools.Timestamp as TestTimestamp
@ -19,12 +17,15 @@ fuzzer =
maybeString =
Fuzz.maybe Fuzz.string
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)
maybeString
maybeString
(Fuzz.maybe TestTimestamp.fuzzer)
maybeString
(Fuzz.pair
(Fuzz.maybe TestTimestamp.fuzzer)
maybeString
)
(Fuzz.pair
maybeString
Fuzz.string

View File

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@ import Internal.Filter.Timeline as Filter
import Internal.Tools.Json as Json
import Internal.Values.Timeline as Timeline exposing (Batch, Timeline)
import Json.Decode as D
import Json.Encode as E
import Test exposing (..)
import Test.Filter.Timeline as TestFilter
@ -250,7 +251,8 @@ suite =
(\timeline ->
timeline
|> 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 (Timeline.mostRecentEvents Filter.pass)
|> Expect.equal (Ok <| Timeline.mostRecentEvents Filter.pass timeline)