create achievements

This commit is contained in:
Asger Juul Brunshøj 2023-06-11 23:28:17 +02:00
parent e12d616436
commit ddd039fead
9 changed files with 169 additions and 13 deletions

2
Cargo.lock generated
View File

@ -234,8 +234,10 @@ dependencies = [
"reqwasm", "reqwasm",
"serde", "serde",
"serde_json", "serde_json",
"wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-logger", "wasm-logger",
"web-sys",
"yew", "yew",
"yew-agent", "yew-agent",
"yew-router", "yew-router",

View File

@ -140,7 +140,7 @@ async fn ws_handler(
} else { } else {
String::from("Unknown browser") String::from("Unknown browser")
}; };
tracing::debug!("{user_agent} connected websocket."); tracing::debug!("New ws connection from: {user_agent}.");
ws.on_upgrade(move |socket| handle_socket(socket, state_watch_rx)) ws.on_upgrade(move |socket| handle_socket(socket, state_watch_rx))
} }

View File

@ -16,3 +16,5 @@ reqwasm = "0.5.0"
yew-agent = "0.2.0" yew-agent = "0.2.0"
serde.workspace = true serde.workspace = true
serde_json = "1.0.96" serde_json = "1.0.96"
web-sys = "0.3.63"
wasm-bindgen = "0.2.86"

View File

