From 238043cd235bc286b791c749f18fcb0217d2c04c Mon Sep 17 00:00:00 2001
From: Fred Clausen <43556888+fredclausen@users.noreply.github.com>
Date: Sun, 2 Jun 2024 01:41:20 -0600
Subject: [PATCH 01/40] wip
---
sh-frontend/src/app/webapp.rs | 52 ++++++++++++++-----
sh-frontend/src/common/mod.rs | 1 +
sh-frontend/src/common/wssprops.rs | 12 +++++
.../src/components/layout_components/live.rs | 8 +--
sh-frontend/src/components/pages/settings.rs | 8 +--
.../setting_components/sh_app_config.rs | 8 ++-
sh-frontend/src/services/websocket.rs | 2 +
7 files changed, 68 insertions(+), 23 deletions(-)
create mode 100644 sh-frontend/src/common/wssprops.rs
diff --git a/sh-frontend/src/app/webapp.rs b/sh-frontend/src/app/webapp.rs
index d5d18ab..8878665 100644
--- a/sh-frontend/src/app/webapp.rs
+++ b/sh-frontend/src/app/webapp.rs
@@ -3,24 +3,48 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
+use std::thread::Scope;
+
use crate::components::layout_components::footer::Footer;
use crate::components::layout_components::live::Live;
use crate::components::layout_components::nav::Nav;
-use crate::services::websocket::ShWebSocketComponent;
+use crate::services::websocket::{ShWebSocketComponent, Msg, WsAction};
+use sh_common::UserWssMessage;
use yew::prelude::*;
-#[function_component(App)]
-pub fn app() -> Html {
- html! {
- <>
-
-
-
-
-
-
-
-
- >
+pub struct App {}
+
+impl Component for App {
+ type Message = crate::services::websocket::Msg;
+ type Properties = ();
+
+ fn create(_ctx: &Context) -> Self {
+ Self {}
+ }
+
+ fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool {
+ log::info!("App update: {:?}", msg);
+ false
}
+
+ fn view(&self, ctx: &Context) -> Html {
+ let send_data_to_wss = ctx.link().callback(move |input: UserWssMessage| {
+ log::debug!("Got a message. Sending up the chain to the websocket!");
+ Msg::WsAction(WsAction::SendData(input))
+ });
+
+ html! {
+ <>
+
+
+
+
+
+
+
+
+ >
+ }
+ }
+
}
diff --git a/sh-frontend/src/common/mod.rs b/sh-frontend/src/common/mod.rs
index f8c1bcf..e71e3f5 100644
--- a/sh-frontend/src/common/mod.rs
+++ b/sh-frontend/src/common/mod.rs
@@ -4,3 +4,4 @@
// https://opensource.org/licenses/MIT.
pub mod panels;
+pub mod wssprops;
\ No newline at end of file
diff --git a/sh-frontend/src/common/wssprops.rs b/sh-frontend/src/common/wssprops.rs
new file mode 100644
index 0000000..2eb16bf
--- /dev/null
+++ b/sh-frontend/src/common/wssprops.rs
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 Fred Clausen
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+use sh_common::UserWssMessage;
+use yew::prelude::*;
+
+#[derive(Properties, Clone, PartialEq)]
+pub struct WssCommunicationProps {
+ pub send_message: Callback,
+}
\ No newline at end of file
diff --git a/sh-frontend/src/components/layout_components/live.rs b/sh-frontend/src/components/layout_components/live.rs
index 108a16e..e992f4c 100644
--- a/sh-frontend/src/components/layout_components/live.rs
+++ b/sh-frontend/src/components/layout_components/live.rs
@@ -9,7 +9,7 @@ use crate::components::pages::help::ShHelp;
use crate::components::pages::settings::ShSettings;
use crate::components::pages::stats::ShStatistics;
use crate::services::saved_state::WebAppState;
-use crate::{common::panels::Panels, services::temp_state::WebAppStateTemp};
+use crate::{common::panels::Panels, services::temp_state::WebAppStateTemp, common::wssprops::WssCommunicationProps};
use yew::prelude::*;
use yew_hooks::{use_event_with_window, use_visible};
use yewdux::prelude::*;
@@ -18,7 +18,7 @@ use yewdux::prelude::*;
// checking the old value vs the new one and setting the panel state if it's changed. This flags a re-render
/// Home page
#[function_component(Live)]
-pub fn live() -> Html {
+pub fn live(props: &WssCommunicationProps) -> Html {
let (_state_local, dispatch_local) = use_store::();
let (_state, dispatch) = use_store::();
log::debug!("Re-rendering live page");
@@ -45,7 +45,7 @@ pub fn live() -> Html {
match *right_panel {
Panels::Messages => html! { },
Panels::Map => html! { },
- Panels::Settings => html! { },
+ Panels::Settings => html! { },
Panels::Help => html! { },
Panels::Stats => html! { },
Panels::None => panic!("Right Panel is none!!!"),
@@ -56,7 +56,7 @@ pub fn live() -> Html {
match *left_panel {
Panels::Messages => html! { },
Panels::Map => html! { },
- Panels::Settings => html! { },
+ Panels::Settings => html! { },
Panels::Help => html! { },
Panels::Stats => html! { },
Panels::None => panic!("Left Panel is none!!!"),
diff --git a/sh-frontend/src/components/pages/settings.rs b/sh-frontend/src/components/pages/settings.rs
index c340833..5d89b8f 100644
--- a/sh-frontend/src/components/pages/settings.rs
+++ b/sh-frontend/src/components/pages/settings.rs
@@ -3,19 +3,19 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
-use crate::components::setting_components::{
+use crate::{common::wssprops::WssCommunicationProps, components::setting_components::{
sh_app_config::ShAppConfig, sh_data_sources::ShDataSourcesConfig,
sh_enabled_data_sources::ShEnabledDataSourcesConfig, sh_map::ShMapConfig,
-};
+}};
use yew::prelude::*;
/// Home page
#[function_component(ShSettings)]
-pub fn settings() -> Html {
+pub fn settings(props: &WssCommunicationProps) -> Html {
html! {
<>
-
+
diff --git a/sh-frontend/src/components/setting_components/sh_app_config.rs b/sh-frontend/src/components/setting_components/sh_app_config.rs
index 1ad760f..cc88650 100644
--- a/sh-frontend/src/components/setting_components/sh_app_config.rs
+++ b/sh-frontend/src/components/setting_components/sh_app_config.rs
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
+use crate::common::wssprops::WssCommunicationProps;
use crate::components::alerts::alert_error::AlertError;
use crate::components::input::input_field::{InputField, InputFieldType};
use crate::services::temp_state::WebAppStateTemp;
@@ -38,7 +39,7 @@ impl From for ButtonAction {
}
#[function_component(ShAppConfig)]
-pub fn sh_app_config() -> Html {
+pub fn sh_app_config(props: &WssCommunicationProps) -> Html {
let config = use_selector(|state: &WebAppStateTemp| state.config.clone());
let (state, dispatch) = use_store::();
let (temp_state, temp_dispatch) = use_store::();
@@ -58,6 +59,8 @@ pub fn sh_app_config() -> Html {
"warn".to_string(),
];
+ let local_props = props.clone();
+
let onsubmit = {
let config = config.clone();
let database_url_node = database_url_node.clone();
@@ -104,6 +107,9 @@ pub fn sh_app_config() -> Html {
UserMessageTypes::UserUpdateAppConfig,
MessageData::ShAppConfig(new_config),
);
+
+ // send a message using the props callback
+ local_props.send_message.emit(message);
}
}
diff --git a/sh-frontend/src/services/websocket.rs b/sh-frontend/src/services/websocket.rs
index 1ea2fef..bf0c849 100644
--- a/sh-frontend/src/services/websocket.rs
+++ b/sh-frontend/src/services/websocket.rs
@@ -9,6 +9,7 @@ use yewdux::Dispatch;
// https://github.com/security-union/yew-websocket/
+#[derive(Debug)]
pub enum WsAction {
Connect,
SendData(UserWssMessage),
@@ -16,6 +17,7 @@ pub enum WsAction {
Lost,
}
+#[derive(Debug)]
pub enum Msg {
WsAction(WsAction),
WsReady(Result),
From 0bc79af5a2f278051d93463a04da0da565b7d971 Mon Sep 17 00:00:00 2001
From: Fred Clausen <43556888+fredclausen@users.noreply.github.com>
Date: Sun, 2 Jun 2024 02:07:18 -0600
Subject: [PATCH 02/40] wip
---
sh-frontend/src/app/webapp.rs | 220 ++++++++++++++++--
sh-frontend/src/common/mod.rs | 2 +-
sh-frontend/src/common/wssprops.rs | 2 +-
.../src/components/layout_components/live.rs | 5 +-
sh-frontend/src/components/pages/settings.rs | 11 +-
.../setting_components/sh_app_config.rs | 2 +-
sh-frontend/src/services/mod.rs | 1 -
sh-frontend/src/services/websocket.rs | 184 ---------------
8 files changed, 221 insertions(+), 206 deletions(-)
delete mode 100644 sh-frontend/src/services/websocket.rs
diff --git a/sh-frontend/src/app/webapp.rs b/sh-frontend/src/app/webapp.rs
index 8878665..f7dc2fc 100644
--- a/sh-frontend/src/app/webapp.rs
+++ b/sh-frontend/src/app/webapp.rs
@@ -3,31 +3,190 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
-use std::thread::Scope;
-
use crate::components::layout_components::footer::Footer;
use crate::components::layout_components::live::Live;
use crate::components::layout_components::nav::Nav;
-use crate::services::websocket::{ShWebSocketComponent, Msg, WsAction};
-use sh_common::UserWssMessage;
-use yew::prelude::*;
+use crate::services::temp_state::WebAppStateTemp;
+use anyhow::Error;
+use sh_common::{
+ MessageData, ServerMessageTypes, ServerWssMessage, UserMessageTypes, UserWssMessage,
+};
+use yew::{html, Component, Context, Html};
+use yew_websocket::websocket::{WebSocketService, WebSocketStatus, WebSocketTask};
+use yewdux::Dispatch;
+
+// https://github.com/security-union/yew-websocket/
+
+#[derive(Debug)]
+pub enum WsAction {
+ Connect,
+ SendData(UserWssMessage),
+ Disconnect,
+ Lost,
+}
+
+#[derive(Debug)]
+pub enum Msg {
+ WsAction(WsAction),
+ WsReady(Result),
+}
+
+impl From for Msg {
+ fn from(action: WsAction) -> Self {
+ Self::WsAction(action)
+ }
+}
-pub struct App {}
+pub struct App {
+ pub fetching: bool,
+ pub ws: Option,
+ pub dispatch: Dispatch,
+}
impl Component for App {
- type Message = crate::services::websocket::Msg;
+ type Message = Msg;
type Properties = ();
fn create(_ctx: &Context) -> Self {
- Self {}
+ let dispatch = Dispatch::::global();
+
+ Self {
+ fetching: false,
+ ws: None,
+ dispatch,
+ }
}
- fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool {
- log::info!("App update: {:?}", msg);
- false
+ fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool {
+ match msg {
+ Msg::WsAction(action) => match action {
+ WsAction::Connect => {
+ let callback = ctx.link().callback(|data| Msg::WsReady(data));
+ let notification = ctx.link().batch_callback(|status| match status {
+ WebSocketStatus::Opened => {
+ let initial_message = UserWssMessage::new(
+ UserMessageTypes::UserRequestConfig,
+ MessageData::NoData,
+ );
+ Some(WsAction::SendData(initial_message).into())
+ }
+ WebSocketStatus::Closed | WebSocketStatus::Error => {
+ Some(WsAction::Lost.into())
+ }
+ });
+ let task = WebSocketService::connect_text(
+ "ws://127.0.0.1:3000/sdre-hub",
+ callback,
+ notification,
+ )
+ .unwrap();
+ self.ws = Some(task);
+ false
+ }
+ WsAction::SendData(data) => {
+ log::debug!("Sending data: {:?}", data);
+ log::debug!("Sending data: {:?}", serde_json::to_string(&data).unwrap());
+ let serialized_data = serde_json::to_string(&data).unwrap();
+ self.ws
+ .as_mut()
+ .unwrap()
+ .send(serde_json::to_string(&serialized_data).unwrap());
+
+ if self.fetching == false {
+ self.fetching = true;
+ self.dispatch.reduce_mut(|state| {
+ state.websocket_connected = true;
+ });
+ }
+ false
+ }
+ WsAction::Disconnect => {
+ self.ws.take();
+ log::info!(
+ "WebSocket connection disconnected. Why? This should be unreachable"
+ );
+ self.fetching = false;
+ self.dispatch.reduce_mut(|state| {
+ state.config = None;
+ state.websocket_connected = false;
+ });
+
+ false
+ }
+ WsAction::Lost => {
+ self.ws = None;
+ log::error!("WebSocket connection lost. Reconnecting");
+ self.fetching = false;
+ // reconnect
+ ctx.link().send_message(WsAction::Connect);
+ self.dispatch.reduce_mut(|state| {
+ state.config = None;
+ state.websocket_connected = false;
+ });
+
+ false
+ }
+ },
+ Msg::WsReady(response) => {
+ log::debug!("Received data: {:?}", response);
+
+ if response.is_err() {
+ log::error!("Error: {:?}", response.err().unwrap());
+ return false;
+ }
+
+ let data = response.unwrap();
+ // remove the first and last characters
+
+ log::debug!(
+ "Received text message after trimming and replacement: {}",
+ data
+ );
+
+ let data_deserialized: ServerWssMessage = match serde_json::from_str(&data) {
+ Ok(message) => message,
+ Err(e) => {
+ log::error!("Error deserializing message: {:?}", e);
+ return false;
+ }
+ };
+
+ match data_deserialized.get_message_type() {
+ ServerMessageTypes::ServerResponseConfig => {
+ log::debug!("Received config message");
+ self.dispatch
+ .reduce_mut(|state| match data_deserialized.get_data() {
+ MessageData::ShConfig(config) => {
+ state.config = Some(config.clone());
+ }
+ _ => {
+ log::error!("Received invalid data type");
+ }
+ });
+ }
+ _ => {
+ log::error!("Received unknown message: {:?}", data_deserialized);
+ }
+ }
+
+ false
+ }
+ }
}
fn view(&self, ctx: &Context) -> Html {
+ if self.ws.is_none() {
+ ctx.link().send_message(WsAction::Connect);
+ log::info!("Connecting to WebSocket");
+ } else {
+ log::info!("WebSocket is connected");
+ ctx.link()
+ .send_message(WsAction::SendData(UserWssMessage::new(
+ UserMessageTypes::UserRequestConfig,
+ MessageData::NoData,
+ )));
+ }
+
let send_data_to_wss = ctx.link().callback(move |input: UserWssMessage| {
log::debug!("Got a message. Sending up the chain to the websocket!");
Msg::WsAction(WsAction::SendData(input))
@@ -35,7 +194,6 @@ impl Component for App {
html! {
<>
-