310 lines
8.0 KiB
Rust

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<SurrealParams>,
}
#[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<Vec<Participant>>,
write: WriteSignal<Vec<Participant>>,
}
#[derive(Clone)]
pub struct SurrealContext {
pub message: Signal<Option<String>>,
send: Rc<dyn Fn(&str)>,
pub ready_state: Signal<ConnectionReadyState>,
pub authenticated: ReadSignal<bool>,
pub set_authenticated: WriteSignal<bool>,
}
#[derive(Serialize, Deserialize, Clone)]
struct SurrealResponse {
id: Option<u32>,
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<Participant>),
Action(SurrealAction),
Null,
}
impl SurrealContext {
pub fn new(
message: Signal<Option<String>>,
send: Rc<dyn Fn(&str)>,
ready_state: Signal<ConnectionReadyState>,
authenticated: ReadSignal<bool>,
set_authenticated: WriteSignal<bool>,
) -> 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<Participant>>(vec![]);
let (authenticated, set_authenticated) = create_signal::<bool>(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::<SurrealContext>();
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::<SurrealContext>();
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::<ParticipantsContext>();
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::<ParticipantsContext>();
let mut participants = participants_context.read.get();
participants.push(action.result);
participants_context.write.set(participants);
}