web: plumb websocket messages through to UI

This commit is contained in:
Bill Thiede 2025-04-15 10:41:51 -07:00
parent f2042f284e
commit 30f3f14040
3 changed files with 35 additions and 28 deletions

View File

@ -2,6 +2,8 @@
// - it's useful when you want to check your code with `cargo make verify` // - it's useful when you want to check your code with `cargo make verify`
// but some rules are too "annoying" or are not applicable for your case.) // but some rules are too "annoying" or are not applicable for your case.)
#![allow(clippy::wildcard_imports)] #![allow(clippy::wildcard_imports)]
// Until https://github.com/rust-lang/rust/issues/138762 is addressed in dependencies
#![allow(wasm_c_abi)]
use log::Level; use log::Level;
use seed::App; use seed::App;

View File

@ -1,6 +1,7 @@
use std::collections::HashSet; use std::collections::HashSet;
use graphql_client::GraphQLQuery; use graphql_client::GraphQLQuery;
use letterbox_shared::WebsocketMessage;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use thiserror::Error; use thiserror::Error;
@ -61,7 +62,7 @@ pub fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
}, },
catchup: None, catchup: None,
last_url: Url::current(), last_url: Url::current(),
websocket: websocket::init(url, &mut orders.proxy(Msg::WebSocket)), websocket: websocket::init(&mut orders.proxy(Msg::WebSocket)),
} }
} }
@ -663,7 +664,15 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
} }
Msg::WebSocket(ws) => { Msg::WebSocket(ws) => {
websocket::update(ws, &mut model.websocket, &mut orders.proxy(Msg::WebSocket)) websocket::update(ws, &mut model.websocket, &mut orders.proxy(Msg::WebSocket));
while let Some(msg) = model.websocket.updates.pop_front() {
orders.send_msg(Msg::WebsocketMessage(msg));
}
}
Msg::WebsocketMessage(msg) => {
match msg {
WebsocketMessage::RefreshMessages => orders.send_msg(Msg::Refresh),
};
} }
} }
} }
@ -831,4 +840,5 @@ pub enum Msg {
CatchupExit, CatchupExit,
WebSocket(websocket::Msg), WebSocket(websocket::Msg),
WebsocketMessage(WebsocketMessage),
} }

View File

@ -1,8 +1,8 @@
use std::rc::Rc; use std::{collections::VecDeque, rc::Rc};
use letterbox_shared::WebsocketMessage; use letterbox_shared::WebsocketMessage;
use log::{error, info}; use log::{error, info};
use seed::{prelude::*, *}; use seed::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasm_sockets::{self, ConnectionStatus, EventClient, Message, WebSocketError}; use wasm_sockets::{self, ConnectionStatus, EventClient, Message, WebSocketError};
use web_sys::CloseEvent; use web_sys::CloseEvent;
@ -31,16 +31,18 @@ const WS_URL: &str = "wss://6758.z.xinu.tv/api/ws";
pub struct Model { pub struct Model {
web_socket: EventClient, web_socket: EventClient,
web_socket_reconnector: Option<StreamHandle>, web_socket_reconnector: Option<StreamHandle>,
pub updates: VecDeque<WebsocketMessage>,
} }
// ------ ------ // ------ ------
// Init // Init
// ------ ------ // ------ ------
pub fn init(_: Url, orders: &mut impl Orders<Msg>) -> Model { pub fn init(orders: &mut impl Orders<Msg>) -> Model {
Model { Model {
web_socket: create_websocket(orders).unwrap(), web_socket: create_websocket(orders).unwrap(),
web_socket_reconnector: None, web_socket_reconnector: None,
updates: VecDeque::new(),
} }
} }
@ -50,39 +52,35 @@ pub fn init(_: Url, orders: &mut impl Orders<Msg>) -> Model {
pub enum Msg { pub enum Msg {
WebSocketOpened, WebSocketOpened,
TextMessageReceived(String), TextMessageReceived(WebsocketMessage),
BinaryMessageReceived(ServerMessage),
CloseWebSocket,
WebSocketClosed(CloseEvent), WebSocketClosed(CloseEvent),
WebSocketFailed, WebSocketFailed,
ReconnectWebSocket(usize), ReconnectWebSocket(usize),
#[allow(dead_code)]
SendMessage(ClientMessage), SendMessage(ClientMessage),
SendBinaryMessage(ClientMessage),
} }
pub fn update(msg: Msg, mut model: &mut Model, orders: &mut impl Orders<Msg>) { pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
match msg { match msg {
Msg::WebSocketOpened => { Msg::WebSocketOpened => {
model.web_socket_reconnector = None; model.web_socket_reconnector = None;
info!("WebSocket connection is open now"); info!("WebSocket connection is open now");
} }
Msg::TextMessageReceived(msg) => { Msg::TextMessageReceived(msg) => {
info!("recieved text {}", msg); model.updates.push_back(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) => { Msg::WebSocketClosed(close_event) => {
info!("=================="); info!(
info!("WebSocket connection was closed:"); r#"==================
info!("Clean: {}", close_event.was_clean()); WebSocket connection was closed:
info!("Code: {}", close_event.code()); Clean: {0}
info!("Reason: {}", close_event.reason()); Code: {1}
info!("=================="); Reason: {2}
=================="#,
close_event.was_clean(),
close_event.code(),
close_event.reason()
);
// Chrome doesn't invoke `on_error` when the connection is lost. // Chrome doesn't invoke `on_error` when the connection is lost.
if !close_event.was_clean() && model.web_socket_reconnector.is_none() { if !close_event.was_clean() && model.web_socket_reconnector.is_none() {
@ -107,9 +105,6 @@ pub fn update(msg: Msg, mut model: &mut Model, orders: &mut impl Orders<Msg>) {
let txt = serde_json::to_string(&msg).unwrap(); let txt = serde_json::to_string(&msg).unwrap();
model.web_socket.send_string(&txt).unwrap(); model.web_socket.send_string(&txt).unwrap();
} }
Msg::SendBinaryMessage(_msg) => {
error!("Attempt to send binary message, unsupported");
}
} }
} }
@ -160,7 +155,7 @@ fn decode_message(message: Message, msg_sender: Rc<dyn Fn(Option<Msg>)>) {
let msg: WebsocketMessage = serde_json::from_str(&txt).unwrap_or_else(|e| { let msg: WebsocketMessage = serde_json::from_str(&txt).unwrap_or_else(|e| {
panic!("failed to parse json into WebsocketMessage: {e}\n'{txt}'") panic!("failed to parse json into WebsocketMessage: {e}\n'{txt}'")
}); });
msg_sender(Some(Msg::TextMessageReceived(txt))); msg_sender(Some(Msg::TextMessageReceived(msg)));
} }
m => error!("unexpected message type received of {m:?}"), m => error!("unexpected message type received of {m:?}"),
} }