diff --git a/public/index.html b/public/index.html index 29da4be..889546c 100644 --- a/public/index.html +++ b/public/index.html @@ -77,6 +77,26 @@ .then(response => response.text()) .then(app.ports.uploadSuccess.send); }); + + function notify(data) { + const notif = new Notification(data.title, { + icon: data.icon, + body: data.body, + }); + notif.onclick = () => location.hash = data.clickUrl; + } + + app.ports.notify.subscribe(data => { + if (Notification.permission === "granted") { + notify(data); + } else if (Notification.permission !== "denied") { + Notification.requestPermission(permission => { + if (permission === "granted") { + notify(data); + } + }); + } + }); diff --git a/src/Command.elm b/src/Command.elm index 0df3196..fabf0cf 100644 --- a/src/Command.elm +++ b/src/Command.elm @@ -42,6 +42,8 @@ module Command , scrollToThreadStatus , searchAccounts , search + , notifyStatus + , notifyNotification ) import Dom @@ -59,6 +61,7 @@ import Ports import String.Extra exposing (replace) import Task import Types exposing (..) +import View.Formatter exposing (textContent) initCommands : Maybe AppRegistration -> Maybe Client -> Maybe String -> Cmd Msg @@ -669,3 +672,59 @@ scrollColumnToBottom column = scrollToThreadStatus : String -> Cmd Msg scrollToThreadStatus cssId = Ports.scrollIntoView <| "thread-status-" ++ cssId + + +notifyStatus : Status -> Cmd Msg +notifyStatus status = + Ports.notify + { title = status.account.acct + , icon = status.account.avatar + , body = status.content |> textContent + , clickUrl = "#thread/" ++ (toString status.id) + } + + +notifyNotification : Notification -> Cmd Msg +notifyNotification notification = + case notification.status of + Just status -> + case notification.type_ of + "reblog" -> + Ports.notify + { title = notification.account.acct ++ " reboosted" + , icon = notification.account.avatar + , body = status.content |> textContent + , clickUrl = "#thread/" ++ (toString status.id) + } + + "favourite" -> + Ports.notify + { title = notification.account.acct ++ " favorited" + , icon = notification.account.avatar + , body = status.content |> textContent + , clickUrl = "#thread/" ++ (toString status.id) + } + + "mention" -> + Ports.notify + { title = notification.account.acct ++ " mentioned you" + , icon = notification.account.avatar + , body = status.content |> textContent + , clickUrl = "#thread/" ++ (toString status.id) + } + + _ -> + Cmd.none + + Nothing -> + case notification.type_ of + "follow" -> + Ports.notify + { title = notification.account.acct ++ " follows you" + , icon = notification.account.avatar + , body = notification.account.note + , clickUrl = "#account/" ++ (toString notification.account.id) + } + + _ -> + Cmd.none diff --git a/src/Ports.elm b/src/Ports.elm index 99865f2..ff1b9f2 100644 --- a/src/Ports.elm +++ b/src/Ports.elm @@ -6,6 +6,7 @@ port module Ports , saveClients , setStatus , uploadMedia + , notify , uploadSuccess , uploadError ) @@ -31,6 +32,9 @@ port scrollIntoView : String -> Cmd msg port uploadMedia : { id : String, url : String, token : String } -> Cmd msg +port notify : { title : String, icon : String, body : String, clickUrl : String } -> Cmd msg + + -- Incoming ports diff --git a/src/Update/WebSocket.elm b/src/Update/WebSocket.elm index 5568157..8626906 100644 --- a/src/Update/WebSocket.elm +++ b/src/Update/WebSocket.elm @@ -1,5 +1,6 @@ module Update.WebSocket exposing (update) +import Command import Mastodon.Decoder import Mastodon.Helper import Mastodon.Model exposing (..) @@ -52,7 +53,8 @@ update msg model = oldNotifications.entries } in - { model | notifications = newNotifications } ! [] + { model | notifications = newNotifications } + ! [ Command.notifyNotification notification ] Err error -> { model | errors = addErrorNotification error model } ! [] diff --git a/src/View/Formatter.elm b/src/View/Formatter.elm index cbe90ce..957cb80 100644 --- a/src/View/Formatter.elm +++ b/src/View/Formatter.elm @@ -1,10 +1,11 @@ -module View.Formatter exposing (formatContent) +module View.Formatter exposing (formatContent, textContent) import Dict import Elmoji import Html exposing (..) import Html.Attributes exposing (..) import HtmlParser +import HtmlParser.Util as ParseUtil import Http import Mastodon.Model exposing (..) import String.Extra exposing (replace, rightOf) @@ -22,6 +23,11 @@ formatContent content mentions = |> toVirtualDom mentions +textContent : String -> String +textContent html = + html |> HtmlParser.parse |> ParseUtil.textContent + + {-| Converts nodes to virtual dom nodes. -} toVirtualDom : List Mention -> List HtmlParser.Node -> List (Html Msg)