1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 17:47:03 +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,8 +248,7 @@ changeRouteTo route model =
let let
session = session =
getSession model getSession model
in
( newModel, newCmd ) =
case route of case route of
Route.Unknown path -> Route.Unknown path ->
let let
@@ -296,14 +292,6 @@ changeRouteTo route model =
Route.Status -> Route.Status ->
Status.init session Status.init session
|> updateWith Status StatusMsg model |> updateWith Status StatusMsg model
in
case model.page of
Monitor _ ->
-- Leaving Monitor page, shut down the web socket.
( newModel, Cmd.batch [ Ports.monitorCommand False, newCmd ] )
_ ->
( newModel, newCmd )
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,23 +32,31 @@ 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 ->
case D.decodeValue (MessageHeader.decoder |> D.at [ "detail" ]) value of
Ok header ->
( { model | messages = header :: model.messages }
, Cmd.none
)
Err err ->
let let
flash = flash =
{ title = "Decoding failed" { title = "Message decoding failed"
, table = [ ( "Error", D.errorToString err ) ] , table = [ ( "Error", D.errorToString err ) ]
} }
in in
@@ -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()
}
}
}