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:
100
ui/src/Main.elm
100
ui/src/Main.elm
@@ -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 )
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
46
ui/src/registerMonitor.js
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user