From 29e81a88acf9e4d2cd2a34b39e0bf1f15dd13a94 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Tue, 10 Sep 2019 23:24:47 -0700 Subject: [PATCH] Start switching from sync to room data --- src/Main.elm | 7 +++++- src/Scylla/AccountData.elm | 23 ------------------- src/Scylla/ListUtils.elm | 39 +++++++++++++++++++++++++++++++++ src/Scylla/Model.elm | 8 ++++--- src/Scylla/Notification.elm | 1 - src/Scylla/Room.elm | 22 +++++++++++++++++-- src/Scylla/Sync.elm | 36 +----------------------------- src/Scylla/Sync/AccountData.elm | 28 ++++++++++++++++++++++- src/Scylla/Views.elm | 25 ++++++++++----------- 9 files changed, 110 insertions(+), 79 deletions(-) delete mode 100644 src/Scylla/AccountData.elm create mode 100644 src/Scylla/ListUtils.elm diff --git a/src/Main.elm b/src/Main.elm index 6b7b79a..e289d23 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -1,8 +1,11 @@ import Browser exposing (application, UrlRequest(..)) import Browser.Navigation as Nav import Browser.Dom exposing (Viewport, setViewportOf) +import Scylla.Room exposing (OpenRooms, applySync) import Scylla.Sync exposing (..) import Scylla.Sync.Events exposing (toMessageEvent, getType, getSender, getUnsigned) +import Scylla.Sync.AccountData exposing (..) +import Scylla.ListUtils exposing (..) import Scylla.Messages exposing (..) import Scylla.Login exposing (..) import Scylla.Api exposing (..) @@ -14,7 +17,7 @@ import Scylla.UserData exposing (..) import Scylla.Notification exposing (..) import Scylla.Storage exposing (..) import Scylla.Markdown exposing (..) -import Scylla.AccountData exposing (..) +import Scylla.Room exposing (..) import Url exposing (Url) import Url.Parser exposing (parse) import Url.Builder @@ -55,6 +58,7 @@ init _ url key = , roomNames = Dict.empty , connected = True , searchText = "" + , rooms = emptyOpenRooms } cmd = getStoreValuePort "scylla.loginInfo" in @@ -355,6 +359,7 @@ updateSyncResponse model r notify = { model | sync = newSync sr , sending = sending (mergeSyncResponse model.sync sr) , roomNames = computeRoomsDisplayNames model.userData (newSync sr) + , rooms = applySync sr model.rooms } in case r of diff --git a/src/Scylla/AccountData.elm b/src/Scylla/AccountData.elm deleted file mode 100644 index f5d2617..0000000 --- a/src/Scylla/AccountData.elm +++ /dev/null @@ -1,23 +0,0 @@ -module Scylla.AccountData exposing (..) -import Scylla.Sync exposing (SyncResponse, roomAccountData) -import Scylla.Sync.AccountData exposing (AccountData) -import Scylla.Sync.Rooms exposing (JoinedRoom) -import Json.Decode as Decode -import Json.Encode as Encode -import Dict exposing (Dict) - -type alias DirectMessages = Dict String String -type alias DirectMessagesRaw = Dict String (List String) - -directMessagesDecoder : Decode.Decoder DirectMessages -directMessagesDecoder = - Decode.dict (Decode.list Decode.string) - |> Decode.map (invertDirectMessages) - -invertDirectMessages : DirectMessagesRaw -> DirectMessages -invertDirectMessages dmr = - Dict.foldl - (\k lv acc -> List.foldl (\v -> Dict.insert v k) acc lv) - Dict.empty - dmr - diff --git a/src/Scylla/ListUtils.elm b/src/Scylla/ListUtils.elm new file mode 100644 index 0000000..496c674 --- /dev/null +++ b/src/Scylla/ListUtils.elm @@ -0,0 +1,39 @@ +module Scylla.ListUtils exposing (..) +import Dict exposing (Dict) +import Set exposing (Set) + +groupBy : (a -> comparable) -> List a -> Dict comparable (List a) +groupBy f xs = + let + update v ml = case ml of + Just l -> Just (v::l) + Nothing -> Just [ v ] + in + List.foldl (\v acc -> Dict.update (f v) (update v) acc) Dict.empty xs + +uniqueByTailRecursive : (a -> comparable) -> List a -> Set comparable -> List a -> List a +uniqueByTailRecursive f l s acc = + case l of + x::tail -> + if Set.member (f x) s + then uniqueByTailRecursive f tail s acc + else uniqueByTailRecursive f tail (Set.insert (f x) s) (x::acc) + [] -> List.reverse acc + +uniqueBy : (a -> comparable) -> List a -> List a +uniqueBy f l = uniqueByTailRecursive f l Set.empty [] + +findFirst : (a -> Bool) -> List a -> Maybe a +findFirst cond l = case l of + x::xs -> if cond x then Just x else findFirst cond xs + [] -> Nothing + +findLast : (a -> Bool) -> List a -> Maybe a +findLast cond l = findFirst cond <| List.reverse l + +findFirstBy : (a -> comparable) -> (a -> Bool) -> List a -> Maybe a +findFirstBy sortFunction cond l = findFirst cond <| List.sortBy sortFunction l + +findLastBy : (a -> comparable) -> (a -> Bool) -> List a -> Maybe a +findLastBy sortFunction cond l = findLast cond <| List.sortBy sortFunction l + diff --git a/src/Scylla/Model.elm b/src/Scylla/Model.elm index 92a271e..3662faf 100644 --- a/src/Scylla/Model.elm +++ b/src/Scylla/Model.elm @@ -1,9 +1,10 @@ module Scylla.Model exposing (..) import Scylla.Api exposing (..) -import Scylla.Sync exposing (SyncResponse, HistoryResponse, senderName, roomName, roomJoinedUsers, findFirst) +import Scylla.Sync exposing (SyncResponse, HistoryResponse, senderName, roomName, roomJoinedUsers) +import Scylla.ListUtils exposing (findFirst) +import Scylla.Room exposing (OpenRooms) import Scylla.Sync.Rooms exposing (JoinedRoom) -import Scylla.Sync.AccountData exposing (AccountData) -import Scylla.AccountData exposing (directMessagesDecoder) +import Scylla.Sync.AccountData exposing (AccountData, directMessagesDecoder) import Scylla.Login exposing (LoginResponse, Username, Password) import Scylla.UserData exposing (UserData) import Scylla.Route exposing (Route(..), RoomId) @@ -37,6 +38,7 @@ type alias Model = , roomNames : Dict RoomId String , connected : Bool , searchText : String + , rooms : OpenRooms } type Msg = diff --git a/src/Scylla/Notification.elm b/src/Scylla/Notification.elm index c3b826b..8af3257 100644 --- a/src/Scylla/Notification.elm +++ b/src/Scylla/Notification.elm @@ -1,7 +1,6 @@ port module Scylla.Notification exposing (..) import Scylla.Sync exposing (SyncResponse, joinedRoomsTimelineEvents) import Scylla.Sync.Events exposing (RoomEvent, MessageEvent, toMessageEvent) -import Scylla.AccountData exposing (..) import Json.Decode as Decode exposing (string, field) import Dict diff --git a/src/Scylla/Room.elm b/src/Scylla/Room.elm index 532ba82..0aec4f3 100644 --- a/src/Scylla/Room.elm +++ b/src/Scylla/Room.elm @@ -1,10 +1,12 @@ module Scylla.Room exposing (..) import Scylla.Route exposing (RoomId) import Scylla.Sync exposing (SyncResponse) +import Scylla.Login exposing (Username) +import Scylla.UserData exposing (UserData) import Scylla.Sync.Events exposing (MessageEvent, StateEvent, toStateEvent, toMessageEvent) -import Scylla.Sync.AccountData exposing (AccountData) +import Scylla.Sync.AccountData exposing (AccountData, getDirectMessages) import Scylla.Sync.Rooms exposing (JoinedRoom, UnreadNotificationCounts, Ephemeral) -import Json.Decode as Decode exposing (Value) +import Json.Decode as Decode exposing (Decoder, Value, decodeValue) import Dict exposing (Dict) type alias RoomState = Dict (String, String) Value @@ -108,3 +110,19 @@ applySync sr or = |> Maybe.withDefault Dict.empty in Dict.foldl applyJoinedRoom or joinedRooms + +getStateData : (String, String) -> Decoder a -> RoomData -> Maybe a +getStateData k d rd = Dict.get k rd.roomState + |> Maybe.andThen (Result.toMaybe << decodeValue d) + +getRoomName : Maybe AccountData -> Dict Username UserData -> RoomId -> RoomData -> String +getRoomName ad ud rid rd = + let + customName = getStateData ("m.room.name", "") Decode.string rd + direct = Maybe.andThen getDirectMessages ad + |> Maybe.andThen (Dict.get rid) + in + case (customName, direct) of + (Just cn, _) -> cn + (_, Just d) -> d + _ -> rid diff --git a/src/Scylla/Sync.elm b/src/Scylla/Sync.elm index fc7a5e3..e6f5ea3 100644 --- a/src/Scylla/Sync.elm +++ b/src/Scylla/Sync.elm @@ -2,6 +2,7 @@ module Scylla.Sync exposing (..) import Scylla.Api exposing (..) import Scylla.Login exposing (Username) import Scylla.Route exposing (RoomId) +import Scylla.ListUtils exposing (..) import Scylla.Sync.DecodeTools exposing (maybeDecode) import Scylla.Sync.Events exposing (..) import Scylla.Sync.Rooms exposing (..) @@ -51,41 +52,6 @@ historyResponseDecoder = |> required "chunk" (list roomEventDecoder) -- Business Logic: Helper Functions -groupBy : (a -> comparable) -> List a -> Dict comparable (List a) -groupBy f xs = - let - update v ml = case ml of - Just l -> Just (v::l) - Nothing -> Just [ v ] - in - List.foldl (\v acc -> Dict.update (f v) (update v) acc) Dict.empty xs - -uniqueByTailRecursive : (a -> comparable) -> List a -> Set comparable -> List a -> List a -uniqueByTailRecursive f l s acc = - case l of - x::tail -> - if Set.member (f x) s - then uniqueByTailRecursive f tail s acc - else uniqueByTailRecursive f tail (Set.insert (f x) s) (x::acc) - [] -> List.reverse acc - -uniqueBy : (a -> comparable) -> List a -> List a -uniqueBy f l = uniqueByTailRecursive f l Set.empty [] - -findFirst : (a -> Bool) -> List a -> Maybe a -findFirst cond l = case l of - x::xs -> if cond x then Just x else findFirst cond xs - [] -> Nothing - -findLast : (a -> Bool) -> List a -> Maybe a -findLast cond l = findFirst cond <| List.reverse l - -findFirstBy : (a -> comparable) -> (a -> Bool) -> List a -> Maybe a -findFirstBy sortFunction cond l = findFirst cond <| List.sortBy sortFunction l - -findLastBy : (a -> comparable) -> (a -> Bool) -> List a -> Maybe a -findLastBy sortFunction cond l = findLast cond <| List.sortBy sortFunction l - findFirstEvent : ({ a | originServerTs : Int } -> Bool) -> List { a | originServerTs : Int } -> Maybe { a | originServerTs : Int } findFirstEvent = findFirstBy .originServerTs diff --git a/src/Scylla/Sync/AccountData.elm b/src/Scylla/Sync/AccountData.elm index 748b09d..3c1e332 100644 --- a/src/Scylla/Sync/AccountData.elm +++ b/src/Scylla/Sync/AccountData.elm @@ -1,7 +1,9 @@ module Scylla.Sync.AccountData exposing (..) +import Scylla.ListUtils exposing (..) import Scylla.Sync.DecodeTools exposing (maybeDecode) import Scylla.Sync.Events exposing (Event, eventDecoder) -import Json.Decode as Decode exposing (Decoder, list) +import Json.Decode as Decode exposing (Decoder, list, decodeValue) +import Dict exposing (Dict) type alias AccountData = { events : Maybe (List Event) @@ -12,3 +14,27 @@ accountDataDecoder = Decode.succeed AccountData |> maybeDecode "events" (list eventDecoder) +type alias DirectMessages = Dict String String + +directMessagesDecoder : Decode.Decoder DirectMessages +directMessagesDecoder = + Decode.dict (Decode.list Decode.string) + |> Decode.map (invertDirectMessages) + +type alias DirectMessagesRaw = Dict String (List String) + +invertDirectMessages : DirectMessagesRaw -> DirectMessages +invertDirectMessages dmr = + Dict.foldl + (\k lv acc -> List.foldl (\v -> Dict.insert v k) acc lv) + Dict.empty + dmr + +getAccountData : String -> Decode.Decoder a -> AccountData -> Maybe a +getAccountData key d ad = ad.events + |> Maybe.andThen (findFirst ((==) key << .type_)) + |> Maybe.map .content + |> Maybe.andThen (Result.toMaybe << decodeValue d) + +getDirectMessages : AccountData -> Maybe DirectMessages +getDirectMessages = getAccountData "m.direct" directMessagesDecoder diff --git a/src/Scylla/Views.elm b/src/Scylla/Views.elm index 9cea669..3136a0b 100644 --- a/src/Scylla/Views.elm +++ b/src/Scylla/Views.elm @@ -3,6 +3,7 @@ import Scylla.Model exposing (..) import Scylla.Sync exposing (..) import Scylla.Sync.Events exposing (..) import Scylla.Sync.Rooms exposing (..) +import Scylla.Room exposing (RoomData, emptyOpenRooms, getRoomName) import Scylla.Route exposing (..) import Scylla.Fnv as Fnv import Scylla.Messages exposing (..) @@ -10,6 +11,7 @@ import Scylla.Login exposing (Username) import Scylla.UserData exposing (UserData) import Scylla.Http exposing (fullMediaUrl) import Scylla.Api exposing (ApiUrl) +import Scylla.ListUtils exposing (groupBy) import Html.Parser import Html.Parser.Util import Svg @@ -85,11 +87,8 @@ reconnectView m = if m.connected roomListView : Model -> Html Msg roomListView m = let - rooms = Maybe.withDefault (Dict.empty) - <| Maybe.andThen .join - <| m.sync.rooms groups = roomGroups - <| Dict.toList rooms + <| Dict.toList m.rooms homeserverList = div [ class "homeservers-list" ] <| List.map (\(k, v) -> homeserverView m k v) <| Dict.toList groups @@ -106,22 +105,22 @@ roomListView m = , homeserverList ] -roomGroups : List (String, JoinedRoom) -> Dict String (List (String, JoinedRoom)) +roomGroups : List (String, RoomData) -> Dict String (List (String, RoomData)) roomGroups jrs = groupBy (homeserver << Tuple.first) jrs -homeserverView : Model -> String -> List (String, JoinedRoom) -> Html Msg +homeserverView : Model -> String -> List (String, RoomData) -> Html Msg homeserverView m hs rs = let roomList = div [ class "rooms-list" ] <| List.map (\(rid, r) -> roomListElementView m rid r) - <| List.sortBy (\(rid, r) -> roomDisplayName m.roomNames rid) rs + <| List.sortBy (\(rid, r) -> getRoomName m.sync.accountData m.userData rid r) rs in div [ class "homeserver-wrapper" ] [ h3 [] [ text hs ], roomList ] -roomListElementView : Model -> RoomId -> JoinedRoom -> Html Msg -roomListElementView m rid jr = +roomListElementView : Model -> RoomId -> RoomData -> Html Msg +roomListElementView m rid rd = let - name = roomDisplayName m.roomNames rid + name = getRoomName m.sync.accountData m.userData rid rd isVisible = m.searchText == "" || (String.contains (String.toLower m.searchText) <| String.toLower name) isCurrentRoom = case currentRoomId m of Nothing -> False @@ -133,10 +132,10 @@ roomListElementView m rid jr = , ("hidden", not isVisible) ] ] - <| roomNotificationCountView jr.unreadNotifications ++ + <| roomNotificationCountView rd.unreadNotifications ++ [ a [ href <| roomUrl rid ] [ text name ] ] -roomNotificationCountView : Maybe UnreadNotificationCounts -> List (Html Msg) +roomNotificationCountView : UnreadNotificationCounts -> List (Html Msg) roomNotificationCountView ns = let wrap b = span @@ -145,7 +144,7 @@ roomNotificationCountView ns = , ("bright", b) ] ] - getCount f = Maybe.withDefault 0 << Maybe.andThen f + getCount f = Maybe.withDefault 0 << f in case (getCount .notificationCount ns, getCount .highlightCount ns) of (0, 0) -> []