Add websocket handler on server, connect from client
Additionally add /test handler that triggers server->client WS message
This commit is contained in:
@@ -43,7 +43,7 @@ pub fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
};
|
||||
// TODO(wathiede): only do this while viewing the index? Or maybe add a new message that force
|
||||
// 'notmuch new' on the server periodically?
|
||||
orders.stream(streams::interval(30_000, || Msg::RefreshStart));
|
||||
//orders.stream(streams::interval(30_000, || Msg::RefreshStart));
|
||||
orders.subscribe(Msg::OnUrlChanged);
|
||||
orders.stream(streams::window_event(Ev::Scroll, |_| Msg::WindowScrolled));
|
||||
|
||||
|
||||
167
web/src/websocket.rs
Normal file
167
web/src/websocket.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use letterbox_shared::WebsocketMessage;
|
||||
use log::{error, info};
|
||||
use seed::{prelude::*, *};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_sockets::{self, ConnectionStatus, EventClient, Message, WebSocketError};
|
||||
use web_sys::CloseEvent;
|
||||
|
||||
/// Message from the server to the client.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ServerMessage {
|
||||
pub id: usize,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
/// Message from the client to the server.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ClientMessage {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
//const WS_URL: &str = "wss://9000.z.xinu.tv/api/ws";
|
||||
//const WS_URL: &str = "wss://9345.z.xinu.tv/api/graphql/ws";
|
||||
const WS_URL: &str = "wss://6758.z.xinu.tv/api/ws";
|
||||
|
||||
// ------ ------
|
||||
// Model
|
||||
// ------ ------
|
||||
|
||||
pub struct Model {
|
||||
web_socket: EventClient,
|
||||
web_socket_reconnector: Option<StreamHandle>,
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
// Init
|
||||
// ------ ------
|
||||
|
||||
pub fn init(_: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
Model {
|
||||
web_socket: create_websocket(orders).unwrap(),
|
||||
web_socket_reconnector: None,
|
||||
}
|
||||
}
|
||||
|
||||
// ------ ------
|
||||
// Update
|
||||
// ------ ------
|
||||
|
||||
pub enum Msg {
|
||||
WebSocketOpened,
|
||||
TextMessageReceived(String),
|
||||
BinaryMessageReceived(ServerMessage),
|
||||
CloseWebSocket,
|
||||
WebSocketClosed(CloseEvent),
|
||||
WebSocketFailed,
|
||||
ReconnectWebSocket(usize),
|
||||
SendMessage(ClientMessage),
|
||||
SendBinaryMessage(ClientMessage),
|
||||
}
|
||||
|
||||
pub fn update(msg: Msg, mut model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
Msg::WebSocketOpened => {
|
||||
model.web_socket_reconnector = None;
|
||||
info!("WebSocket connection is open now");
|
||||
}
|
||||
Msg::TextMessageReceived(msg) => {
|
||||
info!("recieved text {}", msg);
|
||||
}
|
||||
Msg::BinaryMessageReceived(message) => {
|
||||
error!("Client received binary message");
|
||||
}
|
||||
Msg::CloseWebSocket => {
|
||||
model.web_socket_reconnector = None;
|
||||
model.web_socket.close().unwrap();
|
||||
}
|
||||
Msg::WebSocketClosed(close_event) => {
|
||||
info!("==================");
|
||||
info!("WebSocket connection was closed:");
|
||||
info!("Clean: {}", close_event.was_clean());
|
||||
info!("Code: {}", close_event.code());
|
||||
info!("Reason: {}", close_event.reason());
|
||||
info!("==================");
|
||||
|
||||
// Chrome doesn't invoke `on_error` when the connection is lost.
|
||||
if !close_event.was_clean() && model.web_socket_reconnector.is_none() {
|
||||
model.web_socket_reconnector = Some(
|
||||
orders.stream_with_handle(streams::backoff(None, Msg::ReconnectWebSocket)),
|
||||
);
|
||||
}
|
||||
}
|
||||
Msg::WebSocketFailed => {
|
||||
info!("WebSocket failed");
|
||||
if model.web_socket_reconnector.is_none() {
|
||||
model.web_socket_reconnector = Some(
|
||||
orders.stream_with_handle(streams::backoff(None, Msg::ReconnectWebSocket)),
|
||||
);
|
||||
}
|
||||
}
|
||||
Msg::ReconnectWebSocket(retries) => {
|
||||
info!("Reconnect attempt: {}", retries);
|
||||
model.web_socket = create_websocket(orders).unwrap();
|
||||
}
|
||||
Msg::SendMessage(msg) => {
|
||||
let txt = serde_json::to_string(&msg).unwrap();
|
||||
model.web_socket.send_string(&txt).unwrap();
|
||||
}
|
||||
Msg::SendBinaryMessage(_msg) => {
|
||||
error!("Attempt to send binary message, unsupported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_websocket(orders: &impl Orders<Msg>) -> Result<EventClient, WebSocketError> {
|
||||
let msg_sender = orders.msg_sender();
|
||||
|
||||
let mut client = EventClient::new(WS_URL)?;
|
||||
|
||||
client.set_on_error(Some(Box::new(|error| {
|
||||
gloo_console::error!("WS: ", error);
|
||||
})));
|
||||
|
||||
let send = msg_sender.clone();
|
||||
client.set_on_connection(Some(Box::new(move |client: &EventClient| {
|
||||
info!("{:#?}", client.status);
|
||||
let msg = match *client.status.borrow() {
|
||||
ConnectionStatus::Connecting => {
|
||||
info!("Connecting...");
|
||||
None
|
||||
}
|
||||
ConnectionStatus::Connected => Some(Msg::WebSocketOpened),
|
||||
ConnectionStatus::Error => Some(Msg::WebSocketFailed),
|
||||
ConnectionStatus::Disconnected => {
|
||||
info!("Disconnected");
|
||||
None
|
||||
}
|
||||
};
|
||||
send(msg);
|
||||
})));
|
||||
|
||||
let send = msg_sender.clone();
|
||||
client.set_on_close(Some(Box::new(move |ev| {
|
||||
info!("WS: Connection closed");
|
||||
send(Some(Msg::WebSocketClosed(ev)));
|
||||
})));
|
||||
|
||||
let send = msg_sender.clone();
|
||||
client.set_on_message(Some(Box::new(
|
||||
move |_: &EventClient, msg: wasm_sockets::Message| decode_message(msg, Rc::clone(&send)),
|
||||
)));
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
fn decode_message(message: Message, msg_sender: Rc<dyn Fn(Option<Msg>)>) {
|
||||
match message {
|
||||
Message::Text(txt) => {
|
||||
let msg: WebsocketMessage = serde_json::from_str(&txt).unwrap_or_else(|e| {
|
||||
panic!("failed to parse json into WebsocketMessage: {e}\n'{txt}'")
|
||||
});
|
||||
msg_sender(Some(Msg::TextMessageReceived(txt)));
|
||||
}
|
||||
m => error!("unexpected message type received of {m:?}"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user