This is madness

This commit is contained in:
Daan Vanoverloop 2022-09-12 18:03:34 +02:00
parent 49d473e213
commit 2ddbfc2fdb
Signed by: Danacus
GPG Key ID: F2272B50E129FC5C
10 changed files with 75 additions and 88 deletions

1
Cargo.lock generated
View File

@ -1158,6 +1158,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"lan_party_macros", "lan_party_macros",
"log",
"paste", "paste",
"rocket", "rocket",
"schemars", "schemars",

View File

@ -21,3 +21,4 @@ lan_party_macros = { path = "../macros", optional = true }
paste = "1" paste = "1"
thiserror = "1.0" thiserror = "1.0"
displaydoc = "0.2" displaydoc = "0.2"
log = "0.4"

View File

@ -6,6 +6,7 @@ use std::{
}; };
use crate::components::Block; use crate::components::Block;
use log::debug;
use paste::paste; use paste::paste;
use sycamore::prelude::*; use sycamore::prelude::*;
@ -262,7 +263,7 @@ pub struct VecEdit;
impl<'a, G, T, I> Editor<'a, G, I> for VecEdit impl<'a, G, T, I> Editor<'a, G, I> for VecEdit
where where
G: Html, G: Html,
T: for<'b> Editable<'b, G> + Clone + PartialEq + Default + 'a, T: for<'b> Editable<'b, G> + Clone + PartialEq + Default + std::fmt::Debug + 'a,
I: IntoIterator<Item = T> + FromIterator<T> + Clone, I: IntoIterator<Item = T> + FromIterator<T> + Clone,
{ {
fn edit(cx: Scope<'a>, props: EditProps<'a, I>) -> View<G> { fn edit(cx: Scope<'a>, props: EditProps<'a, I>) -> View<G> {
@ -290,6 +291,17 @@ where
}); });
let onadd = move |_| vec.modify().push(create_signal(cx, T::default())); let onadd = move |_| vec.modify().push(create_signal(cx, T::default()));
let onremove = move |item: &'a Signal<T>| {
move |_| {
let cloned = vec.get().as_ref().clone();
vec.set(
cloned
.into_iter()
.filter(|x| x.get().as_ref() != item.get().as_ref())
.collect(),
);
}
};
Block( Block(
cx, cx,
@ -297,62 +309,30 @@ where
title: "List".into(), title: "List".into(),
children: Children::new(cx, move |_| { children: Children::new(cx, move |_| {
view! { cx, view! { cx,
//Block(title="List".into()) {
div { div {
Indexed( Indexed(
iterable=vec, iterable=vec,
view=|cx: BoundedScope<'_, 'a>, x: &'a Signal<T>| { view=move |cx: BoundedScope<'_, 'a>, x: &'a Signal<T>| {
view! { cx, view! { cx,
(x.edit(cx)) (x.edit(cx))
Button(onclick=onremove(x), icon="mdi-delete".into())
br() br()
} }
}, },
) )
Button(onclick=onadd, text="Add new".into(), icon="mdi-plus".into()) Button(onclick=onadd, text="Add new".into(), icon="mdi-plus".into())
} }
//}
} }
}), }),
}, },
) )
/*
Block(
cx,
BlockProps {
title: "List".into(),
children: Children::new(cx, move |_| {
div()
.dyn_c_scoped(move |cx| {
View::new_fragment(
vec.get()
.as_ref()
.clone()
.into_iter()
.map(|x| div().c(x.edit(cx)).c(br()).view(cx))
.collect(),
)
})
.c(Button(
cx,
ButtonProps {
onclick: onadd,
text: "Add new".into(),
icon: "mdi-plus".into(),
},
))
.view(cx)
}),
},
)
*/
} }
} }
impl<'a, G, T> Editable<'a, G> for Vec<T> impl<'a, G, T> Editable<'a, G> for Vec<T>
where where
G: Html, G: Html,
T: for<'b> Editable<'b, G> + Clone + PartialEq + Default + 'a, T: for<'b> Editable<'b, G> + Clone + PartialEq + Default + std::fmt::Debug + 'a,
{ {
type Editor = VecEdit; type Editor = VecEdit;
} }
@ -360,7 +340,7 @@ where
impl<'a, G, T> Editable<'a, G> for HashSet<T> impl<'a, G, T> Editable<'a, G> for HashSet<T>
where where
G: Html, G: Html,
T: for<'b> Editable<'b, G> + Clone + PartialEq + Default + Hash + Eq + 'a, T: for<'b> Editable<'b, G> + Clone + PartialEq + Default + Hash + Eq + std::fmt::Debug + 'a,
{ {
type Editor = VecEdit; type Editor = VecEdit;
} }
@ -370,7 +350,8 @@ where
G: Html, G: Html,
K: Clone + Hash + Eq, K: Clone + Hash + Eq,
V: Clone, V: Clone,
(K, V): for<'b> Editable<'b, G> + Clone + PartialEq + Default + Hash + Eq + 'a, (K, V):
for<'b> Editable<'b, G> + Clone + PartialEq + Default + Hash + Eq + std::fmt::Debug + 'a,
{ {
type Editor = VecEdit; type Editor = VecEdit;
} }

