use crate::util; use leptos::*; use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebsocketReturn}; use serde::{Deserialize, Serialize}; use serde_json::json; use std::{alloc::handle_alloc_error, rc::Rc, time::Duration}; #[derive(Serialize)] #[serde(untagged)] enum SurrealId { String(String), Integer(u32), } #[derive(Serialize)] struct SurrealRequest { id: SurrealId, method: String, params: Vec, } #[derive(Serialize)] #[serde(untagged)] enum SurrealParams { Participant, SigninParam(SigninParam), CreatePersonParam(CreatePersonParam), CreateTimeParam(CreateTimeParam), String(String), } #[derive(Serialize)] struct SigninParam { user: String, pass: String, } #[derive(Serialize)] struct CreatePersonParam { name: String, group: String, } #[derive(Serialize)] pub struct CreateTimeParam { person_id: String, time: u64, } impl CreateTimeParam { pub fn new(person_id: String, minutes: u64, seconds: u64, miliseconds: u64) -> Self { Self { person_id, time: minutes * 60 * 1000 + seconds * 1000 + miliseconds * 10, } } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Participant { pub id: String, pub name: String, pub group: String, } #[derive(Clone, Debug)] pub struct ParticipantsContext { pub read: ReadSignal>, write: WriteSignal>, } #[derive(Clone)] pub struct SurrealContext { pub message: Signal>, send: Rc, pub ready_state: Signal, pub authenticated: ReadSignal, pub set_authenticated: WriteSignal, } #[derive(Serialize, Deserialize, Clone)] struct SurrealResponse { id: Option, result: SurrealResult, } #[derive(Serialize, Deserialize, Clone, Debug)] struct SurrealAction { id: String, action: String, result: Participant, } #[derive(Serialize, Deserialize, Clone)] #[serde(untagged)] enum SurrealResult { String(String), Participants(Vec), Action(SurrealAction), Null, } impl SurrealContext { pub fn new( message: Signal>, send: Rc, ready_state: Signal, authenticated: ReadSignal, set_authenticated: WriteSignal, ) -> Self { Self { message, send, ready_state, authenticated, set_authenticated, } } /// Send a string to surrealDB #[inline(always)] pub fn send(&self, message: &str) { (self.send)(message) } /// sigin SurrealDB pub fn signin(&self, pass: String) { log::debug!("Signing into surrealdb"); let request = SurrealRequest { id: SurrealId::Integer(0), method: String::from("signin"), params: vec![SurrealParams::SigninParam(SigninParam { user: String::from("root"), pass, })], }; self.send(&json!(request).to_string()); } pub fn add_person(&self, name: String, group: String) -> Result<(), String> { if name.is_empty() { return Err(String::from("Name cannot be empty")); } let request = SurrealRequest { id: SurrealId::Integer(10), method: String::from("create"), params: vec![ SurrealParams::String(String::from("person")), SurrealParams::CreatePersonParam(CreatePersonParam { name, group: "group:".to_owned() + &group, }), ], }; Ok(self.send(&json!(request).to_string())) } pub fn add_time(&self, body: CreateTimeParam, event: String) -> Result<(), String> { let request = SurrealRequest { id: SurrealId::Integer(20), method: String::from("create"), params: vec![ SurrealParams::String(event), SurrealParams::CreateTimeParam(body), ], }; Ok(self.send(&json!(request).to_string())) } } pub fn init_surrealdb() { // Initialize a connection to surrealDB let UseWebsocketReturn { ready_state, message, send, .. } = use_websocket("ws://localhost:80/rpc"); // Create reactive signals for the websocket connection to surrealDB let (participants, set_participants) = create_signal::>(vec![]); let (authenticated, set_authenticated) = create_signal::(false); // Bind the websocket connection to a context let websocket = SurrealContext::new( message, Rc::new(send.clone()), ready_state, authenticated, set_authenticated, ); provide_context(websocket); provide_context(ParticipantsContext { read: participants, write: set_participants, }); // Watch for a message recieved from the surrealDB connection create_effect(move |prev_value| { let msg = message.get(); if prev_value != Some(msg.clone()) { match msg { Some(ref text) => { log::debug!("{:?}", text); surrealdb_response(text.to_string()); } None => (), } } msg }); } /// Function to execute when SurrealDB returns a message fn surrealdb_response(response: String) { let response: SurrealResponse = match serde_json::from_str(&response) { Ok(res) => res, Err(err) => { log::warn!("{}", err); return; } }; match response.id { Some(0) => use_surrealdb(response.clone()), Some(1) => get_participants(), Some(2) => log::debug!("Subscribed to live timings"), Some(5) => got_participants(response.result.clone()), Some(_) => (), None => (), } match response.result { SurrealResult::Action(action) => handle_action(action), _ => (), } } /// Function to execute when DB signin is succesful fn use_surrealdb(_response: SurrealResponse) { util::toast::add_toast( "Succesfully signed into DB".to_string(), "success".to_string(), ); let websocket = expect_context::(); websocket.set_authenticated.set(true); let request = SurrealRequest { id: SurrealId::Integer(1), method: String::from("use"), params: vec![ SurrealParams::String(String::from("wrb")), SurrealParams::String(String::from("timings")), ], }; websocket.send(&json!(request).to_string()) } /// Function to get all participants and subscribe to changes fn get_participants() { let websocket = expect_context::(); let request = SurrealRequest { id: SurrealId::Integer(5), method: String::from("select"), params: vec![SurrealParams::String(String::from("person"))], }; websocket.send(&json!(request).to_string()); let request = SurrealRequest { id: SurrealId::Integer(2), method: String::from("live"), params: vec![SurrealParams::String(String::from("person"))], }; websocket.send(&json!(request).to_string()); } /// Function that will execute when participants are recieved fn got_participants(result: SurrealResult) { let participants_context = expect_context::(); if let SurrealResult::Participants(mut value) = result { let mut participants = participants_context.read.get(); participants.append(&mut value); participants_context.write.set(participants); } } /// Function to call when an action is recieved from surrealDB fn handle_action(action: SurrealAction) { let participants_context = expect_context::(); let mut participants = participants_context.read.get(); participants.push(action.result); participants_context.write.set(participants); }