Start switching from sync to room data

This commit is contained in:
Danila Fedorin 2019-09-10 23:24:47 -07:00
parent 676d6c28a7
commit 29e81a88ac
9 changed files with 110 additions and 79 deletions

View File

@ -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

View File

@ -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

39
src/Scylla/ListUtils.elm Normal file
View File

@ -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

View File

@ -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 =

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) -> []