achievements/crates/frontend/src/components/create_milestone.rs

149 lines
4.9 KiB
Rust

use super::error::error_provider::ErrorContext;
use crate::services::rest::RestService;
use crate::services::rest::RestServiceError;
use common::CreateMilestone;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use web_sys::HtmlInputElement;
use yew::html;
use yew::Callback;
use yew::Component;
use yew::Html;
use yew::NodeRef;
use yew_router::scope_ext::RouterScopeExt;
#[derive(Debug)]
pub enum Msg {
Submit,
SubmitResult(Result<(), RestServiceError>),
UpdateInput(String),
}
#[derive(Default)]
pub struct CreateMilestoneComponent {
input_value: String,
submitted: bool,
input_ref: NodeRef,
}
impl Component for CreateMilestoneComponent {
type Message = Msg;
type Properties = ();
fn create(_ctx: &yew::Context<Self>) -> Self {
Self::default()
}
fn rendered(&mut self, _ctx: &yew::Context<Self>, first_render: bool) {
if first_render {
if let Some(input_element) = self.input_ref.get() {
let input_element = input_element
.dyn_into::<HtmlInputElement>()
.expect("Failed to cast input element");
input_element.focus().expect("Failed to focus input");
}
}
}
fn update(&mut self, ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Submit => {
log::info!("Creating achievement");
let Ok(goal) = self.input_value.parse::<f64>() else { return false; };
let goal = goal as usize;
let payload = CreateMilestone { goal };
let link = ctx.link().clone();
spawn_local(async move {
let res = RestService::create_milestone(payload).await;
link.send_message(Msg::SubmitResult(res));
});
self.submitted = true;
true
}
Msg::SubmitResult(result) => {
match result {
Ok(_response) => {
let nav = ctx.link().navigator().unwrap();
nav.push(&crate::Route::Root);
}
Err(err) => {
let (error_context, _context_listener) = ctx
.link()
.context(Callback::from(|_: ErrorContext| ()))
.expect("No Error Context Provided");
log::info!("dispatching error");
error_context.dispatch(err.to_string());
}
};
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);
});
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>
</>
}
} 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())
});
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="milestoneInput">{"New milestone"}</label>
<input ref={self.input_ref.clone()} {oninput} value={self.input_value.to_string()} class="u-full-width" type="number" id="milestoneInput" />
</div>
</div>
<input class="button-primary" type="submit" value="Submit" />
</form>
</>
}
}
}
}