@ -1,7 +1,7 @@
use crate::Route;
use yew::functional::*; use yew::functional::*;
use yew::prelude::*; use yew::prelude::*;
use yew_router::prelude::*;
#[function_component(Admin)] #[function_component(Admin)]
pub fn admin() -> Html { pub fn admin() -> Html {

View File

@ -0,0 +1,139 @@
use common::CreateAchievement;
use reqwasm::http::Request;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use yew::html;
use yew::Callback;
use yew::Component;
use yew::Html;
use yew_router::scope_ext::RouterScopeExt;
#[derive(Debug)]
pub enum Msg {
Submit,
Reset,
UpdateInput(String),
}
#[derive(Default)]
pub struct CreateAchievementComponent {
input_value: String,
submitted: bool,
}
impl Component for CreateAchievementComponent {
type Message = Msg;
type Properties = ();
fn create(_ctx: &yew::Context<Self>) -> Self {
Self::default()
}
fn update(&mut self, _ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Reset => {
self.input_value.clear();
self.submitted = false;
true
}
Msg::Submit => {
log::info!("button click, creating achievement");
let payload = CreateAchievement {
goal: self.input_value.clone(),
};
let payload = serde_json::to_string(&payload).unwrap();
spawn_local(async move {
let req = Request::post("http://127.0.0.1:4000/create")
.header("Content-Type", "application/json")
.body(payload);
let res = req.send().await;
match res {
Ok(response) => {
dbg!(response);
}
Err(err) => {
log::error!("Request failed: {err:?}");
}
}
});
self.submitted = true;
true
}
Msg::UpdateInput(value) => {
self.input_value = value;
true
}
}
}
fn view(&self, ctx: &yew::Context<Self>) -> Html {
let link = ctx.link().clone();
let nav = ctx.link().navigator().unwrap();
if self.submitted {
let onclick_go_back = Callback::from(move |_: web_sys::MouseEvent| {
nav.push(&crate::Route::Root);
});
let onclick_add_another = link.callback(|_: web_sys::MouseEvent| Msg::Reset);
html! {
<>
<div class="row" style="margin-top: 15%">
<div class="twelve columns">
<h4>{"Submitted!"}</h4>
</div>
</div>
<div class="row">
<div class="six columns">
<button onclick={onclick_go_back} class="button">{"Go back"}</button>
</div>
<div class="six columns">
<button onclick={onclick_add_another} class="button">{"Add another"}</button>
</div>
</div>
</>
}
} else {
let onclick_go_back = Callback::from(move |_: web_sys::MouseEvent| {
nav.push(&crate::Route::Root);
});
let onsubmit = link.callback(|e: web_sys::SubmitEvent| {
e.prevent_default();
Msg::Submit
});
let oninput = link.callback(|e: web_sys::InputEvent| {
let Some(input) = e
.target()
.and_then(|t| t.dyn_into::<web_sys::HtmlInputElement>().ok()) else { unreachable!() };
Msg::UpdateInput(input.value())
});
let input_value = self.input_value.clone();
html! {
<>
<div class="row">
<button onclick={onclick_go_back} class="button">{"Back"}</button>
</div>
<form {onsubmit} >
<hr />
<div class="row">
<div class="twelve columns">
<label for="achievementInput">{"New achievement"}</label>
<input {oninput} value={input_value} class="u-full-width" type="text" id="achievementInput" />
</div>
</div>
<input class="button-primary" type="submit" value="Submit" />
</form>
</>
}
}
}
}

View File

@ -1,3 +1,4 @@
pub mod achievement; pub mod achievement;
pub mod admin; pub mod admin;
pub mod create_achievement;
pub mod root; pub mod root;

View File

@ -5,6 +5,7 @@ use std::rc::Rc;
use yew::prelude::*; use yew::prelude::*;
use yew_agent::Bridge; use yew_agent::Bridge;
use yew_agent::Bridged; use yew_agent::Bridged;
use yew_router::scope_ext::RouterScopeExt;
pub struct Root { pub struct Root {
wss: WebsocketService, wss: WebsocketService,
@ -48,6 +49,13 @@ impl Component for Root {
} }
fn view(&self, ctx: &Context<Self>) -> Html { fn view(&self, ctx: &Context<Self>) -> Html {
let link = ctx.link().clone();
let nav = ctx.link().navigator().unwrap();
let onclick_create_achievement = Callback::from(move |_: web_sys::MouseEvent| {
nav.push(&crate::Route::CreateAchievement);
});
let Some(state) = &self.state else { let Some(state) = &self.state else {
return html! { return html! {
<p>{"loading..."}</p> <p>{"loading..."}</p>
@ -66,15 +74,15 @@ impl Component for Root {
.collect::<Html>(); .collect::<Html>();
html! { html! {
<div class="container"> <>
<div class="row"> <h4>{"Achievements"}</h4>
<div class="twelve columns" style="margin-top: 25%"> <hr />
<h4>{"Achievements"}</h4>
</div>
</div>
{achievements} {achievements}
</div>
<hr />
<button onclick={onclick_create_achievement} class="button-primary">{"Create new achievement"}</button>
</>
} }
} }
} }

View File

@ -30,7 +30,7 @@ impl yew_agent::Worker for EventBus {
fn update(&mut self, _msg: Self::Message) {} fn update(&mut self, _msg: Self::Message) {}
fn handle_input(&mut self, msg: Self::Input, id: HandlerId) { fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) {
match msg { match msg {
EventBusInput::EventBusMsg(s) => { EventBusInput::EventBusMsg(s) => {
for sub in &self.subscribers { for sub in &self.subscribers {

View File

@ -1,4 +1,5 @@
use components::admin::Admin; use components::admin::Admin;
use components::create_achievement::CreateAchievementComponent;
use components::root::Root; use components::root::Root;
use std::rc::Rc; use std::rc::Rc;
use yew::prelude::*; use yew::prelude::*;
@ -16,6 +17,8 @@ enum Route {
Root, Root,
#[at("/chat")] #[at("/chat")]
Admin, Admin,
#[at("/create-achievement")]
CreateAchievement,
#[not_found] #[not_found]
#[at("/404")] #[at("/404")]
NotFound, NotFound,
@ -25,6 +28,7 @@ fn switch(selected_route: Route) -> Html {
match selected_route { match selected_route {
Route::Root => html! {<Root />}, Route::Root => html! {<Root />},
Route::Admin => html! {<Admin/>}, Route::Admin => html! {<Admin/>},
Route::CreateAchievement => html! {<CreateAchievementComponent/>},
Route::NotFound => html! {<h1>{"404 not found"}</h1>}, Route::NotFound => html! {<h1>{"404 not found"}</h1>},
} }
} }
@ -42,7 +46,7 @@ pub fn App() -> Html {
html! { html! {
<ContextProvider<AppState> context={(*ctx).clone()}> <ContextProvider<AppState> context={(*ctx).clone()}>
<BrowserRouter> <BrowserRouter>
<div class="flex w-screen h-screen"> <div class="container" style="margin-top: 5%; margin-bottom: 25%">
<Switch<Route> render={switch}/> <Switch<Route> render={switch}/>
</div> </div>
</BrowserRouter> </BrowserRouter>