mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +00:00
ui: Reimplement message monitor as web component, closes #128
This commit is contained in:
@@ -111,9 +111,6 @@ pageSubscriptions page =
|
|||||||
Mailbox subModel ->
|
Mailbox subModel ->
|
||||||
Sub.map MailboxMsg (Mailbox.subscriptions subModel)
|
Sub.map MailboxMsg (Mailbox.subscriptions subModel)
|
||||||
|
|
||||||
Monitor subModel ->
|
|
||||||
Sub.map MonitorMsg (Monitor.subscriptions subModel)
|
|
||||||
|
|
||||||
Status subModel ->
|
Status subModel ->
|
||||||
Sub.map StatusMsg (Status.subscriptions subModel)
|
Sub.map StatusMsg (Status.subscriptions subModel)
|
||||||
|
|
||||||
@@ -251,59 +248,50 @@ changeRouteTo route model =
|
|||||||
let
|
let
|
||||||
session =
|
session =
|
||||||
getSession model
|
getSession model
|
||||||
|
|
||||||
( newModel, newCmd ) =
|
|
||||||
case route of
|
|
||||||
Route.Unknown path ->
|
|
||||||
let
|
|
||||||
flash =
|
|
||||||
{ title = "Unknown route requested"
|
|
||||||
, table = [ ( "Path", path ) ]
|
|
||||||
}
|
|
||||||
in
|
|
||||||
( applyToModelSession (Session.showFlash flash) model
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Route.Home ->
|
|
||||||
Home.init session
|
|
||||||
|> updateWith Home HomeMsg model
|
|
||||||
|
|
||||||
Route.Mailbox name ->
|
|
||||||
Mailbox.init session name Nothing
|
|
||||||
|> updateWith Mailbox MailboxMsg model
|
|
||||||
|
|
||||||
Route.Message mailbox id ->
|
|
||||||
Mailbox.init session mailbox (Just id)
|
|
||||||
|> updateWith Mailbox MailboxMsg model
|
|
||||||
|
|
||||||
Route.Monitor ->
|
|
||||||
if session.config.monitorVisible then
|
|
||||||
Monitor.init session
|
|
||||||
|> updateWith Monitor MonitorMsg model
|
|
||||||
|
|
||||||
else
|
|
||||||
let
|
|
||||||
flash =
|
|
||||||
{ title = "Unknown route requested"
|
|
||||||
, table = [ ( "Error", "Monitor disabled by configuration." ) ]
|
|
||||||
}
|
|
||||||
in
|
|
||||||
( applyToModelSession (Session.showFlash flash) model
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
Route.Status ->
|
|
||||||
Status.init session
|
|
||||||
|> updateWith Status StatusMsg model
|
|
||||||
in
|
in
|
||||||
case model.page of
|
case route of
|
||||||
Monitor _ ->
|
Route.Unknown path ->
|
||||||
-- Leaving Monitor page, shut down the web socket.
|
let
|
||||||
( newModel, Cmd.batch [ Ports.monitorCommand False, newCmd ] )
|
flash =
|
||||||
|
{ title = "Unknown route requested"
|
||||||
|
, table = [ ( "Path", path ) ]
|
||||||
|
}
|
||||||
|
in
|
||||||
|
( applyToModelSession (Session.showFlash flash) model
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
_ ->
|
Route.Home ->
|
||||||
( newModel, newCmd )
|
Home.init session
|
||||||
|
|> updateWith Home HomeMsg model
|
||||||
|
|
||||||
|
Route.Mailbox name ->
|
||||||
|
Mailbox.init session name Nothing
|
||||||
|
|> updateWith Mailbox MailboxMsg model
|
||||||
|
|
||||||
|
Route.Message mailbox id ->
|
||||||
|
Mailbox.init session mailbox (Just id)
|
||||||
|
|> updateWith Mailbox MailboxMsg model
|
||||||
|
|
||||||
|
Route.Monitor ->
|
||||||
|
if session.config.monitorVisible then
|
||||||
|
Monitor.init session
|
||||||
|
|> updateWith Monitor MonitorMsg model
|
||||||
|
|
||||||
|
else
|
||||||
|
let
|
||||||
|
flash =
|
||||||
|
{ title = "Unknown route requested"
|
||||||
|
, table = [ ( "Error", "Monitor disabled by configuration." ) ]
|
||||||
|
}
|
||||||
|
in
|
||||||
|
( applyToModelSession (Session.showFlash flash) model
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
Route.Status ->
|
||||||
|
Status.init session
|
||||||
|
|> updateWith Status StatusMsg model
|
||||||
|
|
||||||
|
|
||||||
getSession : Model -> Session
|
getSession : Model -> Session
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module Page.Monitor exposing (Model, Msg, init, subscriptions, update, view)
|
module Page.Monitor exposing (Model, Msg, init, update, view)
|
||||||
|
|
||||||
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
||||||
import Data.Session as Session exposing (Session)
|
import Data.Session as Session exposing (Session)
|
||||||
@@ -7,7 +7,6 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events as Events
|
import Html.Events as Events
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
import Ports
|
|
||||||
import Route
|
import Route
|
||||||
import Time exposing (Posix)
|
import Time exposing (Posix)
|
||||||
|
|
||||||
@@ -23,34 +22,9 @@ type alias Model =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type MonitorMessage
|
|
||||||
= Connected Bool
|
|
||||||
| Message MessageHeader
|
|
||||||
|
|
||||||
|
|
||||||
init : Session -> ( Model, Cmd Msg )
|
init : Session -> ( Model, Cmd Msg )
|
||||||
init session =
|
init session =
|
||||||
( Model session False []
|
( Model session False [], Cmd.none )
|
||||||
, Ports.monitorCommand True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
|
||||||
|
|
||||||
|
|
||||||
subscriptions : Model -> Sub Msg
|
|
||||||
subscriptions model =
|
|
||||||
let
|
|
||||||
monitorMessage =
|
|
||||||
D.oneOf
|
|
||||||
[ D.map Message MessageHeader.decoder
|
|
||||||
, D.map Connected D.bool
|
|
||||||
]
|
|
||||||
|> D.decodeValue
|
|
||||||
|> Ports.monitorMessage
|
|
||||||
in
|
|
||||||
Sub.map MessageReceived monitorMessage
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -58,29 +32,37 @@ subscriptions model =
|
|||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= MessageReceived (Result D.Error MonitorMessage)
|
= Connected Bool
|
||||||
|
| MessageReceived D.Value
|
||||||
| OpenMessage MessageHeader
|
| OpenMessage MessageHeader
|
||||||
|
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
update msg model =
|
update msg model =
|
||||||
case msg of
|
case msg of
|
||||||
MessageReceived (Ok (Connected status)) ->
|
Connected True ->
|
||||||
( { model | connected = status }, Cmd.none )
|
( { model | connected = True, messages = [] }, Cmd.none )
|
||||||
|
|
||||||
MessageReceived (Ok (Message header)) ->
|
Connected False ->
|
||||||
( { model | messages = header :: model.messages }, Cmd.none )
|
( { model | connected = False }, Cmd.none )
|
||||||
|
|
||||||
MessageReceived (Err err) ->
|
MessageReceived value ->
|
||||||
let
|
case D.decodeValue (MessageHeader.decoder |> D.at [ "detail" ]) value of
|
||||||
flash =
|
Ok header ->
|
||||||
{ title = "Decoding failed"
|
( { model | messages = header :: model.messages }
|
||||||
, table = [ ( "Error", D.errorToString err ) ]
|
, Cmd.none
|
||||||
}
|
)
|
||||||
in
|
|
||||||
( { model | session = Session.showFlash flash model.session }
|
Err err ->
|
||||||
, Cmd.none
|
let
|
||||||
)
|
flash =
|
||||||
|
{ title = "Message decoding failed"
|
||||||
|
, table = [ ( "Error", D.errorToString err ) ]
|
||||||
|
}
|
||||||
|
in
|
||||||
|
( { model | session = Session.showFlash flash model.session }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
OpenMessage header ->
|
OpenMessage header ->
|
||||||
( model
|
( model
|
||||||
@@ -110,6 +92,11 @@ view model =
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
, node "monitor-messages"
|
||||||
|
[ Events.on "connected" (D.map Connected <| D.at [ "detail" ] <| D.bool)
|
||||||
|
, Events.on "message" (D.map MessageReceived D.value)
|
||||||
|
]
|
||||||
|
[]
|
||||||
, table [ class "monitor" ]
|
, table [ class "monitor" ]
|
||||||
[ thead []
|
[ thead []
|
||||||
[ th [] [ text "Date" ]
|
[ th [] [ text "Date" ]
|
||||||
@@ -143,6 +130,8 @@ shortDate zone date =
|
|||||||
, DF.hourNumber
|
, DF.hourNumber
|
||||||
, DF.text ":"
|
, DF.text ":"
|
||||||
, DF.minuteFixed
|
, DF.minuteFixed
|
||||||
|
, DF.text ":"
|
||||||
|
, DF.secondFixed
|
||||||
, DF.text " "
|
, DF.text " "
|
||||||
, DF.amPmUppercase
|
, DF.amPmUppercase
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
port module Ports exposing
|
port module Ports exposing
|
||||||
( monitorCommand
|
( onSessionChange
|
||||||
, monitorMessage
|
|
||||||
, onSessionChange
|
|
||||||
, storeSession
|
, storeSession
|
||||||
)
|
)
|
||||||
|
|
||||||
import Json.Encode exposing (Value)
|
import Json.Encode exposing (Value)
|
||||||
|
|
||||||
|
|
||||||
port monitorCommand : Bool -> Cmd msg
|
|
||||||
|
|
||||||
|
|
||||||
port monitorMessage : (Value -> msg) -> Sub msg
|
|
||||||
|
|
||||||
|
|
||||||
port onSessionChange : (Value -> msg) -> Sub msg
|
port onSessionChange : (Value -> msg) -> Sub msg
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import './main.css'
|
import './main.css'
|
||||||
import '@fortawesome/fontawesome-free/css/all.css'
|
import '@fortawesome/fontawesome-free/css/all.css'
|
||||||
import { Elm } from './Main.elm'
|
import { Elm } from './Main.elm'
|
||||||
import registerMonitorPorts from './registerMonitor'
|
import './monitorMessages'
|
||||||
import './renderedHtml'
|
import './renderedHtml'
|
||||||
|
|
||||||
// Initial configuration from Inbucket server to Elm App.
|
// Initial configuration from Inbucket server to Elm App.
|
||||||
@@ -16,9 +16,6 @@ var app = Elm.Main.init({
|
|||||||
flags: flags,
|
flags: flags,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Message monitor.
|
|
||||||
registerMonitorPorts(app)
|
|
||||||
|
|
||||||
// Session storage.
|
// Session storage.
|
||||||
app.ports.storeSession.subscribe(function (session) {
|
app.ports.storeSession.subscribe(function (session) {
|
||||||
localStorage.session = JSON.stringify(session)
|
localStorage.session = JSON.stringify(session)
|
||||||
|
|||||||
38
ui/src/monitorMessages.js
Normal file
38
ui/src/monitorMessages.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// monitor-messages connects to the Inbucket backend via WebSocket to monitor
|
||||||
|
// incoming messages.
|
||||||
|
customElements.define(
|
||||||
|
'monitor-messages',
|
||||||
|
class MonitorMessages extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
const self = super()
|
||||||
|
// TODO make URI/URL configurable.
|
||||||
|
var uri = '/api/v1/monitor/messages'
|
||||||
|
self._url = ((window.location.protocol === 'https:') ? 'wss://' : 'ws://') + window.location.host + uri
|
||||||
|
self._socket = null
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
const self = this
|
||||||
|
self._socket = new WebSocket(self._url)
|
||||||
|
var ws = self._socket
|
||||||
|
ws.addEventListener('open', function (e) {
|
||||||
|
self.dispatchEvent(new CustomEvent('connected', { detail: true }))
|
||||||
|
})
|
||||||
|
ws.addEventListener('close', function (e) {
|
||||||
|
self.dispatchEvent(new CustomEvent('connected', { detail: false }))
|
||||||
|
})
|
||||||
|
ws.addEventListener('message', function (e) {
|
||||||
|
self.dispatchEvent(new CustomEvent('message', {
|
||||||
|
detail: JSON.parse(e.data),
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
var ws = this._socket
|
||||||
|
if (ws) {
|
||||||
|
ws.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
// Register the websocket listeners for the monitor API.
|
|
||||||
export default function registerMonitorPorts(app) {
|
|
||||||
var uri = '/api/v1/monitor/messages'
|
|
||||||
var url = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + uri
|
|
||||||
|
|
||||||
// Current handler.
|
|
||||||
var handler = null
|
|
||||||
|
|
||||||
app.ports.monitorCommand.subscribe(function (cmd) {
|
|
||||||
if (handler != null) {
|
|
||||||
handler.down()
|
|
||||||
handler = null
|
|
||||||
}
|
|
||||||
if (cmd) {
|
|
||||||
// Command is up.
|
|
||||||
handler = websocketHandler(url, app.ports.monitorMessage)
|
|
||||||
handler.up()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a handler responsible for connecting, disconnecting from web socket.
|
|
||||||
function websocketHandler(url, port) {
|
|
||||||
var ws = null
|
|
||||||
|
|
||||||
return {
|
|
||||||
up: () => {
|
|
||||||
ws = new WebSocket(url)
|
|
||||||
|
|
||||||
ws.addEventListener('open', function (e) {
|
|
||||||
port.send(true)
|
|
||||||
})
|
|
||||||
ws.addEventListener('close', function (e) {
|
|
||||||
port.send(false)
|
|
||||||
})
|
|
||||||
ws.addEventListener('message', function (e) {
|
|
||||||
var msg = JSON.parse(e.data)
|
|
||||||
port.send(msg)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
down: () => {
|
|
||||||
ws.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user