1
0
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:
James Hillyerd
2019-01-01 10:58:14 -08:00
parent dbdc60a0fb
commit f47e2cfcc2
6 changed files with 114 additions and 156 deletions

View File

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

View File

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

View File

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

View File

@@ -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
View 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()
}
}
}
)

View File

@@ -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()
}
}
}