View File

@ -22,7 +22,7 @@ pub struct EventOutcome {
/// # Event /// # Event
/// ///
/// An event in which participants can win or lose points /// An event in which participants can win or lose points
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "openapi", derive(JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "sycamore", derive(WebView))] #[cfg_attr(feature = "sycamore", derive(WebView))]
@ -36,7 +36,6 @@ pub struct Event {
/// Description of the event /// Description of the event
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(default))]
pub description: String, pub description: String,
/// Event type
pub event_type: EventType, pub event_type: EventType,
} }
@ -60,7 +59,9 @@ impl Event {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))] #[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))]
pub struct EventSpec { pub struct EventSpec {
/// Name of the event
pub name: String, pub name: String,
/// Description of the event
pub description: String, pub description: String,
pub event_type: EventTypeSpec, pub event_type: EventTypeSpec,
} }
@ -93,7 +94,7 @@ macro_rules! events {
/// # EventType /// # EventType
/// ///
/// An enumeration of event types /// An enumeration of event types
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "openapi", derive(JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))] #[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))]
@ -160,7 +161,7 @@ pub trait EventTrait {
pub mod test { pub mod test {
use super::*; use super::*;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "openapi", derive(JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))] #[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))]
@ -219,7 +220,7 @@ pub mod team_game {
*, *,
}; };
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "openapi", derive(JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))] #[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))]
@ -421,7 +422,7 @@ pub mod free_for_all_game {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "openapi", derive(JsonSchema))] #[cfg_attr(feature = "openapi", derive(JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))] #[cfg_attr(feature = "sycamore", derive(WebEdit, WebView))]

View File

