feat: added client side signup/verify data validation
This commit is contained in:
parent
ca7ed256f8
commit
f49205a2c6
|
@ -30,6 +30,7 @@ impl Display for AccountRegisterConflictResponse {
|
|||
#[serde(tag = "problem", rename_all = "snake_case")]
|
||||
pub enum AccountRegisterCriteriaProblemResponse {
|
||||
Email,
|
||||
Username,
|
||||
Password,
|
||||
}
|
||||
|
||||
|
|
|
@ -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!"),
|
||||
)),
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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::<HtmlInputElement>()
|
||||
.unwrap()
|
||||
.set_custom_validity("The passwords don't match!");
|
||||
|
||||
document
|
||||
.get_element_by_id("signup-password-confirm")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap()
|
||||
.set_custom_validity("The passwords don't match!");
|
||||
} else {
|
||||
document
|
||||
.get_element_by_id("signup-password")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.unwrap()
|
||||
.set_custom_validity("");
|
||||
|
||||
document
|
||||
.get_element_by_id("signup-password-confirm")
|
||||
.unwrap()
|
||||
.dyn_into::<HtmlInputElement>()
|
||||
.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::<HtmlInputElement>()
|
||||
.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::<HtmlInputElement>()
|
||||
.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::<HtmlInputElement>()
|
||||
.value();
|
||||
.expect("Expected event target!")
|
||||
.unchecked_into::<HtmlInputElement>();
|
||||
|
||||
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::<HtmlInputElement>()
|
||||
.value();
|
||||
.expect("Expected event target!")
|
||||
.unchecked_into::<HtmlInputElement>();
|
||||
|
||||
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::<HtmlInputElement>()
|
||||
.value();
|
||||
|
||||
|
@ -171,22 +212,29 @@ pub fn SignUp(props: &Props) -> Html {
|
|||
<form class="auth-container" onsubmit={onsubmit}>
|
||||
<div class="auth-field">
|
||||
<label for="username">{"Username"}</label><br/>
|
||||
<input class="auth-input" type="text" name="username" onchange={on_username_changed}/><br/>
|
||||
<input class="auth-input" type="text" name="username" onchange={on_username_changed}
|
||||
required=true pattern="^[a-zA-Z0-9]+$" /><br/>
|
||||
<div class="auth-input-tooltip">{"The username has to be alphanumeric."}</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-field">
|
||||
<label for="email">{"Email"}</label><br/>
|
||||
<input class="auth-input" type="text" name="email" onchange={on_email_changed}/><br/>
|
||||
<input class="auth-input" type="email" name="email" onchange={on_email_changed} required=true
|
||||
pattern=r"^([a-z0-9_+]([a-z0-9_+.]*[a-z0-9_+])?)@([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})"/>
|
||||
<div class="auth-input-tooltip">{"Enter a valid email."}</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-field">
|
||||
<label for="password">{"Password"}</label><br/>
|
||||
<input class="auth-input" type="password" name="password" onchange={on_password_changed}/><br/>
|
||||
</div>
|
||||
<input id="signup-password" class="auth-input" type="password" name="password" oninput={on_password_input} required=true
|
||||
pattern=r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E])[A-Za-z0-9\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]{12,}$" /><br/>
|
||||
</div>
|
||||
|
||||
<div class="auth-field">
|
||||
<label for="password-reenter">{"Password"}</label><br/>
|
||||
<input class="auth-input" type="password" name="password-reenter" onchange={on_password_reenter_changed}/><br/>
|
||||
<label for="password-confirm">{"Confirm Password"}</label><br/>
|
||||
<input id="signup-password-confirm" class="auth-input" type="password" name="password-confirm" oninput={on_password_confirm_input} required=true
|
||||
pattern=r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E])[A-Za-z0-9\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]{12,}$" /><br/>
|
||||
<div class="auth-input-tooltip">{"A password must contain uppercase, lowercase, numbers and special characters."}</div>
|
||||
</div>
|
||||
|
||||
if !state.loading {
|
||||
|
@ -218,7 +266,8 @@ pub fn SignUp(props: &Props) -> Html {
|
|||
|
||||
<div class="auth-field">
|
||||
<label for="token">{"Verification Token"}</label><br/>
|
||||
<input class="auth-input" type="text" name="token" onchange={on_token_changed}/><br/>
|
||||
<input class="auth-input" type="text" name="token" onchange={on_token_changed} required=true
|
||||
pattern=r"^([0-9a-f]{32})" /><br/>
|
||||
</div>
|
||||
|
||||
if !state.loading {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue