From df7177962011e1b7bf1530174e16b8655dda99d0 Mon Sep 17 00:00:00 2001 From: Bram Date: Fri, 26 Apr 2024 11:31:35 +0200 Subject: [PATCH] Add Matrix.Room --- elm.json | 1 + src/Internal/Values/Room.elm | 39 ++++++++++++++++++++-- src/Matrix/Room.elm | 64 ++++++++++++++++++++++++++++++++++++ src/Types.elm | 11 +++++-- tests/Test/Values/Room.elm | 57 +++++++++++++++++++++++--------- 5 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 src/Matrix/Room.elm diff --git a/elm.json b/elm.json index 624bdf3..3dd4d1e 100644 --- a/elm.json +++ b/elm.json @@ -9,6 +9,7 @@ "Internal.Values.Timeline", "Matrix", "Matrix.Event", + "Matrix.Room", "Matrix.Settings", "Matrix.User" ], diff --git a/src/Internal/Values/Room.elm b/src/Internal/Values/Room.elm index 7d25082..86b933e 100644 --- a/src/Internal/Values/Room.elm +++ b/src/Internal/Values/Room.elm @@ -1,7 +1,8 @@ module Internal.Values.Room exposing ( Room, init - , Batch, addBatch, addSync, addEvents + , Batch, addBatch, addSync, addEvents, mostRecentEvents , getAccountData, setAccountData + , coder, encode, decode ) {-| @@ -26,19 +27,24 @@ room state reflect the homeserver state of the room. ## Timeline -@docs Batch, addBatch, addSync, addEvents +@docs Batch, addBatch, addSync, addEvents, mostRecentEvents ## Account data @docs getAccountData, setAccountData + +## JSON coding + +@docs coder, encode, decode + -} import FastDict as Dict exposing (Dict) import Internal.Config.Log exposing (log) import Internal.Config.Text as Text -import Internal.Filter.Timeline exposing (Filter) +import Internal.Filter.Timeline as Filter exposing (Filter) import Internal.Tools.Hashdict as Hashdict exposing (Hashdict) import Internal.Tools.Json as Json import Internal.Values.Event as Event exposing (Event) @@ -166,6 +172,20 @@ coder = ) +{-| Decode a Room from JSON format. +-} +decode : Json.Decoder Room +decode = + Json.decode coder + + +{-| Encode a Room into JSON format. +-} +encode : Json.Encoder Room +encode = + Json.encode coder + + {-| Get a piece of account data as information from the room. -} getAccountData : String -> Room -> Maybe Json.Value @@ -185,6 +205,19 @@ init roomId = } +{-| Get the most recent events from the timeline. +-} +mostRecentEvents : Room -> List Event +mostRecentEvents room = + room.timeline + |> Timeline.mostRecentEvents Filter.pass + |> List.map (List.filterMap (\e -> Hashdict.get e room.events)) + |> List.sortBy List.length + -- Get the largest list of events + |> List.head + |> Maybe.withDefault [] + + {-| Set a piece of account data as information about the room. -} setAccountData : String -> Json.Value -> Room -> Room diff --git a/src/Matrix/Room.elm b/src/Matrix/Room.elm new file mode 100644 index 0000000..9ebc08e --- /dev/null +++ b/src/Matrix/Room.elm @@ -0,0 +1,64 @@ +module Matrix.Room exposing + ( Room, mostRecentEvents + , getAccountData + ) + +{-| + + +# Room + +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 + +This module exposes various functions that help you inspect various aspects of +a room. + + +## Account data + +Account data is personal information that the user stores about this Matrix +room. This may include information like: + + - What type of room this is + - A list of members in the room to ignore + - A list of currently ongoing chess matches in the room + - Personal notes the user may be taking + +You may consider the account data as a `Dict String Json.Value` type. Account +data is linked to the user account: other logged in devices can see the account +data too, as the server synchronizes it, but the server shouldn´t show it to +other users. + +@docs getAccountData + +-} + +import Internal.Values.Envelope as Envelope +import Internal.Values.Room as Internal +import Json.Encode as E +import Types exposing (Room(..)) + + +{-| The Matrix Room type representing a room that the Matrix user has joined. +-} +type alias Room = + Types.Room + + +{-| Get a piece of account data linked to a certain string key. +-} +getAccountData : String -> Room -> Maybe E.Value +getAccountData key (Room room) = + Envelope.extract (Internal.getAccountData key) room + + +{-| Get a list of the most recent events sent in the room. +-} +mostRecentEvents : Room -> List Types.Event +mostRecentEvents (Room room) = + Envelope.mapList Internal.mostRecentEvents room + |> List.map Types.Event diff --git a/src/Types.elm b/src/Types.elm index aef9df0..c202354 100644 --- a/src/Types.elm +++ b/src/Types.elm @@ -1,4 +1,4 @@ -module Types exposing (Vault(..), Event(..), User(..)) +module Types exposing (Vault(..), Event(..), Room(..), User(..)) {-| The Elm SDK uses a lot of records and values that are easy to manipulate. Yet, the [Elm design guidelines](https://package.elm-lang.org/help/design-guidelines#keep-tags-and-record-constructors-secret) @@ -12,12 +12,13 @@ access their content directly. The opaque types are placed in a central module so all exposed modules can safely access all exposed data types without risking to create circular imports. -@docs Vault, Event, User +@docs Vault, Event, Room, User -} import Internal.Values.Envelope as Envelope import Internal.Values.Event as Event +import Internal.Values.Room as Room import Internal.Values.User as User import Internal.Values.Vault as Vault @@ -28,6 +29,12 @@ type Event = Event (Envelope.Envelope Event.Event) +{-| Opaque type for Matrix Room +-} +type Room + = Room (Envelope.Envelope Room.Room) + + {-| Opaque type for Matrix User -} type User diff --git a/tests/Test/Values/Room.elm b/tests/Test/Values/Room.elm index 01ac715..afab64f 100644 --- a/tests/Test/Values/Room.elm +++ b/tests/Test/Values/Room.elm @@ -1,16 +1,19 @@ module Test.Values.Room exposing (..) -import Test exposing (..) +import Expect import Fuzz exposing (Fuzzer) import Internal.Values.Room as Room exposing (Room) -import Test.Values.Event as TestEvent -import Test.Filter.Timeline as TestFilter -import Json.Encode as E import Json.Decode as D -import Expect +import Json.Encode as E +import Test exposing (..) +import Test.Filter.Timeline as TestFilter +import Test.Values.Event as TestEvent + placeholderValue : E.Value -placeholderValue = E.string "foo bar baz" +placeholderValue = + E.string "foo bar baz" + fuzzer : Fuzzer Room fuzzer = @@ -18,17 +21,24 @@ fuzzer = |> Fuzz.map Room.init |> addAFewTimes Fuzz.string (\key -> Room.setAccountData key placeholderValue) |> addAFewTimes (Fuzz.list TestEvent.fuzzer) Room.addEvents - |> add4AFewTimes (Fuzz.list TestEvent.fuzzer) TestFilter.fuzzer (Fuzz.maybe Fuzz.string) Fuzz.string + |> add4AFewTimes (Fuzz.list TestEvent.fuzzer) + TestFilter.fuzzer + (Fuzz.maybe Fuzz.string) + Fuzz.string (\a b c d -> Room.Batch a b c d |> Room.addBatch ) - |> add4AFewTimes (Fuzz.list TestEvent.fuzzer) TestFilter.fuzzer (Fuzz.maybe Fuzz.string) Fuzz.string + |> add4AFewTimes (Fuzz.list TestEvent.fuzzer) + TestFilter.fuzzer + (Fuzz.maybe Fuzz.string) + Fuzz.string (\a b c d -> Room.Batch a b c d |> Room.addSync ) + addAFewTimes : Fuzzer a -> (a -> Room -> Room) -> Fuzzer Room -> Fuzzer Room addAFewTimes fuzz f roomFuzzer = Fuzz.map2 @@ -36,31 +46,38 @@ addAFewTimes fuzz f roomFuzzer = (Fuzz.list fuzz) roomFuzzer + add2AFewTimes : Fuzzer a -> Fuzzer b -> (a -> b -> Room -> Room) -> Fuzzer Room -> Fuzzer Room add2AFewTimes fuzz1 fuzz2 f roomFuzzer = Fuzz.map2 - (\items room -> List.foldl (\(a, b) -> f a b) room items) - ( Fuzz.list <| Fuzz.pair fuzz1 fuzz2 ) + (\items room -> List.foldl (\( a, b ) -> f a b) room items) + (Fuzz.list <| Fuzz.pair fuzz1 fuzz2) roomFuzzer + add3AFewTimes : Fuzzer a -> Fuzzer b -> Fuzzer c -> (a -> b -> c -> Room -> Room) -> Fuzzer Room -> Fuzzer Room add3AFewTimes fuzz1 fuzz2 fuzz3 f roomFuzzer = Fuzz.map2 - (\items room -> List.foldl (\(a, b, c) -> f a b c) room items) - ( Fuzz.list <| Fuzz.triple fuzz1 fuzz2 fuzz3 ) + (\items room -> List.foldl (\( a, b, c ) -> f a b c) room items) + (Fuzz.list <| Fuzz.triple fuzz1 fuzz2 fuzz3) roomFuzzer + add4AFewTimes : Fuzzer a -> Fuzzer b -> Fuzzer c -> Fuzzer d -> (a -> b -> c -> d -> Room -> Room) -> Fuzzer Room -> Fuzzer Room add4AFewTimes fuzz1 fuzz2 fuzz3 fuzz4 f roomFuzzer = Fuzz.map2 - (\items room -> List.foldl (\((a, b), (c, d)) -> f a b c d) room items) - ( Fuzz.list <| Fuzz.pair (Fuzz.pair fuzz1 fuzz2) (Fuzz.pair fuzz3 fuzz4) ) + (\items room -> List.foldl (\( ( a, b ), ( c, d ) ) -> f a b c d) room items) + (Fuzz.list <| Fuzz.pair (Fuzz.pair fuzz1 fuzz2) (Fuzz.pair fuzz3 fuzz4)) roomFuzzer + suite : Test suite = describe "Room" - [ fuzz3 fuzzer Fuzz.string Fuzz.string "JSON Account Data can be overridden" + [ fuzz3 fuzzer + Fuzz.string + Fuzz.string + "JSON Account Data can be overridden" (\room key text -> room |> Room.setAccountData key (E.string text) @@ -69,4 +86,14 @@ suite = |> Maybe.andThen Result.toMaybe |> Expect.equal (Just text) ) + , fuzz fuzzer + "Room -> JSON -> Room is equal" + (\room -> + room + |> Room.encode + |> D.decodeValue Room.decode + |> Result.toMaybe + |> Maybe.map Tuple.first + |> Expect.equal (Just room) + ) ]