Compare commits

..

3 Commits

8 changed files with 90 additions and 16 deletions

View File

@ -36,6 +36,7 @@ init flags url key =
, errors = []
, roomText = Dict.empty
, transactionId = 0
, userData = Dict.empty
}
cmd = case flags.token of
Just _ -> Cmd.none
@ -58,7 +59,9 @@ update msg model = case msg of
TryUrl urlRequest -> updateTryUrl model urlRequest
ChangeRoute r -> ({ model | route = r }, Cmd.none)
ReceiveLoginResponse r -> updateLoginResponse model r
ReceiveSyncResponse r -> updateSyncResponse model r
ReceiveFirstSyncResponse r -> updateSyncResponse model r False
ReceiveSyncResponse r -> updateSyncResponse model r True
ReceiveUserData s r -> (model, Cmd.none)
ChangeRoomText r t -> ({ model | roomText = Dict.insert r t model.roomText}, Cmd.none)
SendRoomText r -> updateSendRoomText model r
SendRoomTextResponse r -> (model, Cmd.none)
@ -87,8 +90,8 @@ updateLoginResponse model r = case r of
] )
Err e -> (model, Cmd.none)
updateSyncResponse : Model -> Result Http.Error SyncResponse -> (Model, Cmd Msg)
updateSyncResponse model r =
updateSyncResponse : Model -> Result Http.Error SyncResponse -> Bool -> (Model, Cmd Msg)
updateSyncResponse model r notify =
let
token = Maybe.withDefault "" model.token
nextBatch = Result.withDefault model.sync.nextBatch

View File

@ -3,6 +3,7 @@ import Scylla.Model exposing (..)
import Scylla.Api exposing (..)
import Scylla.Sync exposing (syncResponseDecoder)
import Scylla.Login exposing (loginResponseDecoder, Username, Password)
import Scylla.UserData exposing (userDataDecoder, UserData)
import Json.Encode exposing (object, string, int)
import Http exposing (request, emptyBody, jsonBody, expectJson, expectWhatever)
@ -16,7 +17,7 @@ firstSync apiUrl token = request
, headers = authenticatedHeaders token
, url = (fullUrl apiUrl) ++ "/sync"
, body = emptyBody
, expect = expectJson ReceiveSyncResponse syncResponseDecoder
, expect = expectJson ReceiveFirstSyncResponse syncResponseDecoder
, timeout = Nothing
, tracker = Nothing
}
@ -66,3 +67,14 @@ login apiUrl username password = request
, timeout = Nothing
, tracker = Nothing
}
userData : ApiUrl -> ApiToken -> Username -> Cmd Msg
userData apiUrl token username = request
{ method = "GET"
, headers = authenticatedHeaders token
, url = (fullUrl apiUrl) ++ "/profile/" ++ username
, body = emptyBody
, expect = expectJson (ReceiveUserData username) userDataDecoder
, timeout = Nothing
, tracker = Nothing
}

View File

@ -2,6 +2,7 @@ module Scylla.Model exposing (..)
import Scylla.Api exposing (..)
import Scylla.Sync exposing (SyncResponse, JoinedRoom)
import Scylla.Login exposing (LoginResponse, Username, Password)
import Scylla.UserData exposing (UserData)
import Scylla.Route exposing (Route)
import Browser.Navigation as Nav
import Dict exposing (Dict)
@ -20,6 +21,7 @@ type alias Model =
, errors : List String
, roomText : Dict String String
, transactionId : Int
, userData : Dict Username UserData
}
type Msg =
@ -32,6 +34,8 @@ type Msg =
| ChangeRoomText String String -- Change to a room's input text
| SendRoomText String -- Sends a message typed into a given room's input
| SendRoomTextResponse (Result Http.Error ()) -- A send message response finished
| ReceiveFirstSyncResponse (Result Http.Error SyncResponse) -- HTTP, Sync has finished
| ReceiveSyncResponse (Result Http.Error SyncResponse) -- HTTP, Sync has finished
| ReceiveLoginResponse (Result Http.Error LoginResponse) -- HTTP, Login has finished
| ReceiveUserData Username (Result Http.Error UserData)

View File

