WIP: big button for reconnection + automatic disconnection.
parent
418495e274
commit
db8cd669ae
|
@ -45,7 +45,7 @@
|
||||||
-- | - mail recovery, password change
|
-- | - mail recovery, password change
|
||||||
module App.Container where
|
module App.Container where
|
||||||
|
|
||||||
import Prelude (Unit, bind, discard, unit, ($), (=<<), (<>), show, pure)
|
import Prelude (Unit, bind, discard, unit, ($), (=<<), (<>), show, pure, (+), (&&))
|
||||||
|
|
||||||
import Bulma as Bulma
|
import Bulma as Bulma
|
||||||
|
|
||||||
|
@ -170,6 +170,11 @@ data Action
|
||||||
-- | Close the main notification, at the top of the page.
|
-- | Close the main notification, at the top of the page.
|
||||||
| CloseNotif
|
| CloseNotif
|
||||||
|
|
||||||
|
-- | In order to keep the websocket overhead to a minimum, unused connections are
|
||||||
|
-- | closed automatically by the client. In practice, this is handled by a simple counter
|
||||||
|
-- | incremented each time a KeepAlive message is sent.
|
||||||
|
| ResetKeepAliveCounter
|
||||||
|
|
||||||
data Notification = NoNotification | GoodNotification String | BadNotification String
|
data Notification = NoNotification | GoodNotification String | BadNotification String
|
||||||
|
|
||||||
-- | The component's state is composed of:
|
-- | The component's state is composed of:
|
||||||
|
@ -184,6 +189,9 @@ type State = { token :: Maybe String
|
||||||
, store_AuthenticationDaemonAdmin_state :: Maybe AdminInterface.State
|
, store_AuthenticationDaemonAdmin_state :: Maybe AdminInterface.State
|
||||||
, notif :: Notification
|
, notif :: Notification
|
||||||
, login :: Maybe String
|
, login :: Maybe String
|
||||||
|
, keepalive_counter :: Int
|
||||||
|
, are_we_connected_to_authd :: Boolean
|
||||||
|
, are_we_connected_to_dnsmanagerd :: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | The list of child components: log, `WS` twice (once for each ws connection),
|
-- | The list of child components: log, `WS` twice (once for each ws connection),
|
||||||
|
@ -236,6 +244,9 @@ initialState _ = { token: Nothing
|
||||||
, store_AuthenticationDaemonAdmin_state: Nothing
|
, store_AuthenticationDaemonAdmin_state: Nothing
|
||||||
, notif: NoNotification
|
, notif: NoNotification
|
||||||
, login: Nothing
|
, login: Nothing
|
||||||
|
, keepalive_counter: 0
|
||||||
|
, are_we_connected_to_authd: false
|
||||||
|
, are_we_connected_to_dnsmanagerd: false
|
||||||
}
|
}
|
||||||
|
|
||||||
render :: forall m. MonadAff m => State -> H.ComponentHTML Action ChildSlots m
|
render :: forall m. MonadAff m => State -> H.ComponentHTML Action ChildSlots m
|
||||||
|
@ -243,6 +254,7 @@ render state
|
||||||
= HH.div_ $
|
= HH.div_ $
|
||||||
[ render_header
|
[ render_header
|
||||||
, render_nav
|
, render_nav
|
||||||
|
, reconnection_bar
|
||||||
, render_notifications
|
, render_notifications
|
||||||
, case state.current_page of
|
, case state.current_page of
|
||||||
Home -> render_home
|
Home -> render_home
|
||||||
|
@ -256,9 +268,20 @@ render state
|
||||||
-- The footer includes logs and both the WS child components.
|
-- The footer includes logs and both the WS child components.
|
||||||
, Bulma.hr
|
, Bulma.hr
|
||||||
, Bulma.columns_ [ Bulma.column_ [ Bulma.h3 "Logs (watch this if something fails 😅)", render_logs ]
|
, Bulma.columns_ [ Bulma.column_ [ Bulma.h3 "Logs (watch this if something fails 😅)", render_logs ]
|
||||||
, Bulma.column_ [ render_auth_WS, render_dnsmanager_WS ] ]
|
, Bulma.column_ [ Bulma.level [ render_auth_WS, render_dnsmanager_WS ] [] ] ]
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
|
reconnection_bar :: forall w i. HH.HTML w i
|
||||||
|
reconnection_bar =
|
||||||
|
if (state.are_we_connected_to_authd && state.are_we_connected_to_dnsmanagerd)
|
||||||
|
then Bulma.p "yay, you're connected"
|
||||||
|
else Bulma.p "OH NOES :("
|
||||||
|
render_auth_WS :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
|
render_auth_WS = HH.slot _ws_auth unit WS.component (Tuple "ws://127.0.0.1:8080" "authd") AuthenticationDaemonEvent
|
||||||
|
|
||||||
|
render_dnsmanager_WS :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
|
render_dnsmanager_WS = HH.slot _ws_dns unit WS.component (Tuple "ws://127.0.0.1:8081" "dnsmanagerd") DNSManagerDaemonEvent
|
||||||
|
|
||||||
render_notifications =
|
render_notifications =
|
||||||
case state.notif of
|
case state.notif of
|
||||||
NoNotification -> HH.div_ []
|
NoNotification -> HH.div_ []
|
||||||
|
@ -303,12 +326,6 @@ render state
|
||||||
render_logs :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
render_logs :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
||||||
render_logs = Bulma.container [ HH.slot_ _log unit AppLog.component unit ]
|
render_logs = Bulma.container [ HH.slot_ _log unit AppLog.component unit ]
|
||||||
|
|
||||||
render_auth_WS :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
|
||||||
render_auth_WS = HH.slot _ws_auth unit WS.component "ws://127.0.0.1:8080" AuthenticationDaemonEvent
|
|
||||||
|
|
||||||
render_dnsmanager_WS :: forall monad. MonadAff monad => H.ComponentHTML Action ChildSlots monad
|
|
||||||
render_dnsmanager_WS = HH.slot _ws_dns unit WS.component "ws://127.0.0.1:8081" DNSManagerDaemonEvent
|
|
||||||
|
|
||||||
handleAction :: forall o monad. MonadAff monad => Action -> H.HalogenM State Action ChildSlots o monad Unit
|
handleAction :: forall o monad. MonadAff monad => Action -> H.HalogenM State Action ChildSlots o monad Unit
|
||||||
handleAction = case _ of
|
handleAction = case _ of
|
||||||
Initialize -> do
|
Initialize -> do
|
||||||
|
@ -349,13 +366,16 @@ handleAction = case _ of
|
||||||
Nothing -> H.tell _nav unit $ NavigationInterface.ToggleLogged false
|
Nothing -> H.tell _nav unit $ NavigationInterface.ToggleLogged false
|
||||||
Just _ -> H.tell _nav unit $ NavigationInterface.ToggleLogged true
|
Just _ -> H.tell _nav unit $ NavigationInterface.ToggleLogged true
|
||||||
|
|
||||||
|
ResetKeepAliveCounter -> H.modify_ _ { keepalive_counter = 0 }
|
||||||
KeepAlive auth_or_dnsmanager -> case auth_or_dnsmanager of
|
KeepAlive auth_or_dnsmanager -> case auth_or_dnsmanager of
|
||||||
Left _ -> do
|
Left _ -> do
|
||||||
message <- H.liftEffect $ AuthD.serialize $ AuthD.MkKeepAlive {}
|
message <- H.liftEffect $ AuthD.serialize $ AuthD.MkKeepAlive {}
|
||||||
H.tell _ws_auth unit (WS.ToSend message)
|
H.tell _ws_auth unit (WS.ToSendKeepAlive message)
|
||||||
|
H.modify_ \state -> state { keepalive_counter = state.keepalive_counter + 1 }
|
||||||
|
|
||||||
Right _ -> do
|
Right _ -> do
|
||||||
message <- H.liftEffect $ DNSManager.serialize $ DNSManager.MkKeepAlive {}
|
message <- H.liftEffect $ DNSManager.serialize $ DNSManager.MkKeepAlive {}
|
||||||
H.tell _ws_dns unit (WS.ToSend message)
|
H.tell _ws_dns unit (WS.ToSendKeepAlive message)
|
||||||
|
|
||||||
AuthenticateToAuthd v -> case v of
|
AuthenticateToAuthd v -> case v of
|
||||||
Left token -> do
|
Left token -> do
|
||||||
|
@ -490,6 +510,7 @@ handleAction = case _ of
|
||||||
WS.MessageReceived (Tuple _ message) -> handleAction $ DecodeAuthMessage message
|
WS.MessageReceived (Tuple _ message) -> handleAction $ DecodeAuthMessage message
|
||||||
|
|
||||||
WS.WSJustConnected -> do
|
WS.WSJustConnected -> do
|
||||||
|
H.modify_ _ { are_we_connected_to_authd = true }
|
||||||
sessionstorage <- H.liftEffect $ Window.sessionStorage =<< HTML.window
|
sessionstorage <- H.liftEffect $ Window.sessionStorage =<< HTML.window
|
||||||
token <- H.liftEffect $ Storage.getItem "user-authd-token" sessionstorage
|
token <- H.liftEffect $ Storage.getItem "user-authd-token" sessionstorage
|
||||||
case token of
|
case token of
|
||||||
|
@ -498,10 +519,14 @@ handleAction = case _ of
|
||||||
handleAction $ Log $ SystemLog "Let's authenticate to authd"
|
handleAction $ Log $ SystemLog "Let's authenticate to authd"
|
||||||
handleAction $ AuthenticateToAuthd (Left t)
|
handleAction $ AuthenticateToAuthd (Left t)
|
||||||
|
|
||||||
WS.WSJustClosed -> handleAction $ Log $ ErrorLog "You just got disconnected from authd."
|
WS.WSJustClosed -> do
|
||||||
|
H.modify_ _ { are_we_connected_to_dnsmanagerd = false }
|
||||||
|
handleAction $ Log $ ErrorLog "You just got disconnected from authd."
|
||||||
WS.Log message -> handleAction $ Log message
|
WS.Log message -> handleAction $ Log message
|
||||||
WS.KeepAlive -> handleAction $ KeepAlive $ Left unit
|
WS.KeepAlive -> handleAction $ KeepAlive $ Left unit
|
||||||
|
|
||||||
|
WS.ResetKeepAliveCounter -> handleAction ResetKeepAliveCounter
|
||||||
|
|
||||||
DecodeAuthMessage message -> do
|
DecodeAuthMessage message -> do
|
||||||
receivedMessage <- H.liftEffect $ AuthD.deserialize message
|
receivedMessage <- H.liftEffect $ AuthD.deserialize message
|
||||||
case receivedMessage of
|
case receivedMessage of
|
||||||
|
@ -671,11 +696,17 @@ handleAction = case _ of
|
||||||
-- | `dnsmanagerd websocket component` wants to do something.
|
-- | `dnsmanagerd websocket component` wants to do something.
|
||||||
DNSManagerDaemonEvent ev -> case ev of
|
DNSManagerDaemonEvent ev -> case ev of
|
||||||
WS.MessageReceived (Tuple _ message) -> handleAction $ DecodeDNSMessage message
|
WS.MessageReceived (Tuple _ message) -> handleAction $ DecodeDNSMessage message
|
||||||
WS.WSJustConnected -> handleAction AuthenticateToDNSManager
|
WS.WSJustConnected -> do
|
||||||
WS.WSJustClosed -> handleAction $ Log $ ErrorLog "You just got disconnected from dnsmanagerd."
|
H.modify_ _ { are_we_connected_to_dnsmanagerd = true }
|
||||||
|
handleAction AuthenticateToDNSManager
|
||||||
|
WS.WSJustClosed -> do
|
||||||
|
H.modify_ _ { are_we_connected_to_dnsmanagerd = false }
|
||||||
|
handleAction $ Log $ ErrorLog "You just got disconnected from dnsmanagerd."
|
||||||
WS.Log message -> handleAction $ Log message
|
WS.Log message -> handleAction $ Log message
|
||||||
WS.KeepAlive -> handleAction $ KeepAlive $ Right unit
|
WS.KeepAlive -> handleAction $ KeepAlive $ Right unit
|
||||||
|
|
||||||
|
WS.ResetKeepAliveCounter -> handleAction ResetKeepAliveCounter
|
||||||
|
|
||||||
-- | `DecodeDNSMessage`: decode a received `dnsmanagerd` message, then transfer it to `DispatchDNSMessage`.
|
-- | `DecodeDNSMessage`: decode a received `dnsmanagerd` message, then transfer it to `DispatchDNSMessage`.
|
||||||
DecodeDNSMessage message -> do
|
DecodeDNSMessage message -> do
|
||||||
receivedMessage <- H.liftEffect $ DNSManager.deserialize message
|
receivedMessage <- H.liftEffect $ DNSManager.deserialize message
|
||||||
|
|
|
@ -34,23 +34,36 @@ import Web.Socket.WebSocket as WS
|
||||||
|
|
||||||
import App.Type.LogMessage
|
import App.Type.LogMessage
|
||||||
|
|
||||||
keepalive = 30000.0 :: Number
|
import CSSClasses as C
|
||||||
|
|
||||||
|
keepalive = 5000.0 :: Number
|
||||||
|
|
||||||
-- Input is the WS url.
|
-- Input is the WS url.
|
||||||
type Input = String
|
type Input = (Tuple String String)
|
||||||
|
|
||||||
-- | The component can perform 4 actions: log messages, notify that a message has been received,
|
-- | The component can perform 4 actions: log messages, notify that a message has been received,
|
||||||
-- | notify when a connection has been established or when it has been closed.
|
-- | notify when a connection has been established or when it has been closed.
|
||||||
data Output
|
data Output
|
||||||
-- | MessageReceived (Tuple URL message)
|
-- | MessageReceived (Tuple URL message)
|
||||||
= MessageReceived (Tuple String ArrayBuffer) -- Provide a received message to the parent.
|
= MessageReceived (Tuple String ArrayBuffer) -- Provide a received message to the parent.
|
||||||
| WSJustConnected -- Inform the parent the connection is up.
|
|
||||||
| WSJustClosed -- Inform the parent the connection is down.
|
-- | Inform the parent the connection is up.
|
||||||
|
| WSJustConnected
|
||||||
|
|
||||||
|
-- | Inform the parent the connection is down.
|
||||||
|
| WSJustClosed
|
||||||
|
|
||||||
|
-- | Log a message.
|
||||||
| Log LogMessage
|
| Log LogMessage
|
||||||
| KeepAlive -- Ask the parent to handle a keep-alive message.
|
|
||||||
|
-- | Ask the parent to send a keep-alive message.
|
||||||
|
| KeepAlive
|
||||||
|
|
||||||
|
-- | Ask the parent to reset the keep-alive counter used to automatically close the WS connection.
|
||||||
|
| ResetKeepAliveCounter
|
||||||
|
|
||||||
-- | The component can receive a single action from other components: sending a message throught the websocket.
|
-- | The component can receive a single action from other components: sending a message throught the websocket.
|
||||||
data Query a = ToSend ArrayBuffer a
|
data Query a = ToSend ArrayBuffer a | ToSendKeepAlive ArrayBuffer a | Connect a
|
||||||
|
|
||||||
type Slot = H.Slot Query Output
|
type Slot = H.Slot Query Output
|
||||||
|
|
||||||
|
@ -91,6 +104,7 @@ data Action
|
||||||
-- | to inform if the connection has to be re-established.
|
-- | to inform if the connection has to be re-established.
|
||||||
type WSInfo
|
type WSInfo
|
||||||
= { url :: String
|
= { url :: String
|
||||||
|
, service_name :: String
|
||||||
, connection :: Maybe WS.WebSocket
|
, connection :: Maybe WS.WebSocket
|
||||||
, reconnect :: Boolean
|
, reconnect :: Boolean
|
||||||
}
|
}
|
||||||
|
@ -112,8 +126,9 @@ component =
|
||||||
}
|
}
|
||||||
|
|
||||||
initialState :: Input -> State
|
initialState :: Input -> State
|
||||||
initialState url =
|
initialState (Tuple url service_name) =
|
||||||
{ wsInfo: { url: url
|
{ wsInfo: { url: url
|
||||||
|
, service_name: service_name
|
||||||
, connection: Nothing
|
, connection: Nothing
|
||||||
, reconnect: false
|
, reconnect: false
|
||||||
}
|
}
|
||||||
|
@ -129,24 +144,13 @@ render { wsInfo }
|
||||||
HH.div [ HP.style "margin-bottom: 0.125rem; color: grey;" ] [ HH.small_ [ HH.text txt ] ]
|
HH.div [ HP.style "margin-bottom: 0.125rem; color: grey;" ] [ HH.small_ [ HH.text txt ] ]
|
||||||
|
|
||||||
renderReconnectButton :: Boolean -> H.ComponentHTML Action () m
|
renderReconnectButton :: Boolean -> H.ComponentHTML Action () m
|
||||||
renderReconnectButton cond =
|
renderReconnectButton cond = HH.p_ [
|
||||||
if cond
|
if cond then reconnection_btn [ HH.text $ "Reconnect to \"" <> wsInfo.service_name <> "\"" ]
|
||||||
then
|
else renderFootnote $ "Connected to \"" <> wsInfo.service_name <> "\""
|
||||||
HH.p_
|
]
|
||||||
[ HH.button
|
reconnection_btn = HH.button [ HP.type_ HP.ButtonButton
|
||||||
[ HP.type_ HP.ButtonButton
|
|
||||||
, HE.onClick \_ -> ConnectWebSocket
|
, HE.onClick \_ -> ConnectWebSocket
|
||||||
]
|
, HP.classes (C.has_background_danger_light <> C.is_large)
|
||||||
[ HH.text "Reconnect?" ]
|
|
||||||
]
|
|
||||||
else
|
|
||||||
HH.p_
|
|
||||||
[ renderFootnote $
|
|
||||||
"NOTE: A 'Reconnect?' button will appear if the connection drops (for URL: '"
|
|
||||||
<>
|
|
||||||
wsInfo.url
|
|
||||||
<>
|
|
||||||
"')"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
handleAction :: forall m. MonadAff m => Action -> H.HalogenM State Action () Output m Unit
|
handleAction :: forall m. MonadAff m => Action -> H.HalogenM State Action () Output m Unit
|
||||||
|
@ -220,10 +224,23 @@ handleAction action = do
|
||||||
handleQuery :: forall a m. MonadAff m => Query a -> H.HalogenM State Action () Output m (Maybe a)
|
handleQuery :: forall a m. MonadAff m => Query a -> H.HalogenM State Action () Output m (Maybe a)
|
||||||
handleQuery = case _ of
|
handleQuery = case _ of
|
||||||
ToSend message a -> do
|
ToSend message a -> do
|
||||||
|
H.raise ResetKeepAliveCounter
|
||||||
send_message message
|
send_message message
|
||||||
pure (Just a)
|
pure (Just a)
|
||||||
|
-- Sending a keepalive: do not tell the parent to reset the keepalive counter.
|
||||||
|
ToSendKeepAlive message a -> do
|
||||||
|
send_message message
|
||||||
|
pure (Just a)
|
||||||
|
Connect a -> do
|
||||||
|
state <- H.get
|
||||||
|
case state.wsInfo.connection of
|
||||||
|
Nothing -> do
|
||||||
|
handleAction ConnectWebSocket
|
||||||
|
pure (Just a)
|
||||||
|
Just _ -> pure Nothing
|
||||||
|
|
||||||
send_message :: forall m. MonadAff m => ArrayBuffer -> H.HalogenM State Action () Output m Unit
|
send_message :: forall m. MonadAff m =>
|
||||||
|
ArrayBuffer -> H.HalogenM State Action () Output m Unit
|
||||||
send_message message = do
|
send_message message = do
|
||||||
{ wsInfo } <- H.get
|
{ wsInfo } <- H.get
|
||||||
case wsInfo.connection of
|
case wsInfo.connection of
|
||||||
|
|
Loading…
Reference in New Issue