I love macros!
This commit is contained in:
parent
277782f16b
commit
cdc056dadc
|
@ -4,9 +4,9 @@
|
||||||
<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>Yew App</title>
|
<title>Yew App</title>
|
||||||
|
|
||||||
<link rel="preload" href="/index-d56dfc869fea14a4_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
<link rel="preload" href="/index-b24e51ea036df368_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||||
<link rel="modulepreload" href="/index-d56dfc869fea14a4.js"></head>
|
<link rel="modulepreload" href="/index-b24e51ea036df368.js"></head>
|
||||||
<body class="base theme-dark bg-gray-900 text-gray-400">
|
<body class="base theme-dark bg-gray-900 text-gray-400">
|
||||||
|
|
||||||
|
|
||||||
<script type="module">import init from '/index-d56dfc869fea14a4.js';init('/index-d56dfc869fea14a4_bg.wasm');</script></body></html>
|
<script type="module">import init from '/index-b24e51ea036df368.js';init('/index-b24e51ea036df368_bg.wasm');</script></body></html>
|
|
@ -11,10 +11,21 @@ use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||||
use web_sys::{Event, HtmlInputElement, InputEvent};
|
use web_sys::{Event, HtmlInputElement, InputEvent};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Binding<T> {
|
||||||
|
pub onchange: Callback<T>,
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Binding<T> {
|
||||||
|
pub fn new(value: T, onchange: Callback<T>) -> Self {
|
||||||
|
Self { value, onchange }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct InputProps {
|
pub struct InputProps {
|
||||||
pub value: String,
|
pub bind: Binding<String>,
|
||||||
pub onchange: Callback<String>,
|
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +40,8 @@ fn get_value_from_input_event(e: InputEvent) -> String {
|
||||||
#[function_component(TextInput)]
|
#[function_component(TextInput)]
|
||||||
pub fn text_input(props: &InputProps) -> Html {
|
pub fn text_input(props: &InputProps) -> Html {
|
||||||
let InputProps {
|
let InputProps {
|
||||||
value,
|
|
||||||
onchange,
|
|
||||||
class,
|
class,
|
||||||
|
bind: Binding { onchange, value },
|
||||||
} = props.clone();
|
} = props.clone();
|
||||||
|
|
||||||
let oninput = Callback::from(move |input_event: InputEvent| {
|
let oninput = Callback::from(move |input_event: InputEvent| {
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::components::{Page, RenderEvent};
|
use crate::{
|
||||||
|
components::{Page, RenderEvent},
|
||||||
|
init,
|
||||||
|
};
|
||||||
|
use wasm_bindgen_futures::spawn_local;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_hooks::*;
|
use yew_hooks::*;
|
||||||
|
|
||||||
|
@ -6,25 +10,18 @@ use crate::{clone, util::api_request};
|
||||||
|
|
||||||
#[function_component(EventsPage)]
|
#[function_component(EventsPage)]
|
||||||
pub fn events_page() -> Html {
|
pub fn events_page() -> Html {
|
||||||
let events: UseAsyncHandle<Vec<lan_party_core::event::Event>, _> = use_async(async move {
|
let events = use_state(|| Vec::new());
|
||||||
api_request::<_, Vec<lan_party_core::event::Event>>("GET", "/event", Option::<()>::None)
|
|
||||||
|
init!(events => {
|
||||||
|
events.set(api_request::<_, Vec<lan_party_core::event::Event>>("GET", "/event", Option::<()>::None)
|
||||||
.await
|
.await
|
||||||
.map(|inner| inner.unwrap())
|
.map(|inner| inner.unwrap())
|
||||||
.map_err(|_| "failed to load users")
|
.unwrap())
|
||||||
});
|
});
|
||||||
|
|
||||||
clone!(events; use_effect_with_deps(move |_| {
|
|
||||||
if events.data.is_none() {
|
|
||||||
events.run();
|
|
||||||
}
|
|
||||||
|| ()
|
|
||||||
}, ()));
|
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<Page>
|
<Page>
|
||||||
{ if let Some(events) = &events.data {
|
{ events.view() }
|
||||||
events.view()
|
|
||||||
} else { html! {} }}
|
|
||||||
</Page>
|
</Page>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
clone, clone_cb,
|
bind, bind_change, bind_value, clone, clone_cb, clone_cb_spawn,
|
||||||
components::{Button, Loading, Page, Table, TextInput},
|
components::{Binding, Button, Loading, Page, Table, TextInput},
|
||||||
|
init,
|
||||||
util::api_request,
|
util::api_request,
|
||||||
};
|
};
|
||||||
use lan_party_core::user::User;
|
use lan_party_core::user::User;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
use yew_hooks::*;
|
||||||
|
|
||||||
#[function_component(UsersPage)]
|
#[function_component(UsersPage)]
|
||||||
pub fn users_page() -> Html {
|
pub fn users_page() -> Html {
|
||||||
|
@ -16,60 +18,45 @@ pub fn users_page() -> Html {
|
||||||
let current_score = use_state(|| String::new());
|
let current_score = use_state(|| String::new());
|
||||||
let users = use_state(|| Vec::new());
|
let users = use_state(|| Vec::new());
|
||||||
|
|
||||||
clone!(users; use_effect_with_deps(move |_| {
|
init!(users => {
|
||||||
spawn_local(async move {
|
|
||||||
users.set(api_request::<_, Vec<User>>("GET", "/user", Option::<()>::None)
|
users.set(api_request::<_, Vec<User>>("GET", "/user", Option::<()>::None)
|
||||||
.await
|
.await
|
||||||
.map(|inner| inner.unwrap())
|
.map(|inner| inner.unwrap())
|
||||||
.unwrap());
|
.unwrap());
|
||||||
});
|
});
|
||||||
|| ()
|
|
||||||
}, ()));
|
|
||||||
|
|
||||||
let oncheck = clone_cb!(score_edit, current_score, users; move |_| {
|
let oncheck = clone_cb_spawn!(score_edit, current_score, users => {
|
||||||
clone!(score_edit, users, current_score; {
|
if let (Some(score_edit), Ok(score)) = (*score_edit, current_score.parse()) {
|
||||||
spawn_local(async move {
|
|
||||||
if let Some(score_edit) = *score_edit {
|
|
||||||
if let Ok(score) = current_score.parse() {
|
|
||||||
let user: &User = &users[score_edit];
|
let user: &User = &users[score_edit];
|
||||||
api_request::<_, ()>("POST", &format!("/user/{}/score", user.name), Some(score)).await.unwrap();
|
api_request::<_, ()>("POST", &format!("/user/{}/score", user.name), Some(score))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
let mut cloned = (*users).clone();
|
let mut cloned = (*users).clone();
|
||||||
cloned[score_edit].score = score;
|
cloned[score_edit].score = score;
|
||||||
users.set(cloned);
|
users.set(cloned);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
score_edit.set(None);
|
score_edit.set(None);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let onedit = clone_cb!(current_score, score_edit, users; i => move |_| {
|
let onedit = clone_cb!(current_score, score_edit, users => i => move |_| {
|
||||||
let user: &User = &users[i];
|
let user: &User = &users[i];
|
||||||
current_score.set(user.score.to_string());
|
current_score.set(user.score.to_string());
|
||||||
score_edit.set(Some(i));
|
score_edit.set(Some(i));
|
||||||
});
|
});
|
||||||
|
|
||||||
let ondelete = clone_cb!(users; i => move |_| {
|
let ondelete = clone_cb_spawn!(users => i => {
|
||||||
clone!(users; {
|
|
||||||
spawn_local(async move {
|
|
||||||
let user: &User = &users[i];
|
let user: &User = &users[i];
|
||||||
api_request::<_, ()>("DELETE", &format!("/user/{}", user.name), Option::<()>::None).await.unwrap();
|
api_request::<_, ()>("DELETE", &format!("/user/{}", user.name), Option::<()>::None).await.unwrap();
|
||||||
let cloned = users.iter().cloned().filter(|u| u.name != user.name).collect();
|
let cloned = users.iter().cloned().filter(|u| u.name != user.name).collect();
|
||||||
users.set(cloned);
|
users.set(cloned);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let onadd = clone_cb!(new_username, users; move |_| {
|
let onadd = clone_cb_spawn!(new_username, users => {
|
||||||
clone!(new_username, users; {
|
|
||||||
spawn_local(async move {
|
|
||||||
let user = api_request::<String, User>("POST", "/user", Some((*new_username).clone())).await.unwrap();
|
let user = api_request::<String, User>("POST", "/user", Some((*new_username).clone())).await.unwrap();
|
||||||
let mut cloned = (*users).clone();
|
let mut cloned = (*users).clone();
|
||||||
cloned.push(user.unwrap());
|
cloned.push(user.unwrap());
|
||||||
users.set(cloned);
|
users.set(cloned);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<Page>
|
<Page>
|
||||||
|
@ -83,8 +70,7 @@ pub fn users_page() -> Html {
|
||||||
<span class="inline-block">
|
<span class="inline-block">
|
||||||
<TextInput
|
<TextInput
|
||||||
class={"mx-2 appearance-none block bg-gray-700 text-slate-400 border border-gray-600 rounded py-2 px-2 w-20 leading-tight focus:outline-none focus:ring-1 focus:ring-gray-500 focus:border-gray-500"}
|
class={"mx-2 appearance-none block bg-gray-700 text-slate-400 border border-gray-600 rounded py-2 px-2 w-20 leading-tight focus:outline-none focus:ring-1 focus:ring-gray-500 focus:border-gray-500"}
|
||||||
onchange={clone_cb!(current_score; move |value| current_score.set(value))}
|
bind={bind!(current_score)}
|
||||||
value={(*current_score).clone()}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<Button icon={"mdi-check"} onclick={oncheck.clone()} />
|
<Button icon={"mdi-check"} onclick={oncheck.clone()} />
|
||||||
|
@ -108,8 +94,7 @@ pub fn users_page() -> Html {
|
||||||
<span class="inline-block">
|
<span class="inline-block">
|
||||||
<TextInput
|
<TextInput
|
||||||
class={"mx-2 appearance-none block bg-gray-700 text-slate-400 border border-gray-600 rounded py-2 px-2 w-50 leading-tight focus:outline-none focus:ring-1 focus:ring-gray-500 focus:border-gray-500"}
|
class={"mx-2 appearance-none block bg-gray-700 text-slate-400 border border-gray-600 rounded py-2 px-2 w-50 leading-tight focus:outline-none focus:ring-1 focus:ring-gray-500 focus:border-gray-500"}
|
||||||
onchange={clone_cb!(new_username; move |value| new_username.set(value))}
|
bind={bind!(new_username)}
|
||||||
value={(*new_username).clone()}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -77,7 +77,7 @@ pub async fn api_request<B: Serialize, R: for<'a> Deserialize<'a>>(
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! clone {
|
macro_rules! clone {
|
||||||
($($var:ident),* $(,)? ; $body:expr) => {{
|
($($var:ident),* $(,)? => $body:expr) => {{
|
||||||
$(let $var = $var.clone();)*
|
$(let $var = $var.clone();)*
|
||||||
$body
|
$body
|
||||||
}};
|
}};
|
||||||
|
@ -85,16 +85,58 @@ macro_rules! clone {
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! clone_cb {
|
macro_rules! clone_cb {
|
||||||
($($var:ident),* ; $body:expr) => {
|
($($var:ident),* $(,)? => $body:expr) => {
|
||||||
clone!($($var,)* ; {
|
clone!($($var,)* => {
|
||||||
Callback::from($body)
|
Callback::from($body)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($($var:ident),* ; $($param:ident),* => $body:expr) => {
|
($($var:ident),* $(,)? => $($param:ident),* $(,)? => $body:expr) => {
|
||||||
clone!($($var,)* ; {
|
clone!($($var,)* => {
|
||||||
move |$($param,)*| {
|
move |$($param,)*| {
|
||||||
Callback::from($body)
|
Callback::from($body)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! clone_cb_spawn {
|
||||||
|
($($var:ident),* => $body:expr) => {
|
||||||
|
clone_cb!($($var,)* => move |_| {
|
||||||
|
clone!($($var,)* => spawn_local(async move { $body }))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($($var:ident),* $(,)? => $($param:ident),* => $body:expr) => {
|
||||||
|
clone_cb!($($var,)* => $($param),* => move |_| {
|
||||||
|
clone!($($var,)* => spawn_local(async move { $body }))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! init {
|
||||||
|
($var:ident => $body:expr) => {
|
||||||
|
clone!($var => use_mount(move || spawn_local(async move { $body })))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bind_value {
|
||||||
|
($value:ident) => {
|
||||||
|
(*$value).clone()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bind_change {
|
||||||
|
($value:ident) => {
|
||||||
|
clone_cb!($value => move |value| $value.set(value))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bind {
|
||||||
|
($value:ident) => {
|
||||||
|
Binding::new(bind_value!($value), bind_change!($value))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue