diff --git a/crates/frontend/src/components/create_milestone.rs b/crates/frontend/src/components/create_milestone.rs index 3ecabe9..7b0f8f8 100644 --- a/crates/frontend/src/components/create_milestone.rs +++ b/crates/frontend/src/components/create_milestone.rs @@ -1,127 +1,113 @@ -use super::error::error_provider::get_error_context; +use crate::components::error::error_provider::ErrorContext; use crate::services::rest::RestService; -use crate::services::rest::RestServiceError; use common::CreateMilestone; +use std::rc::Rc; use wasm_bindgen::JsCast; use wasm_bindgen_futures::spawn_local; use web_sys::HtmlInputElement; use yew::html; +use yew::prelude::*; use yew::Callback; -use yew::Component; use yew::Html; -use yew::NodeRef; -use yew_router::scope_ext::RouterScopeExt; +use yew_router::prelude::use_navigator; -#[derive(Debug)] -pub enum Msg { - Submit, - SubmitResult(Result<(), RestServiceError>), - UpdateInput(String), -} +#[derive(Properties, PartialEq)] +pub struct Props; -#[derive(Default)] -pub struct CreateMilestoneComponent { - input_value: String, - input_ref: NodeRef, +#[function_component] +pub fn CreateMilestoneComponent(_: &Props) -> Html { + let nav = use_navigator().expect("cannot get navigator"); + let err_ctx = Rc::new(use_context::()); - /// Submitted and awaiting response - awaiting_response: bool, -} -impl Component for CreateMilestoneComponent { - type Message = Msg; - type Properties = (); - - fn create(_ctx: &yew::Context) -> Self { - Self::default() - } - - fn rendered(&mut self, _ctx: &yew::Context, first_render: bool) { - if first_render { - if let Some(input_element) = self.input_ref.get() { + let input_ref = use_node_ref(); + // Focus input after it is rendered + { + let input_ref = input_ref.clone(); + use_effect(move || { + if let Some(input_element) = input_ref.get() { let input_element = input_element .dyn_into::() .expect("Failed to cast input element"); input_element.focus().expect("Failed to focus input"); } - } + + // effect destructor + || {} + }); } - fn update(&mut self, ctx: &yew::Context, msg: Self::Message) -> bool { - match msg { - Msg::Submit => { - log::info!("Creating milestone"); - let Ok(goal) = self.input_value.parse::() else { return false; }; + let input_value = use_state(String::default); + let awaiting_response = use_state(|| 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.awaiting_response = true; - - true - } - Msg::SubmitResult(result) => { - match result { - Ok(_response) => { - let nav = ctx.link().navigator().unwrap(); - nav.push(&crate::Route::Admin) - } - Err(err) => { - if let Some(err_ctx) = get_error_context(ctx) { - err_ctx.dispatch(err.to_string()); - } - } - }; - self.awaiting_response = false; - true - } - Msg::UpdateInput(value) => { - self.input_value = value; - true - } - } - } - - fn view(&self, ctx: &yew::Context) -> Html { - let link = ctx.link().clone(); - let nav = ctx.link().navigator().unwrap(); - - let onclick_go_back = Callback::from(move |_: web_sys::MouseEvent| { + let onclick_go_back = { + let nav = nav.clone(); + Callback::from(move |_: web_sys::MouseEvent| { nav.push(&crate::Route::Admin); - }); + }) + }; - let onsubmit = link.callback(|e: web_sys::SubmitEvent| { + let onsubmit = { + let input_value = input_value.clone(); + let awaiting_response = awaiting_response.clone(); + + Callback::from(move |e: yew::SubmitEvent| { e.prevent_default(); - Msg::Submit - }); - let oninput = link.callback(|e: web_sys::InputEvent| { + log::info!("Creating milestone"); + let Ok(goal) = input_value.parse::() else { + log::error!("Input parse error"); + return; + }; + + let goal = goal as usize; + let payload = CreateMilestone { goal }; + { + let nav = nav.clone(); + let err_ctx = Rc::clone(&err_ctx); + awaiting_response.set(true); + let awaiting_response = awaiting_response.clone(); + spawn_local(async move { + match RestService::create_milestone(payload).await { + Ok(_response) => nav.push(&crate::Route::Admin), + Err(err) => { + if let Some(err_ctx) = &*err_ctx { + err_ctx.dispatch(err.to_string()); + } + } + }; + awaiting_response.set(false); + }); + } + }) + }; + + let oninput = { + let input_value = input_value.clone(); + + Callback::from(move |e: web_sys::InputEvent| { let Some(input) = e .target() .and_then(|t| t.dyn_into::().ok()) else { unreachable!() }; - Msg::UpdateInput(input.value()) - }); + input_value.set(input.value()); + }) + }; - html! { - <> -
- + html! { + <> +
+ +
+ +
+
+
+
+ +
- - -
-
-
- - -
-
- - - - } +
+ + + } } diff --git a/todo.md b/todo.md index d79d6be..a247217 100644 --- a/todo.md +++ b/todo.md @@ -1,3 +1 @@ -- UI errors from failed requests - websocket reconnect -- make it an admin interface, and move the new + remove buttons there, as well as milestone management