diff --git a/application/Cargo.toml b/application/Cargo.toml index fdc4ebf..3fee6c2 100644 --- a/application/Cargo.toml +++ b/application/Cargo.toml @@ -27,6 +27,7 @@ cfg-if = "1.0.0" once_cell = "1.19.0" futures = "0.3.30" uuid = "1.8.0" +leptos-use = "0.10.6" [features] hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] diff --git a/application/src/app.rs b/application/src/app.rs index e6a2841..881955a 100644 --- a/application/src/app.rs +++ b/application/src/app.rs @@ -16,6 +16,7 @@ pub fn App() -> impl IntoView { provide_context(participants); util::surrealdb::init_participants(); + util::websocket::client::init_websocket(); view! { // injects a stylesheet into the document diff --git a/application/src/pages/add_participant.rs b/application/src/pages/add_participant.rs index 8e25cb6..a757c9f 100644 --- a/application/src/pages/add_participant.rs +++ b/application/src/pages/add_participant.rs @@ -1,19 +1,19 @@ use leptos::*; use leptos_router::ActionForm; +use crate::util::surrealdb::schemas::Participant; + cfg_if::cfg_if! { if #[cfg(feature = "ssr")] { use crate::util::surrealdb::{DB, schemas}; - use crate::util::websocket::server; + use crate::util::websocket::{server, ParticipantsAction}; use leptos::logging; } } #[server(AddParticipant)] async fn add_participant(name: String, group: String) -> Result<(), ServerFnError> { - let websocket_sate = &server::WEBSOCKET_STATE; - - websocket_sate.tx.send("BOE".to_string()).unwrap(); + let websocket_state = &server::WEBSOCKET_STATE; let created: Vec = DB .create("participant") @@ -27,7 +27,20 @@ async fn add_participant(name: String, group: String) -> Result<(), ServerFnErro participant.name, participant.group ); - Ok(()) + + let action = ParticipantsAction::Add { + participant: Participant { + name: participant.name.clone(), + group: participant.group.clone(), + id: participant.id.to_string(), + events: None, + }, + }; + + match websocket_state.apply(action) { + Ok(_) => Ok(()), + Err(_) => Err(ServerFnError::new("Error sending websocket action")), + } } None => Err(ServerFnError::ServerError(String::from( "Could not create participant", diff --git a/application/src/pages/add_time.rs b/application/src/pages/add_time.rs index 0411a2b..56d8d1d 100644 --- a/application/src/pages/add_time.rs +++ b/application/src/pages/add_time.rs @@ -45,5 +45,6 @@ pub fn AddTime() -> impl IntoView { +

{ move || format!("{:?}", participants.unwrap().get()) }

} } diff --git a/application/src/util/websocket.rs b/application/src/util/websocket.rs index 573b0e2..98f64ec 100644 --- a/application/src/util/websocket.rs +++ b/application/src/util/websocket.rs @@ -1,2 +1,36 @@ #[cfg(feature = "ssr")] pub mod server; + +pub mod client; + +use serde::{Deserialize, Serialize}; + +use crate::util::surrealdb::schemas::Participant; + +#[derive(Serialize, Deserialize)] +pub enum ParticipantsAction { + Add { participant: Participant }, + Replace { participant: Participant }, + Remove { id: String }, +} + +/* +pub fn apply_participants_patch(mut participants: Vec, action: ParticipantsAction) { + match action { + ParticipantsAction::Add { participant } => { + participants.push(participant); + } + ParticipantsAction::Remove { id } => { + participants.retain(|participant| participant.id != id); + } + ParticipantsAction::Replace { participant } => { + if let Some(index) = participants + .iter() + .position(|item| item.id == participant.id) + { + let _ = std::mem::replace(&mut participants[index], participant); + } + } + } +} +*/ diff --git a/application/src/util/websocket/client.rs b/application/src/util/websocket/client.rs new file mode 100644 index 0000000..b27544e --- /dev/null +++ b/application/src/util/websocket/client.rs @@ -0,0 +1,55 @@ +use crate::util::surrealdb::schemas; +use leptos::*; +use leptos_use::*; + +use super::ParticipantsAction; + +pub fn init_websocket() { + let UseWebsocketReturn { + ready_state, + message, + .. + } = use_websocket("ws://localhost:3000/ws"); + + create_effect(move |prev_value| { + let msg = message.get(); + + if prev_value != Some(msg.clone()) { + match msg { + Some(ref text) => handle_message(text), + None => (), + } + } + + msg + }); +} + +fn handle_message(message: &str) { + let action: ParticipantsAction = match serde_json::from_str(message) { + Ok(res) => res, + Err(_err) => { + return; + } + }; + + let participants_context = use_context::>>().unwrap(); + + match action { + ParticipantsAction::Add { participant } => { + participants_context.update(|participants| participants.push(participant)) + } + ParticipantsAction::Remove { id } => participants_context + .update(|participants| participants.retain(|participant| participant.id != id)), + ParticipantsAction::Replace { participant } => { + participants_context.update(|participants| { + if let Some(index) = participants + .iter() + .position(|item| item.id == participant.id) + { + let _ = std::mem::replace(&mut participants[index], participant); + } + }) + } + } +} diff --git a/application/src/util/websocket/server.rs b/application/src/util/websocket/server.rs index de359c6..cbcbf99 100644 --- a/application/src/util/websocket/server.rs +++ b/application/src/util/websocket/server.rs @@ -1,3 +1,4 @@ +use crate::util::websocket; use axum::{ extract::ws::{Message, WebSocket, WebSocketUpgrade}, response::IntoResponse, @@ -23,6 +24,15 @@ pub struct WebSocketState { pub tx: broadcast::Sender, } +impl WebSocketState { + pub fn apply(&self, action: websocket::ParticipantsAction) -> Result<(), serde_json::Error> { + let message = serde_json::to_string(&action)?; + let _ = self.tx.send(message); + + Ok(()) + } +} + #[derive(Clone, axum::extract::FromRef)] pub struct AppState { pub leptos_options: LeptosOptions,