@ -27,11 +27,11 @@ macro_rules! viewable {
#[derive(Prop)] #[derive(Prop)]
pub struct ViewProps<'a, T> { pub struct ViewProps<'a, T> {
pub state: &'a Signal<T>, pub state: &'a T,
} }
impl<'a, T> From<&'a Signal<T>> for ViewProps<'a, T> { impl<'a, T> From<&'a T> for ViewProps<'a, T> {
fn from(state: &'a Signal<T>) -> Self { fn from(state: &'a T) -> Self {
ViewProps { state } ViewProps { state }
} }
} }
@ -40,9 +40,9 @@ pub trait IntoView<'a, G: Html> {
fn view(self, cx: Scope<'a>) -> View<G>; fn view(self, cx: Scope<'a>) -> View<G>;
} }
impl<'a, G: Html, T: Viewable<'a, G>> IntoView<'a, G> for &'a Signal<T> impl<'a, G: Html, T: Viewable<'a, G>> IntoView<'a, G> for &'a T
where where
ViewProps<'a, T>: From<&'a Signal<T>>, ViewProps<'a, T>: From<&'a T>,
{ {
fn view(self, cx: Scope<'a>) -> View<G> { fn view(self, cx: Scope<'a>) -> View<G> {
T::view(cx, self.into()) T::view(cx, self.into())
@ -80,7 +80,7 @@ where
{ {
fn view(cx: Scope<'a>, props: ViewProps<'a, T>) -> View<G> { fn view(cx: Scope<'a>, props: ViewProps<'a, T>) -> View<G> {
view! { cx, view! { cx,
(props.state.get().clone().create()) (props.state.create())
} }
} }
} }
@ -102,9 +102,8 @@ pub struct BoolView;
impl<'a, G: Html> Viewer<'a, G, bool> for BoolView { impl<'a, G: Html> Viewer<'a, G, bool> for BoolView {
fn view(cx: Scope<'a>, props: ViewProps<'a, bool>) -> View<G> { fn view(cx: Scope<'a>, props: ViewProps<'a, bool>) -> View<G> {
let signal = create_signal(cx, props.state.get());
view! { cx, view! { cx,
input(type="checkbox", checked=*signal.get().as_ref().clone(), disabled=true) input(type="checkbox", checked=*props.state, disabled=true)
} }
} }
} }
@ -128,8 +127,7 @@ where
view! { cx, view! { cx,
//Block(title="List".into()) { //Block(title="List".into()) {
div { div {
(View::new_fragment(props.state.get().as_ref().clone().into_iter().map(|x| { (View::new_fragment(props.state.clone().into_iter().map(|x| create_ref(cx, x)).map(|x| {
let x = create_signal(cx, x);
div().c(x.view(cx)).c(br()).view(cx) div().c(x.view(cx)).c(br()).view(cx)
}).collect())) }).collect()))
} }
@ -173,12 +171,10 @@ impl<'a, G: Html, A: for<'b> Viewable<'b, G> + Clone, B: for<'b> Viewable<'b, G>
Viewer<'a, G, (A, B)> for TupleView Viewer<'a, G, (A, B)> for TupleView
{ {
fn view(cx: Scope<'a>, props: ViewProps<'a, (A, B)>) -> View<G> { fn view(cx: Scope<'a>, props: ViewProps<'a, (A, B)>) -> View<G> {
let a = create_signal(cx, props.state.get().as_ref().clone().0);
let b = create_signal(cx, props.state.get().as_ref().clone().1);
view! { cx, view! { cx,
Block(title="Tuple".into()) { Block(title="Tuple".into()) {
(a.view(cx)) (props.state.0.view(cx))
(b.view(cx)) (props.state.1.view(cx))
} }
} }
} }
@ -197,8 +193,8 @@ pub struct OptionView;
impl<'a, G: Html, T: for<'b> Viewable<'b, G> + Clone> Viewer<'a, G, Option<T>> for OptionView { impl<'a, G: Html, T: for<'b> Viewable<'b, G> + Clone> Viewer<'a, G, Option<T>> for OptionView {
fn view(cx: Scope<'a>, props: ViewProps<'a, Option<T>>) -> View<G> { fn view(cx: Scope<'a>, props: ViewProps<'a, Option<T>>) -> View<G> {
match props.state.get().as_ref().clone() { match props.state {
Some(x) => view! { cx, (create_signal(cx, x.clone()).view(cx)) }, Some(x) => view! { cx, (x.view(cx)) },
None => view! { cx, "None" }, None => view! { cx, "None" },
} }
} }

View File

@ -147,7 +147,7 @@ fn struct_edit(props: &ItemProps, s: DataStruct) -> TokenStream2 {
view! { cx, view! { cx,
Block(title=#title.to_string()) { Block(title=#title.to_string()) {
p { p(class="description") {
#description #description
} }
#(#fields_view)* #(#fields_view)*
@ -264,7 +264,7 @@ fn enum_edit(props: &ItemProps, e: DataEnum) -> TokenStream2 {
view! { cx, view! { cx,
Block(title=#title.to_string()) { Block(title=#title.to_string()) {
p { p(class="description") {
#description #description
} }
select(bind:value=selected) { select(bind:value=selected) {
@ -307,10 +307,10 @@ fn struct_view(props: &ItemProps, s: DataStruct) -> TokenStream2 {
} }
}); });
let signals = fields.clone().map(|f| { let refs = fields.clone().map(|f| {
let name = f.name; let name = f.name;
quote! { quote! {
let #name = create_signal(cx, state.get().#name.clone()); let #name = create_ref(cx, state.#name.clone());
} }
}); });
@ -337,11 +337,11 @@ fn struct_view(props: &ItemProps, s: DataStruct) -> TokenStream2 {
quote! { quote! {
let state = props.state; let state = props.state;
#(#signals)* #(#refs)*
view! { cx, view! { cx,
Block(title=#title.to_string()) { Block(title=#title.to_string()) {
p { p(class="description") {
#description #description
} }
#(#fields_view)* #(#fields_view)*
@ -380,7 +380,7 @@ fn enum_view(props: &ItemProps, e: DataEnum) -> TokenStream2 {
let variant = v.variant; let variant = v.variant;
quote! { quote! {
#name::#variant(x) => create_signal(cx, x).view(cx) #name::#variant(x) => x.view(cx)
} }
}); });
@ -406,14 +406,14 @@ fn enum_view(props: &ItemProps, e: DataEnum) -> TokenStream2 {
view! { cx, view! { cx,
Block(title=#title.to_string()) { Block(title=#title.to_string()) {
p { p(class="description") {
#description #description
} }
(match state.get().as_ref().clone() { (match state {
#(#view_description,)* #(#view_description,)*
_ => view! { cx, } _ => view! { cx, }
}) })
(match state.get().as_ref().clone() { (match state {
#(#view_match,)* #(#view_match,)*
_ => view! { cx, } _ => view! { cx, }
}) })

8
web/dist/index.html vendored
View File

@ -2,11 +2,11 @@
<meta charset="utf-8"> <meta charset="utf-8">
<!--<link data-trunk href="tailwind.css" rel="css">--> <!--<link data-trunk href="tailwind.css" rel="css">-->
<link rel="stylesheet" href="/simple.min-d15f5ff500b4c62a.css"> <link rel="stylesheet" href="/simple.min-d15f5ff500b4c62a.css">
<link rel="stylesheet" href="/style-6e929d2286e0d817.css"> <link rel="stylesheet" href="/style-5e1a36be9e479fbe.css">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet">
<title>LAN Party</title> <title>LAN Party</title>
<link rel="preload" href="/index-16cdb8c780ec2870_bg.wasm" as="fetch" type="application/wasm" crossorigin=""> <link rel="preload" href="/index-61979c95126900f4_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
<link rel="modulepreload" href="/index-16cdb8c780ec2870.js"></head> <link rel="modulepreload" href="/index-61979c95126900f4.js"></head>
<body> <body>
<script type="module">import init from '/index-16cdb8c780ec2870.js';init('/index-16cdb8c780ec2870_bg.wasm');</script></body></html> <script type="module">import init from '/index-61979c95126900f4.js';init('/index-61979c95126900f4_bg.wasm');</script></body></html>

View File

@ -17,18 +17,15 @@ pub fn EventsPage<'a, G: Html>(cx: Scope<'a>) -> View<G> {
let event_spec = create_signal(cx, EventSpec::default()); let event_spec = create_signal(cx, EventSpec::default());
let event_update = create_signal(cx, EventUpdate::default()); let event_update = create_signal(cx, EventUpdate::default());
let events = create_signal(cx, None); let events: &'a Signal<Vec<Event>> = create_signal(cx, Vec::<Event>::new());
let test_event = create_signal(cx, None);
spawn_local_scoped(cx, async move { spawn_local_scoped(cx, async move {
events.set(Some( events.set(
api_request::<_, Vec<Event>>(Method::GET, "/event", Option::<()>::None) api_request::<_, Vec<Event>>(Method::GET, "/event", Option::<()>::None)
.await .await
.map(|inner| inner.unwrap()) .map(|inner| inner.unwrap())
.unwrap(), .unwrap(),
)); );
test_event.set(Some(events.get().unwrap().get(0).unwrap().clone()));
debug!("{:#?}", test_event);
}); });
let onadd = move |_| { let onadd = move |_| {
@ -41,17 +38,25 @@ pub fn EventsPage<'a, G: Html>(cx: Scope<'a>) -> View<G> {
.await .await
.unwrap(); .unwrap();
debug!("{:#?}", new_event); debug!("{:#?}", new_event);
let mut cloned = (*events).get().as_ref().clone(); events.modify().push(new_event.unwrap());
cloned.unwrap().push(new_event.unwrap());
events.set(cloned);
}); });
}; };
view! { cx, view! { cx,
Block(title="Events".into()) { Block(title="Events".into()) {
(test_event.view(cx)) Keyed(
(events.view(cx)) iterable=&events,
view=move |cx, event| {
let event = create_ref(cx, event);
view! { cx,
(event.view(cx))
br()
}
},
key=|event| (event.name.clone()),
)
} }
br()
Block(title="Create new event".into()) { Block(title="Create new event".into()) {
(event_spec.edit(cx)) (event_spec.edit(cx))
Button(icon="mdi-check".into(), onclick=onadd) Button(icon="mdi-check".into(), onclick=onadd)

View File

@ -93,9 +93,7 @@ pub fn UsersPage<'a, G: Html>(cx: Scope<'a>) -> View<G> {
) )
.await .await
.unwrap(); .unwrap();
let mut cloned = (*users).get().as_ref().clone(); users.modify().push(user.unwrap());
cloned.push(user.unwrap());
users.set(cloned);
}); });
}; };

View File

@ -62,3 +62,7 @@ textarea:focus, input:focus{
font-size: 0.9rem; font-size: 0.9rem;
font-style: italic; font-style: italic;
} }
body {
grid-template-columns: 1fr min(60rem, 90%) 1fr;
}