diff --git a/app/src/main.rs b/app/src/main.rs index 4706ad4..732afde 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -9,7 +9,8 @@ use yew::{prelude::*, suspense::use_future}; use yew_router::prelude::*; use api::{ - CreateHouseholdRequest, CreateHouseholdResponse, Household, LoginRequest, LoginResponse, + AddToHouseholdRequest, CreateHouseholdRequest, CreateHouseholdResponse, Household, + LoginRequest, LoginResponse, UserInfo, }; use crate::{ @@ -180,8 +181,6 @@ fn HouseholdListSelect() -> HtmlResult { let name = info.name.clone(); let nav = navigator.clone(); let onclick = Callback::from(move |_| { - log::info!("Clicked {id}"); - if let Err(e) = LocalStorage::set( "household", HouseholdInfo { @@ -222,21 +221,61 @@ fn HouseholdListSelect() -> HtmlResult { }) } +#[derive(Properties, PartialEq)] +struct CreateHouseholdProps { + token: String, +} + +async fn do_resolve_user(token: String, username: String) -> anyhow::Result> { + let rsp = gloo_net::http::Request::get(api!("search/user/{username}")) + .header("Authorization", &format!("Bearer {token}")) + .send() + .await?; + + if rsp.status() == 404 { + return Ok(None); + } + + if !rsp.ok() { + anyhow::bail!("Request failed: {rsp:?}") + } + + let rsp: UserInfo = rsp.json().await?; + + Ok(Some(rsp.id)) +} + +pub async fn do_add_user_to_household( + token: String, + household: Uuid, + user: Uuid, +) -> anyhow::Result<()> { + let rsp = gloo_net::http::Request::put(api!("household/{household}")) + .json(&AddToHouseholdRequest { user })? + .header("Authorization", &format!("Bearer {token}")) + .send() + .await?; + + if !rsp.ok() { + anyhow::bail!("Request failed: {rsp:?}") + } + + Ok(()) +} + #[function_component] -fn HouseholdSelection() -> Html { - let token = use_state(|| match LocalStorage::get::("token") { - Ok(v) => Some(v), - Err(StorageError::KeyNotFound(_)) => None, - Err(e) => unreachable!("Could not get household: {e:?}"), - }); +fn CreateHousehold(props: &CreateHouseholdProps) -> Html { let error = use_state(|| None::); let navigator = use_navigator().unwrap(); + let members = use_state(Vec::<(Uuid, String)>::new); + let err = error.clone(); - let tok = token.clone(); + let tok = props.token.clone(); + let mem = members.clone(); let on_submit = Callback::from(move |()| { let document = gloo_utils::document(); - let token = tok.as_ref().unwrap().to_owned(); + let token = tok.clone(); let name: HtmlInputElement = document .get_element_by_id("newHsName") @@ -245,13 +284,23 @@ fn HouseholdSelection() -> Html { .expect("newHsName is not an input element"); let name = name.value(); + let members = mem.clone(); let err = err.clone(); let navigator = navigator.clone(); wasm_bindgen_futures::spawn_local(async move { - match do_new_household(token.token, name.clone()).await { + match do_new_household(token.clone(), name.clone()).await { Ok(id) => { let household_info = HouseholdInfo { name, id }; + for (uid, user) in &*members { + if let Err(e) = do_add_user_to_household(token.clone(), id, *uid).await { + err.set(Some(format!( + "Could not add user {user} (but household was created): {e:?}" + ))); + return; + } + } + if let Err(e) = LocalStorage::set("household", household_info) { log::error!("Could not switch to new household: {e:?}") } @@ -261,6 +310,7 @@ fn HouseholdSelection() -> Html { navigator.push(&Route::Index); err.set(None); + members.set(Vec::new()); } Err(e) => { err.set(Some(format!("Could not create: {e:?}"))); @@ -269,6 +319,109 @@ fn HouseholdSelection() -> Html { }); }); + let err = error.clone(); + let tok = props.token.clone(); + let mem = members.clone(); + let add_member = Callback::from(move |_| { + let document = gloo_utils::document(); + + let username: HtmlInputElement = document + .get_element_by_id("newHsAddMember") + .unwrap() + .dyn_into() + .expect("newHsAddMember is not an input element"); + let username = username.value(); + + let tok = tok.clone(); + let err = err.clone(); + let members = mem.clone(); + + wasm_bindgen_futures::spawn_local(async move { + match do_resolve_user(tok.clone(), username.clone()).await { + Err(e) => { + err.set(Some(format!("Error adding a member: {e:?}"))); + } + Ok(None) => { + err.set(Some(format!("User '{username}' does not exist"))); + } + Ok(Some(id)) => { + let mut m = (*members).clone(); + m.push((id, username)); + members.set(m); + } + } + }); + }); + + let mem = members.clone(); + let remove_user = |idx| { + Callback::from(move |_| { + let mut m = (*mem).clone(); + m.remove(idx); + mem.set(m); + }) + }; + + html! { + + if let Some(err) = &*error { + + } +
+ + +
+

{"Additional Members"}

+
    + { + for members.iter().enumerate().map(move |(idx, (_, name))| html!{ +
  • + {name} + +
  • + }) + } +
+
+ + +
+
+ } +} + +#[function_component] +fn HouseholdSelection() -> Html { + let token = use_state(|| match LocalStorage::get::("token") { + Ok(v) => Some(v), + Err(StorageError::KeyNotFound(_)) => None, + Err(e) => unreachable!("Could not get household: {e:?}"), + }); + let fallback = html! { {"Loading..."} }; match &*token { @@ -290,27 +443,7 @@ fn HouseholdSelection() -> Html { > {"New household"} - - if let Some(err) = &*error { - - } -
- - -
-
+ }, }