From f49205a2c6a949e95557ac984ef1fb9b018d9745 Mon Sep 17 00:00:00 2001 From: antifallobst Date: Wed, 11 Oct 2023 17:34:33 +0200 Subject: [PATCH] feat: added client side signup/verify data validation --- src/backend/data.rs | 1 + src/backend/mod.rs | 3 + src/topbar/guest/mod.rs | 3 +- src/topbar/guest/signup.rs | 109 +++++++++++++++++++++++++++---------- style/topbar.css | 15 +++++ 5 files changed, 99 insertions(+), 32 deletions(-) diff --git a/src/backend/data.rs b/src/backend/data.rs index d527e58..7c77b56 100644 --- a/src/backend/data.rs +++ b/src/backend/data.rs @@ -30,6 +30,7 @@ impl Display for AccountRegisterConflictResponse { #[serde(tag = "problem", rename_all = "snake_case")] pub enum AccountRegisterCriteriaProblemResponse { Email, + Username, Password, } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 156ba68..39ff3ec 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -125,6 +125,9 @@ impl Session { data::AccountRegisterCriteriaProblemResponse::Email => { Err(Error::msg(format!("Not a valid email!"))) } + data::AccountRegisterCriteriaProblemResponse::Username => Err(Error::msg( + format!("The username has to be alphanumerical!"), + )), data::AccountRegisterCriteriaProblemResponse::Password => Err(Error::msg( format!("The password does not meet the criteria!"), )), diff --git a/src/topbar/guest/mod.rs b/src/topbar/guest/mod.rs index d28da67..e6c7bc0 100644 --- a/src/topbar/guest/mod.rs +++ b/src/topbar/guest/mod.rs @@ -2,7 +2,6 @@ mod signin; mod signup; use crate::topbar::Tab; -use anyhow::Result; use signin::SignIn; use signup::SignUp; use std::rc::Rc; @@ -65,7 +64,7 @@ pub fn TopBar() -> Html { let state = state.clone(); Callback::from(move |_| state.dispatch(Action::ToggleSignUp)) }; - + let close_auth_callback = { let state = state.clone(); Callback::from(move |_| state.dispatch(Action::CloseAuth)) diff --git a/src/topbar/guest/signup.rs b/src/topbar/guest/signup.rs index 9907fe6..32e63bf 100644 --- a/src/topbar/guest/signup.rs +++ b/src/topbar/guest/signup.rs @@ -1,6 +1,6 @@ use crate::backend::Session; use crate::Callbacks; -use anyhow::{Error, Result}; +use anyhow::Result; use std::ops::Deref; use wasm_bindgen::JsCast; use web_sys::HtmlInputElement; @@ -27,11 +27,50 @@ struct Data { pub username: String, pub email: String, pub password: String, - pub password_reenter: String, + pub password_confirm: String, pub token: String, } +impl Data { + fn confirm_password(&self) { + let document = web_sys::window() + .expect("Global window does not exist!") + .document() + .expect("Expecting a document on window!"); + + if self.password != self.password_confirm { + document + .get_element_by_id("signup-password") + .unwrap() + .dyn_into::() + .unwrap() + .set_custom_validity("The passwords don't match!"); + + document + .get_element_by_id("signup-password-confirm") + .unwrap() + .dyn_into::() + .unwrap() + .set_custom_validity("The passwords don't match!"); + } else { + document + .get_element_by_id("signup-password") + .unwrap() + .dyn_into::() + .unwrap() + .set_custom_validity(""); + + document + .get_element_by_id("signup-password-confirm") + .unwrap() + .dyn_into::() + .unwrap() + .set_custom_validity(""); + } + } +} + #[derive(Properties, PartialEq)] pub struct Props { pub close: Callback<()>, @@ -47,7 +86,7 @@ pub fn SignUp(props: &Props) -> Html { let on_username_changed = Callback::from(move |event: Event| { let username = event .target() - .expect("WTF") + .expect("Expected event target!") .unchecked_into::() .value(); @@ -60,46 +99,48 @@ pub fn SignUp(props: &Props) -> Html { let on_email_changed = Callback::from(move |event: Event| { let email = event .target() - .expect("WTF") + .expect("Expected event target!") .unchecked_into::() .value(); - let mut data = cloned_state.deref().clone(); - data.email = email; - cloned_state.set(data); + let mut state = cloned_state.deref().clone(); + state.email = email; + cloned_state.set(state); }); let cloned_state = state.clone(); - let on_password_changed = Callback::from(move |event: Event| { + let on_password_input = Callback::from(move |event: InputEvent| { let password = event .target() - .expect("WTF") - .unchecked_into::() - .value(); + .expect("Expected event target!") + .unchecked_into::(); - let mut data = cloned_state.deref().clone(); - data.password = password; - cloned_state.set(data); + let mut state = cloned_state.deref().clone(); + state.password = password.value(); + state.confirm_password(); + cloned_state.set(state); }); let cloned_state = state.clone(); - let on_password_reenter_changed = Callback::from(move |event: Event| { - let password_reenter = event + let on_password_confirm_input = Callback::from(move |event: InputEvent| { + let password_confirm = event .target() - .expect("WTF") - .unchecked_into::() - .value(); + .expect("Expected event target!") + .unchecked_into::(); - let mut data = cloned_state.deref().clone(); - data.password_reenter = password_reenter; - cloned_state.set(data); + let mut state = cloned_state.deref().clone(); + state.password_confirm = password_confirm.value(); + if password_confirm.check_validity() { + state.confirm_password(); + } + cloned_state.set(state); }); let cloned_state = state.clone(); let on_token_changed = Callback::from(move |event: Event| { let token = event .target() - .expect("WTF") + .expect("Expected event target!") .unchecked_into::() .value(); @@ -171,22 +212,29 @@ pub fn SignUp(props: &Props) -> Html {

-
+
+
{"The username has to be alphanumeric."}

-
+ +
{"Enter a valid email."}

-
-
+
+
-
-
+
+
+
{"A password must contain uppercase, lowercase, numbers and special characters."}
if !state.loading { @@ -218,7 +266,8 @@ pub fn SignUp(props: &Props) -> Html {

-
+
if !state.loading { diff --git a/style/topbar.css b/style/topbar.css index eaeaf89..737bec8 100644 --- a/style/topbar.css +++ b/style/topbar.css @@ -107,6 +107,21 @@ outline: none; } +.auth-input:valid { + border-color: limegreen; +} + +.auth-input-tooltip { + display: none; + color: red; +} + +/* +.auth-input:invalid ~ .auth-input-tooltip { + display: block; +} +*/ + .auth-status { font: normal 14px Roboto; color: #ff0000;