1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-21 11:37:07 +00:00

ui: Re-implement websockets with ports+JS

This commit is contained in:
James Hillyerd
2018-11-13 21:26:37 -08:00
parent ac3a94412d
commit ecd0c124d4
11 changed files with 188 additions and 79 deletions

View File

@@ -180,54 +180,68 @@ updatePage msg model =
setRoute : Route -> Model -> ( Model, Cmd Msg, Session.Msg )
setRoute route model =
case route of
Route.Unknown hash ->
( model, Cmd.none, Session.SetFlash ("Unknown route requested: " ++ hash) )
let
( newModel, newCmd, newSession ) =
case route of
Route.Unknown hash ->
( model, Cmd.none, Session.SetFlash ("Unknown route requested: " ++ hash) )
Route.Home ->
let
( subModel, subCmd ) =
Home.init
in
( { model | page = Home subModel }
, Cmd.map HomeMsg subCmd
, Session.none
)
Route.Home ->
let
( subModel, subCmd ) =
Home.init
in
( { model | page = Home subModel }
, Cmd.map HomeMsg subCmd
, Session.none
)
Route.Mailbox name ->
let
( subModel, subCmd ) =
Mailbox.init name Nothing
in
( { model | page = Mailbox subModel }
, Cmd.map MailboxMsg subCmd
, Session.none
)
Route.Mailbox name ->
let
( subModel, subCmd ) =
Mailbox.init name Nothing
in
( { model | page = Mailbox subModel }
, Cmd.map MailboxMsg subCmd
, Session.none
)
Route.Message mailbox id ->
let
( subModel, subCmd ) =
Mailbox.init mailbox (Just id)
in
( { model | page = Mailbox subModel }
, Cmd.map MailboxMsg subCmd
, Session.none
)
Route.Message mailbox id ->
let
( subModel, subCmd ) =
Mailbox.init mailbox (Just id)
in
( { model | page = Mailbox subModel }
, Cmd.map MailboxMsg subCmd
, Session.none
)
Route.Monitor ->
( { model | page = Monitor (Monitor.init model.session.host) }
, Ports.windowTitle "Inbucket Monitor"
, Session.none
)
Route.Monitor ->
let
( subModel, subCmd ) =
Monitor.init
in
( { model | page = Monitor subModel }
, Cmd.map MonitorMsg subCmd
, Session.none
)
Route.Status ->
( { model | page = Status Status.init }
, Cmd.batch
[ Ports.windowTitle "Inbucket Status"
, Cmd.map StatusMsg Status.load
]
, Session.none
)
Route.Status ->
( { model | page = Status Status.init }
, Cmd.batch
[ Ports.windowTitle "Inbucket Status"
, Cmd.map StatusMsg Status.load
]
, Session.none
)
in
case model.page of
Monitor _ ->
-- Leaving Monitor page, shut down the web socket.
( newModel, Cmd.batch [ Ports.monitorCommand False, newCmd ], newSession )
_ ->
( newModel, newCmd, newSession )
applySession : ( Model, Cmd Msg, Session.Msg ) -> ( Model, Cmd Msg )

View File

@@ -15,25 +15,28 @@ import DateFormat
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events as Events
import Json.Decode exposing (decodeString)
import Json.Decode as D
import Ports
import Route
import WebSocket
-- MODEL
type alias Model =
{ wsUrl : String
{ connected : Bool
, messages : List MessageHeader
}
init : String -> Model
init host =
{ wsUrl = "ws://" ++ host ++ "/api/v1/monitor/messages"
, messages = []
}
init : ( Model, Cmd Msg )
init =
( Model False []
, Cmd.batch
[ Ports.windowTitle "Inbucket Monitor"
, Ports.monitorCommand True
]
)
@@ -42,7 +45,16 @@ init host =
subscriptions : Model -> Sub Msg
subscriptions model =
WebSocket.listen model.wsUrl (decodeString MessageHeader.decoder >> NewMessage)
let
monitorMessage =
D.oneOf
[ D.map Message MessageHeader.decoder
, D.map Connected D.bool
]
|> D.decodeValue
|> Ports.monitorMessage
in
Sub.map MonitorResult monitorMessage
@@ -50,17 +62,25 @@ subscriptions model =
type Msg
= NewMessage (Result String MessageHeader)
= MonitorResult (Result String MonitorMessage)
| OpenMessage MessageHeader
type MonitorMessage
= Connected Bool
| Message MessageHeader
update : Session -> Msg -> Model -> ( Model, Cmd Msg, Session.Msg )
update session msg model =
case msg of
NewMessage (Ok msg) ->
MonitorResult (Ok (Connected status)) ->
( { model | connected = status }, Cmd.none, Session.none )
MonitorResult (Ok (Message msg)) ->
( { model | messages = msg :: model.messages }, Cmd.none, Session.none )
NewMessage (Err err) ->
MonitorResult (Err err) ->
( model, Cmd.none, Session.SetFlash err )
OpenMessage msg ->
@@ -78,7 +98,17 @@ view : Session -> Model -> Html Msg
view session model =
div [ id "page" ]
[ h1 [] [ text "Monitor" ]
, p [] [ text "Messages will be listed here shortly after delivery." ]
, p []
[ text "Messages will be listed here shortly after delivery. "
, em []
[ text
(if model.connected then
"Connected."
else
"Disconnected!"
)
]
]
, table [ id "monitor" ]
[ thead []
[ th [] [ text "Date" ]

View File

@@ -1,9 +1,22 @@
port module Ports exposing (onSessionChange, storeSession, windowTitle)
port module Ports
exposing
( monitorCommand
, monitorMessage
, onSessionChange
, storeSession
, windowTitle
)
import Data.Session exposing (Persistent)
import Json.Encode exposing (Value)
port monitorCommand : Bool -> Cmd msg
port monitorMessage : (Value -> msg) -> Sub msg
port onSessionChange : (Value -> msg) -> Sub msg

View File

@@ -1,33 +1,40 @@
import './main.css';
import { Main } from './Main.elm';
import registerServiceWorker from './registerServiceWorker';
import './main.css'
import { Main } from './Main.elm'
import registerServiceWorker from './registerServiceWorker'
import registerMonitorPorts from './registerMonitor'
var app = Main.embed(document.getElementById('root'), sessionObject());
// App startup.
var app = Main.embed(document.getElementById('root'), sessionObject())
// Message monitor.
registerMonitorPorts(app)
// Session storage.
app.ports.storeSession.subscribe(function (session) {
localStorage.session = JSON.stringify(session);
});
app.ports.windowTitle.subscribe(function (title) {
document.title = title;
});
localStorage.session = JSON.stringify(session)
})
window.addEventListener("storage", function (event) {
if (event.storageArea === localStorage && event.key === "session") {
app.ports.onSessionChange.send(sessionObject());
app.ports.onSessionChange.send(sessionObject())
}
}, false);
}, false)
function sessionObject() {
var s = localStorage.session;
var s = localStorage.session
try {
if (s) {
return JSON.parse(s);
return JSON.parse(s)
}
} catch (error) {
console.error(error);
console.error(error)
}
return null;
return null
}
registerServiceWorker();
// Window title.
app.ports.windowTitle.subscribe(function (title) {
document.title = title
})
registerServiceWorker()

46
ui/src/registerMonitor.js Normal file
View File

@@ -0,0 +1,46 @@
// 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()
}
}
}