@ -1,11 +1,11 @@
port module Scylla.Notification exposing (..)
import Scylla.Model exposing (..)
import Json.Decode
type alias Notification =
{ name : String
, text : String
, room : String
}
port sendNotificationPort : Notification -> Cmd msg
port onNotificationClockPort : (Json.Decode.Value -> msg) -> Sub msg
port onNotificationClickPort : (Json.Decode.Value -> msg) -> Sub msg

View File

@ -1,5 +1,7 @@
module Scylla.Sync exposing (..)
import Scylla.Api exposing (..)
import Scylla.Notification exposing (..)
import Scylla.Login exposing (Username)
import Dict exposing (Dict)
import Json.Decode as Decode exposing (Decoder, int, string, float, list, value, dict, bool, field)
import Json.Decode.Pipeline exposing (required, optional)
@ -267,6 +269,7 @@ uniqueByRecursive f l s = case l of
uniqueBy : (a -> comparable) -> List a -> List a
uniqueBy f l = uniqueByRecursive f l Set.empty
-- Business Logic: Merging
mergeMaybe : (a -> a -> a) -> Maybe a -> Maybe a -> Maybe a
mergeMaybe f l r = case (l, r) of
(Just v1, Just v2) -> Just <| f v1 v2
@ -347,6 +350,16 @@ mergeSyncResponse l r =
, accountData = mergeMaybe mergeAccountData l.accountData r.accountData
}
-- Business Logic: Names
senderName : String -> String
senderName s =
let
colonIndex = Maybe.withDefault -1
<| List.head
<| String.indexes ":" s
in
String.slice 1 colonIndex s
roomName : JoinedRoom -> Maybe String
roomName jr =
let
@ -355,3 +368,35 @@ roomName jr =
name e = Result.toMaybe <| Decode.decodeValue (field "name" string) e.content
in
Maybe.andThen name <| Maybe.andThen nameEvent <| Maybe.andThen .events <| state
-- Business Logic: Event Extraction
notificationEvent : SyncResponse -> Maybe (String, RoomEvent)
notificationEvent s =
let
applyPair k = List.map (\v -> (k, v))
in
List.head
<| List.sortBy (\(k, v) -> v.originServerTs)
<| Dict.foldl (\k v a -> a ++ applyPair k v) []
<| joinedRoomsEvents s
joinedRoomsEvents : SyncResponse -> Dict String (List RoomEvent)
joinedRoomsEvents s =
Maybe.withDefault Dict.empty
<| Maybe.map (Dict.map (\k v -> Maybe.withDefault [] <| Maybe.andThen .events v.timeline))
<| Maybe.andThen .join s.rooms
-- Business Logic: User Extraction
roomsUsers : SyncResponse -> List Username
roomsUsers s =
let
users dict =
List.map .sender
<| (List.concatMap <| Maybe.withDefault [] << .events)
<| (List.filterMap .timeline)
<| Dict.values dict
usersFor f = Maybe.withDefault [] <| Maybe.map users <| Maybe.andThen f s.rooms
joinedUsers = usersFor .join
leftUsers = usersFor .leave
in
leftUsers ++ joinedUsers

14
src/Scylla/UserData.elm Normal file
View File

@ -0,0 +1,14 @@
module Scylla.UserData exposing (..)
import Json.Decode as Decode exposing (Decoder, int, string, float, list, value, dict, bool, field)
import Json.Decode.Pipeline exposing (required, optional)
type alias UserData =
{ displayName : Maybe String
, avatarUrl : Maybe String
}
userDataDecoder : Decoder UserData
userDataDecoder =
Decode.succeed UserData
|> optional "displayname" (Decode.map Just string) Nothing
|> optional "avatar_url" (Decode.map Just string) Nothing

View File

@ -19,15 +19,6 @@ stringColor s =
in
"hsl(" ++ hue ++ ", 82%, 71%)"
senderName : String -> String
senderName s =
let
colonIndex = Maybe.withDefault -1
<| List.head
<| String.indexes ":" s
in
String.slice 1 colonIndex s
viewFull : Model -> List (Html Msg)
viewFull model =
let

View File

@ -7,6 +7,11 @@ function setupNotificationPorts(app) {
var options = {
"body" : data.text
}
new Notification(data.name, options)
var n = new Notification(data.name, options)
n.onclick = function() {
app.ports.onNotificationClickPort.send({
"room" : data.room
});
}
})
}