Compare commits
No commits in common. "main" and "new-person" have entirely different histories.
main
...
new-person
@ -16,28 +16,16 @@ console_error_panic_hook = "0.1"
|
|||||||
leptos-use = "0.10.2"
|
leptos-use = "0.10.2"
|
||||||
serde = "1.0.196"
|
serde = "1.0.196"
|
||||||
serde_json = "1.0.113"
|
serde_json = "1.0.113"
|
||||||
rand = "0.8.5"
|
|
||||||
gloo-timers = "0.3.0"
|
|
||||||
strsim = "0.11.0"
|
|
||||||
|
|
||||||
# utils
|
# utils
|
||||||
# strum = { version = "0.25", features = ["derive", "strum_macros"] }
|
# strum = { version = "0.25", features = ["derive", "strum_macros"] }
|
||||||
# strum_macros = "0.25"
|
# strum_macros = "0.25"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
|
||||||
version = "0.3"
|
|
||||||
features = [
|
|
||||||
"Document",
|
|
||||||
"Window",
|
|
||||||
"Element",
|
|
||||||
"ScrollIntoViewOptions",
|
|
||||||
"ScrollLogicalPosition",
|
|
||||||
"ScrollBehavior",
|
|
||||||
]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
|
web-sys = { version = "0.3", features = ["Document", "Window"] }
|
||||||
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
@ -13,8 +13,6 @@ html,
|
|||||||
body {
|
body {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
|
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
@ -27,7 +25,6 @@ body {
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -113,84 +110,8 @@ form {
|
|||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toastcontainer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
gap: 10px;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 999;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toastcontainer span {
|
|
||||||
padding: 10px 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
width: 300px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toastcontainer span:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
background-color: #f29826;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
background-color: #d9534f;
|
color: red;
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
background-color: #2181d4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success {
|
|
||||||
background-color: #4bb24b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.participants-container {
|
|
||||||
height: 200px;
|
|
||||||
overflow: scroll;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
list-style-type: none;
|
|
||||||
margin-top: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.participants-container li {
|
|
||||||
text-align: left;
|
|
||||||
background-color: $secondary-bg-color-light;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.participants-container li:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: $secondary-bg-color-lighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
.participants-container .selected {
|
|
||||||
border: 1px solid $primary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-input-container {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin: 10px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-input-container input[type=number] {
|
|
||||||
width: 30px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
-moz-appearance: textfield;
|
font-size: 1.2em;
|
||||||
}
|
|
||||||
|
|
||||||
.time-input-container input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1 @@
|
|||||||
pub mod navbar;
|
pub mod navbar;
|
||||||
pub mod toast;
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
use crate::util;
|
|
||||||
use leptos::*;
|
|
||||||
|
|
||||||
/// Navigation bar
|
|
||||||
#[component]
|
|
||||||
pub fn Toasts() -> impl IntoView {
|
|
||||||
let notifications = expect_context::<util::toast::NotificationsContext>();
|
|
||||||
|
|
||||||
view! {
|
|
||||||
<div class="toastcontainer">
|
|
||||||
{move || notifications.notifications.get().into_iter()
|
|
||||||
.map(|n| view! {
|
|
||||||
<span on:click=move |_| util::toast::remove_toast((*n.id).to_string()) class=n.option>{n.text}</span>
|
|
||||||
}
|
|
||||||
).collect_view()}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,7 +20,6 @@ pub fn App() -> impl IntoView {
|
|||||||
// Provides context that manages stylesheets, titles, meta tags, etc.
|
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||||
provide_meta_context();
|
provide_meta_context();
|
||||||
util::surrealdb::init_surrealdb();
|
util::surrealdb::init_surrealdb();
|
||||||
util::toast::init_toast();
|
|
||||||
|
|
||||||
let websocket = expect_context::<util::surrealdb::SurrealContext>();
|
let websocket = expect_context::<util::surrealdb::SurrealContext>();
|
||||||
let _participants = use_context::<util::surrealdb::ParticipantsContext>()
|
let _participants = use_context::<util::surrealdb::ParticipantsContext>()
|
||||||
@ -59,17 +58,14 @@ pub fn App() -> impl IntoView {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Router>
|
<Router>
|
||||||
<components::toast::Toasts />
|
|
||||||
<components::navbar::Navbar />
|
<components::navbar::Navbar />
|
||||||
<main>
|
<main>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" view=move || {
|
<Route path="/" view=move || {
|
||||||
view! {
|
view! {
|
||||||
// only show the outlet if the signin was successfull
|
// only show the outlet if the signin was successfull
|
||||||
<Show when=move || !websocket.loading.get() fallback=|| view! { <p>"Connection to database..."</p>}>
|
<Show when=move || websocket.authenticated.get() fallback=login::Login>
|
||||||
<Show when=move || websocket.authenticated.get() fallback=login::Login>
|
<Outlet/>
|
||||||
<Outlet/>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
</Show>
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
@ -77,7 +73,6 @@ pub fn App() -> impl IntoView {
|
|||||||
<Route path="/participants" view=participants::Participants />
|
<Route path="/participants" view=participants::Participants />
|
||||||
<Route path="/participants/add" view=participants::add::Add />
|
<Route path="/participants/add" view=participants::add::Add />
|
||||||
<Route path="/times" view=times::Times />
|
<Route path="/times" view=times::Times />
|
||||||
<Route path="/times/add" view=times::add::Add />
|
|
||||||
<Route path="/*" view=NotFound />
|
<Route path="/*" view=NotFound />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_router::*;
|
|
||||||
|
|
||||||
pub mod add;
|
pub mod add;
|
||||||
|
|
||||||
@ -7,6 +6,10 @@ pub mod add;
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn Times() -> impl IntoView {
|
pub fn Times() -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<A class="btn-add-link" href="/times/add">"Tijd toevoegen"</A>
|
<div class="container">
|
||||||
|
|
||||||
|
<h1>"Tijden"</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,209 +1,9 @@
|
|||||||
use crate::util::surrealdb;
|
use leptos::*;
|
||||||
use leptos::{ev::keydown, *};
|
|
||||||
use leptos_use::*;
|
|
||||||
use strsim::normalized_damerau_levenshtein;
|
|
||||||
use web_sys::{ScrollIntoViewOptions, ScrollLogicalPosition};
|
|
||||||
|
|
||||||
use crate::util::surrealdb::Participant;
|
|
||||||
|
|
||||||
// Functions to sort all the participants and include a search term
|
|
||||||
fn sort_participants(participants: Vec<Participant>, search: String) -> Vec<Participant> {
|
|
||||||
let mut filtered_sorted_list: Vec<(Participant, f64)> = participants
|
|
||||||
.into_iter()
|
|
||||||
.map(|participant| {
|
|
||||||
(
|
|
||||||
participant.clone(),
|
|
||||||
normalized_damerau_levenshtein(
|
|
||||||
&participant.name.to_lowercase(),
|
|
||||||
&search.to_lowercase(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
filtered_sorted_list.sort_by(|a, b| {
|
|
||||||
let (_, sim_score_a) = a;
|
|
||||||
let (_, sim_score_b) = b;
|
|
||||||
sim_score_b
|
|
||||||
.partial_cmp(sim_score_a)
|
|
||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
|
||||||
});
|
|
||||||
|
|
||||||
filtered_sorted_list
|
|
||||||
.into_iter()
|
|
||||||
.map(|(item, _)| item)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub fn SelectOption(is: &'static str, value: ReadSignal<String>) -> impl IntoView {
|
|
||||||
view! {
|
|
||||||
<option
|
|
||||||
value=is
|
|
||||||
selected=move || value.get() == is
|
|
||||||
>
|
|
||||||
{is}
|
|
||||||
</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Navigation bar
|
/// Navigation bar
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Add() -> impl IntoView {
|
pub fn Add() -> impl IntoView {
|
||||||
let websocket = expect_context::<crate::util::surrealdb::SurrealContext>();
|
|
||||||
let participants = expect_context::<crate::util::surrealdb::ParticipantsContext>();
|
|
||||||
|
|
||||||
let container_ref: NodeRef<html::Ul> = create_node_ref();
|
|
||||||
let name_ref: NodeRef<html::Input> = create_node_ref();
|
|
||||||
|
|
||||||
let (name, set_name) = create_signal(String::from(""));
|
|
||||||
let (event, set_event) = create_signal(String::from("lifesaver"));
|
|
||||||
let (error, set_error) = create_signal(String::from(""));
|
|
||||||
let (minutes, set_minutes) = create_signal::<u64>(0);
|
|
||||||
let (seconds, set_seconds) = create_signal::<u64>(0);
|
|
||||||
let (miliseconds, set_miliseconds) = create_signal::<u64>(0);
|
|
||||||
let (selected, set_selected) = create_signal::<usize>(0);
|
|
||||||
|
|
||||||
let participants_sorted =
|
|
||||||
create_memo(move |_| sort_participants(participants.read.get(), name.get()));
|
|
||||||
|
|
||||||
let _ = use_event_listener(use_document(), keydown, move |evt| {
|
|
||||||
if evt.key() == "ArrowDown".to_string() {
|
|
||||||
set_selected.update(|x| {
|
|
||||||
let len = participants.read.get_untracked().len();
|
|
||||||
if *x != len {
|
|
||||||
*x += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if evt.key() == "ArrowUp".to_string() {
|
|
||||||
set_selected.update(|x| {
|
|
||||||
if *x != 0 {
|
|
||||||
*x -= 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let el: web_sys::Element = container_ref
|
|
||||||
.get_untracked()
|
|
||||||
.unwrap()
|
|
||||||
.children()
|
|
||||||
.item(selected.get_untracked().try_into().unwrap())
|
|
||||||
.expect("No element found");
|
|
||||||
|
|
||||||
el.scroll_into_view_with_scroll_into_view_options(
|
|
||||||
&ScrollIntoViewOptions::new().block(ScrollLogicalPosition::Center),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let on_submit = move |ev: leptos::ev::SubmitEvent| {
|
|
||||||
ev.prevent_default();
|
|
||||||
set_error.set(String::from(""));
|
|
||||||
|
|
||||||
let person = &participants_sorted.get()[selected.get()];
|
|
||||||
|
|
||||||
match websocket.add_time(
|
|
||||||
surrealdb::CreateTimeParam::new(
|
|
||||||
person.clone().id,
|
|
||||||
minutes.get(),
|
|
||||||
seconds.get(),
|
|
||||||
miliseconds.get(),
|
|
||||||
),
|
|
||||||
event.get(),
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
set_name.set(String::from(""));
|
|
||||||
set_minutes.set(0);
|
|
||||||
set_seconds.set(0);
|
|
||||||
set_miliseconds.set(0);
|
|
||||||
|
|
||||||
let _ = name_ref.get().unwrap().focus();
|
|
||||||
}
|
|
||||||
Err(err) => set_error.set(err),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<h1>"Tijd toevoegen"</h1>
|
<h1>"Deelnemer toevoegen"</h1>
|
||||||
<form class="add" on:submit=on_submit>
|
|
||||||
<div class="time-input-container">
|
|
||||||
<span>"Onderdeel:"</span>
|
|
||||||
<select
|
|
||||||
on:change=move |ev| {
|
|
||||||
set_event.set(event_target_value(&ev));
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SelectOption value=event is="lifesaver"/>
|
|
||||||
<SelectOption value=event is="popduiken"/>
|
|
||||||
<SelectOption value=event is="hindernis"/>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<input type="text"
|
|
||||||
placeholder="Name"
|
|
||||||
tabindex=0
|
|
||||||
node_ref=name_ref
|
|
||||||
on:input=move |ev| {
|
|
||||||
set_name.set(event_target_value(&ev));
|
|
||||||
set_selected.set(0);
|
|
||||||
}
|
|
||||||
prop:value=name
|
|
||||||
/>
|
|
||||||
<ul class="participants-container" tabindex=-1 node_ref=container_ref>
|
|
||||||
{move || participants_sorted.get().into_iter().enumerate().map(|(i, participant)| view! {
|
|
||||||
<li on:click=move |_| set_selected.set(i) class:selected=move || selected.get() == i>{participant.name + " " + "(" + &participant.group[6..].to_string() + ")" }</li>
|
|
||||||
}).collect_view()}
|
|
||||||
</ul>
|
|
||||||
<div class="time-input-container">
|
|
||||||
<span>"Tijd:"</span>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
tabindex=0
|
|
||||||
placeholder="mm"
|
|
||||||
min=0
|
|
||||||
max=99
|
|
||||||
on:input=move |ev| {
|
|
||||||
let x = event_target_value(&ev).parse::<u64>();
|
|
||||||
match x {
|
|
||||||
Ok(x) => set_minutes.set(x),
|
|
||||||
Err(_) => set_minutes.set(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prop:value=minutes
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
tabindex=0
|
|
||||||
placeholder="ss"
|
|
||||||
min=0
|
|
||||||
max=59
|
|
||||||
on:input=move |ev| {
|
|
||||||
let x = event_target_value(&ev).parse::<u64>();
|
|
||||||
match x {
|
|
||||||
Ok(x) => set_seconds.set(x),
|
|
||||||
Err(_) => set_seconds.set(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prop:value=seconds
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
tabindex=0
|
|
||||||
placeholder="ms"
|
|
||||||
min=0
|
|
||||||
max=99
|
|
||||||
on:input=move |ev| {
|
|
||||||
let x = event_target_value(&ev).parse::<u64>();
|
|
||||||
match x {
|
|
||||||
Ok(x) => set_miliseconds.set(x),
|
|
||||||
Err(_) => set_miliseconds.set(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prop:value=miliseconds
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<input type="submit" tabindex=0 value="Submit" />
|
|
||||||
</form>
|
|
||||||
<p class="error">
|
|
||||||
{ error }
|
|
||||||
</p>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1 @@
|
|||||||
pub mod surrealdb;
|
pub mod surrealdb;
|
||||||
pub mod toast;
|
|
||||||
|
@ -1,26 +1,12 @@
|
|||||||
use crate::util;
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_use::{
|
use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebsocketReturn};
|
||||||
core::ConnectionReadyState,
|
|
||||||
storage::use_local_storage,
|
|
||||||
use_websocket,
|
|
||||||
utils::{FromToStringCodec, StringCodec},
|
|
||||||
UseWebsocketReturn,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum SurrealId {
|
|
||||||
String(String),
|
|
||||||
Integer(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct SurrealRequest {
|
struct SurrealRequest {
|
||||||
id: SurrealId,
|
id: u32,
|
||||||
method: String,
|
method: String,
|
||||||
params: Vec<SurrealParams>,
|
params: Vec<SurrealParams>,
|
||||||
}
|
}
|
||||||
@ -31,7 +17,6 @@ enum SurrealParams {
|
|||||||
Participant,
|
Participant,
|
||||||
SigninParam(SigninParam),
|
SigninParam(SigninParam),
|
||||||
CreatePersonParam(CreatePersonParam),
|
CreatePersonParam(CreatePersonParam),
|
||||||
CreateTimeParam(CreateTimeParam),
|
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,33 +32,14 @@ struct CreatePersonParam {
|
|||||||
group: String,
|
group: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Clone, Debug)]
|
||||||
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 struct Participant {
|
||||||
pub id: String,
|
name: String,
|
||||||
pub name: String,
|
group: String,
|
||||||
pub group: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ParticipantsContext {
|
pub struct ParticipantsContext(pub ReadSignal<Vec<Participant>>);
|
||||||
pub read: ReadSignal<Vec<Participant>>,
|
|
||||||
write: WriteSignal<Vec<Participant>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SurrealContext {
|
pub struct SurrealContext {
|
||||||
@ -82,41 +48,18 @@ pub struct SurrealContext {
|
|||||||
pub ready_state: Signal<ConnectionReadyState>,
|
pub ready_state: Signal<ConnectionReadyState>,
|
||||||
pub authenticated: ReadSignal<bool>,
|
pub authenticated: ReadSignal<bool>,
|
||||||
pub set_authenticated: WriteSignal<bool>,
|
pub set_authenticated: WriteSignal<bool>,
|
||||||
pub loading: ReadSignal<bool>,
|
|
||||||
pub set_loading: WriteSignal<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SurrealResponse {
|
struct SurrealResponse {
|
||||||
id: Option<u32>,
|
id: u32,
|
||||||
result: SurrealResult,
|
result: SurrealResult,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SurrealResultError {
|
|
||||||
code: i32,
|
|
||||||
message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
struct SurrealResponseError {
|
|
||||||
id: u32,
|
|
||||||
error: SurrealResultError,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
struct SurrealAction {
|
|
||||||
id: String,
|
|
||||||
action: String,
|
|
||||||
result: Participant,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum SurrealResult {
|
enum SurrealResult {
|
||||||
String(String),
|
String(String),
|
||||||
Participants(Vec<Participant>),
|
|
||||||
Action(SurrealAction),
|
|
||||||
Null,
|
Null,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +70,6 @@ impl SurrealContext {
|
|||||||
ready_state: Signal<ConnectionReadyState>,
|
ready_state: Signal<ConnectionReadyState>,
|
||||||
authenticated: ReadSignal<bool>,
|
authenticated: ReadSignal<bool>,
|
||||||
set_authenticated: WriteSignal<bool>,
|
set_authenticated: WriteSignal<bool>,
|
||||||
loading: ReadSignal<bool>,
|
|
||||||
set_loading: WriteSignal<bool>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
message,
|
message,
|
||||||
@ -136,8 +77,6 @@ impl SurrealContext {
|
|||||||
ready_state,
|
ready_state,
|
||||||
authenticated,
|
authenticated,
|
||||||
set_authenticated,
|
set_authenticated,
|
||||||
loading,
|
|
||||||
set_loading,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +91,7 @@ impl SurrealContext {
|
|||||||
log::debug!("Signing into surrealdb");
|
log::debug!("Signing into surrealdb");
|
||||||
|
|
||||||
let request = SurrealRequest {
|
let request = SurrealRequest {
|
||||||
id: SurrealId::Integer(0),
|
id: 0,
|
||||||
method: String::from("signin"),
|
method: String::from("signin"),
|
||||||
params: vec![SurrealParams::SigninParam(SigninParam {
|
params: vec![SurrealParams::SigninParam(SigninParam {
|
||||||
user: String::from("root"),
|
user: String::from("root"),
|
||||||
@ -169,7 +108,7 @@ impl SurrealContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let request = SurrealRequest {
|
let request = SurrealRequest {
|
||||||
id: SurrealId::Integer(10),
|
id: 1,
|
||||||
method: String::from("create"),
|
method: String::from("create"),
|
||||||
params: vec![
|
params: vec![
|
||||||
SurrealParams::String(String::from("person")),
|
SurrealParams::String(String::from("person")),
|
||||||
@ -182,19 +121,6 @@ impl SurrealContext {
|
|||||||
|
|
||||||
Ok(self.send(&json!(request).to_string()))
|
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() {
|
pub fn init_surrealdb() {
|
||||||
@ -207,9 +133,8 @@ pub fn init_surrealdb() {
|
|||||||
} = use_websocket("ws://localhost:80/rpc");
|
} = use_websocket("ws://localhost:80/rpc");
|
||||||
|
|
||||||
// Create reactive signals for the websocket connection to surrealDB
|
// Create reactive signals for the websocket connection to surrealDB
|
||||||
let (participants, set_participants) = create_signal::<Vec<Participant>>(vec![]);
|
let (participants, _set_participants) = create_signal::<Vec<Participant>>(vec![]);
|
||||||
let (authenticated, set_authenticated) = create_signal::<bool>(false);
|
let (authenticated, set_authenticated) = create_signal::<bool>(false);
|
||||||
let (loading, set_loading) = create_signal::<bool>(true);
|
|
||||||
|
|
||||||
// Bind the websocket connection to a context
|
// Bind the websocket connection to a context
|
||||||
let websocket = SurrealContext::new(
|
let websocket = SurrealContext::new(
|
||||||
@ -218,38 +143,10 @@ pub fn init_surrealdb() {
|
|||||||
ready_state,
|
ready_state,
|
||||||
authenticated,
|
authenticated,
|
||||||
set_authenticated,
|
set_authenticated,
|
||||||
loading,
|
|
||||||
set_loading,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
provide_context(websocket.clone());
|
provide_context(websocket);
|
||||||
provide_context(ParticipantsContext {
|
provide_context(ParticipantsContext(participants));
|
||||||
read: participants,
|
|
||||||
write: set_participants,
|
|
||||||
});
|
|
||||||
|
|
||||||
create_effect(move |prev_value| {
|
|
||||||
let status = ready_state.get();
|
|
||||||
|
|
||||||
if prev_value != Some(status.clone()) && status == ConnectionReadyState::Open {
|
|
||||||
let (token, _, _) = use_local_storage::<String, FromToStringCodec>("surrealdb-token");
|
|
||||||
|
|
||||||
if token.get().is_empty() {
|
|
||||||
set_loading.set(false);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
let request = SurrealRequest {
|
|
||||||
id: SurrealId::Integer(0),
|
|
||||||
method: String::from("authenticate"),
|
|
||||||
params: vec![SurrealParams::String(token.get())],
|
|
||||||
};
|
|
||||||
|
|
||||||
websocket.send(&json!(request).to_string());
|
|
||||||
};
|
|
||||||
|
|
||||||
status
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch for a message recieved from the surrealDB connection
|
// Watch for a message recieved from the surrealDB connection
|
||||||
create_effect(move |prev_value| {
|
create_effect(move |prev_value| {
|
||||||
@ -272,31 +169,6 @@ pub fn init_surrealdb() {
|
|||||||
/// Function to execute when SurrealDB returns a message
|
/// Function to execute when SurrealDB returns a message
|
||||||
fn surrealdb_response(response: String) {
|
fn surrealdb_response(response: String) {
|
||||||
let response: SurrealResponse = match serde_json::from_str(&response) {
|
let response: SurrealResponse = match serde_json::from_str(&response) {
|
||||||
Ok(res) => res,
|
|
||||||
Err(_) => {
|
|
||||||
handle_error(response);
|
|
||||||
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 an error is returned from Surrealdb
|
|
||||||
fn handle_error(response: String) {
|
|
||||||
let response: SurrealResponseError = match serde_json::from_str(&response) {
|
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::warn!("{}", err);
|
log::warn!("{}", err);
|
||||||
@ -304,37 +176,21 @@ fn handle_error(response: String) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if response.id == 0 && response.error.code == -32000 {
|
match response.id {
|
||||||
let (_, _, remove_token) =
|
0 => use_surrealdb(response),
|
||||||
use_local_storage::<String, FromToStringCodec>("surrealdb-token");
|
1 => log::debug!("Succesfully execute use wrb timings"),
|
||||||
let websocket = expect_context::<SurrealContext>();
|
_ => return,
|
||||||
|
|
||||||
remove_token();
|
|
||||||
websocket.set_loading.set(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function to execute when DB signin is succesful
|
/// Function to execute when DB signin is succesful
|
||||||
fn use_surrealdb(response: SurrealResponse) {
|
fn use_surrealdb(_response: SurrealResponse) {
|
||||||
util::toast::add_toast(
|
|
||||||
"Succesfully signed into DB".to_string(),
|
|
||||||
"success".to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (_, set_token, _) = use_local_storage::<String, FromToStringCodec>("surrealdb-token");
|
|
||||||
|
|
||||||
match response.result {
|
|
||||||
SurrealResult::String(str) => set_token.set(str),
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
|
|
||||||
let websocket = expect_context::<SurrealContext>();
|
let websocket = expect_context::<SurrealContext>();
|
||||||
|
|
||||||
websocket.set_authenticated.set(true);
|
websocket.set_authenticated.set(true);
|
||||||
websocket.set_loading.set(false);
|
|
||||||
|
|
||||||
let request = SurrealRequest {
|
let request = SurrealRequest {
|
||||||
id: SurrealId::Integer(1),
|
id: 1,
|
||||||
method: String::from("use"),
|
method: String::from("use"),
|
||||||
params: vec![
|
params: vec![
|
||||||
SurrealParams::String(String::from("wrb")),
|
SurrealParams::String(String::from("wrb")),
|
||||||
@ -344,44 +200,3 @@ fn use_surrealdb(response: SurrealResponse) {
|
|||||||
|
|
||||||
websocket.send(&json!(request).to_string())
|
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);
|
|
||||||
}
|
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
use gloo_timers::future::TimeoutFuture;
|
|
||||||
use leptos::*;
|
|
||||||
use rand::distributions::{Alphanumeric, DistString};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NotificationsContext {
|
|
||||||
pub notifications: ReadSignal<Vec<ToastNotification>>,
|
|
||||||
pub set_notifications: WriteSignal<Vec<ToastNotification>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ToastNotification {
|
|
||||||
pub text: String,
|
|
||||||
pub option: String, // error, warning, info, success
|
|
||||||
pub id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_toast() {
|
|
||||||
let (notifications, set_notifications) = create_signal::<Vec<ToastNotification>>(vec![]);
|
|
||||||
provide_context(NotificationsContext {
|
|
||||||
notifications,
|
|
||||||
set_notifications,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_toast(text: String, option: String) {
|
|
||||||
let context = expect_context::<NotificationsContext>();
|
|
||||||
|
|
||||||
let id = Alphanumeric.sample_string(&mut rand::thread_rng(), 4);
|
|
||||||
|
|
||||||
let mut vec = context.notifications.get();
|
|
||||||
vec.push(ToastNotification {
|
|
||||||
text,
|
|
||||||
option,
|
|
||||||
id: id.clone(),
|
|
||||||
});
|
|
||||||
context.set_notifications.set(vec);
|
|
||||||
|
|
||||||
spawn_local(async {
|
|
||||||
TimeoutFuture::new(5000).await;
|
|
||||||
remove_toast(id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_toast(id: String) {
|
|
||||||
let context = expect_context::<NotificationsContext>();
|
|
||||||
|
|
||||||
let mut vec = context.notifications.get_untracked();
|
|
||||||
vec.retain(|x| x.id != id);
|
|
||||||
context.set_notifications.set(vec);
|
|
||||||
}
|
|
24
flake.lock
24
flake.lock
@ -5,11 +5,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1710146030,
|
"lastModified": 1705309234,
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -38,11 +38,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1710951922,
|
"lastModified": 1707514827,
|
||||||
"narHash": "sha256-FOOBJ3DQenLpTNdxMHR2CpGZmYuctb92gF0lpiirZ30=",
|
"narHash": "sha256-Y+wqFkvikpE1epCx57PsGw+M1hX5aY5q/xgk+ebDwxI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f091af045dff8347d66d186a62d42aceff159456",
|
"rev": "20f65b86b6485decb43c5498780c223571dd56ef",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -54,11 +54,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1711001935,
|
"lastModified": 1707546158,
|
||||||
"narHash": "sha256-URtGpHue7HHZK0mrHnSf8wJ6OmMKYSsoLmJybrOLFSQ=",
|
"narHash": "sha256-nYYJTpzfPMDxI8mzhQsYjIUX+grorqjKEU9Np6Xwy/0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "20f77aa09916374aa3141cbc605c955626762c9a",
|
"rev": "d934204a0f8d9198e1e4515dd6fec76a139c87f0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -98,11 +98,11 @@
|
|||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1711073443,
|
"lastModified": 1707703915,
|
||||||
"narHash": "sha256-PpNb4xq7U5Q/DdX40qe7CijUsqhVVM3VZrhN0+c6Lcw=",
|
"narHash": "sha256-Vej69igzNr3eVDca6+32uO+TXjVWx6ZUwwy3iZuzhJ4=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "eec55ba9fcde6be4c63942827247e42afef7fafe",
|
"rev": "e6679d2ff9136d00b3a7168d2bf1dff9e84c5758",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
Loading…
Reference in New Issue
Block a user