feat: improved the signin ux
This commit is contained in:
parent
d0e119eabc
commit
93cacdb8e5
|
@ -1,6 +1,6 @@
|
||||||
mod data;
|
mod data;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Error, Result};
|
||||||
use gloo_net::http::Request;
|
use gloo_net::http::Request;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use web_sys::RequestMode;
|
use web_sys::RequestMode;
|
||||||
|
@ -29,7 +29,13 @@ impl Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login(&self, username: String, password: String, callback: Callback<Result<String>>) {
|
pub fn login(
|
||||||
|
&self,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
callback: Callback<(Result<String>, Callback<Result<()>>)>,
|
||||||
|
status_callback: Callback<Result<()>>,
|
||||||
|
) {
|
||||||
let url = format!("{}/account/authenticate", &self.base_url);
|
let url = format!("{}/account/authenticate", &self.base_url);
|
||||||
|
|
||||||
let body = json!({
|
let body = json!({
|
||||||
|
@ -39,16 +45,25 @@ impl Session {
|
||||||
|
|
||||||
let call = async move {
|
let call = async move {
|
||||||
let request = Request::post(&url).mode(RequestMode::Cors).json(&body)?;
|
let request = Request::post(&url).mode(RequestMode::Cors).json(&body)?;
|
||||||
let response = request
|
let response = request.send().await?;
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json::<data::AccountAuthenticateResponse>()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(response.token)
|
match response.status() {
|
||||||
|
200 => Ok(response
|
||||||
|
.json::<data::AccountAuthenticateResponse>()
|
||||||
|
.await?
|
||||||
|
.token),
|
||||||
|
400 => Err(Error::msg(format!("Bad request"))),
|
||||||
|
401 => Err(Error::msg(format!("Wrong password!"))),
|
||||||
|
403 => Err(Error::msg(format!("You're not allowed to do this!"))),
|
||||||
|
404 => Err(Error::msg(format!("Unknown username!"))),
|
||||||
|
424 => Err(Error::msg(format!("This account is not verified!"))),
|
||||||
|
e => Err(Error::msg(format!("Unknown response: {e}"))),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
wasm_bindgen_futures::spawn_local(async move { callback.emit(call.await) });
|
wasm_bindgen_futures::spawn_local(
|
||||||
|
async move { callback.emit((call.await, status_callback)) },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_token(&mut self, token: String) {
|
pub fn set_token(&mut self, token: String) {
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -37,7 +37,7 @@ fn switch(routes: Route) -> Html {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Callbacks {
|
pub struct Callbacks {
|
||||||
pub sign_in_callback: Callback<Result<String>>,
|
pub sign_in_callback: Callback<(Result<String>, Callback<Result<()>>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
|
@ -47,12 +47,21 @@ fn App() -> Html {
|
||||||
|
|
||||||
let cloned_state = state.clone();
|
let cloned_state = state.clone();
|
||||||
let callbacks = use_state(move || Callbacks {
|
let callbacks = use_state(move || Callbacks {
|
||||||
sign_in_callback: Callback::from(move |response: Result<String>| {
|
sign_in_callback: Callback::from(
|
||||||
|
move |response: (Result<String>, Callback<Result<()>>)| {
|
||||||
let mut new_state = cloned_state.deref().clone();
|
let mut new_state = cloned_state.deref().clone();
|
||||||
|
|
||||||
new_state.set_token(response.expect("!!! remove this temporary expect !!!"));
|
response.1.emit(match response.0 {
|
||||||
|
Ok(token) => {
|
||||||
|
new_state.set_token(token);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
});
|
||||||
|
|
||||||
cloned_state.set(new_state);
|
cloned_state.set(new_state);
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod signin;
|
||||||
mod signup;
|
mod signup;
|
||||||
|
|
||||||
use crate::topbar::Tab;
|
use crate::topbar::Tab;
|
||||||
|
use anyhow::Result;
|
||||||
use signin::SignIn;
|
use signin::SignIn;
|
||||||
use signup::SignUp;
|
use signup::SignUp;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -10,11 +11,16 @@ use yew::prelude::*;
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
ToggleSignIn,
|
ToggleSignIn,
|
||||||
ToggleSignUp,
|
ToggleSignUp,
|
||||||
|
|
||||||
|
SignInStatus(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
sign_in: bool,
|
sign_in: bool,
|
||||||
sign_up: bool,
|
sign_up: bool,
|
||||||
|
|
||||||
|
sign_in_status: AttrValue,
|
||||||
|
sign_up_status: AttrValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for State {
|
impl Default for State {
|
||||||
|
@ -22,6 +28,9 @@ impl Default for State {
|
||||||
Self {
|
Self {
|
||||||
sign_in: false,
|
sign_in: false,
|
||||||
sign_up: false,
|
sign_up: false,
|
||||||
|
|
||||||
|
sign_in_status: AttrValue::default(),
|
||||||
|
sign_up_status: AttrValue::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +50,11 @@ impl Reducible for State {
|
||||||
state.sign_in = false;
|
state.sign_in = false;
|
||||||
state.sign_up = !self.sign_up;
|
state.sign_up = !self.sign_up;
|
||||||
}
|
}
|
||||||
|
Action::SignInStatus(s) => {
|
||||||
|
state.sign_in = true;
|
||||||
|
state.sign_up = false;
|
||||||
|
state.sign_in_status = s.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Rc::new(state)
|
Rc::new(state)
|
||||||
}
|
}
|
||||||
|
@ -60,6 +74,14 @@ pub fn TopBar() -> Html {
|
||||||
Callback::from(move |_| state.dispatch(Action::ToggleSignUp))
|
Callback::from(move |_| state.dispatch(Action::ToggleSignUp))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sign_in_status = {
|
||||||
|
let state = state.clone();
|
||||||
|
Callback::from(move |status: Result<()>| match status {
|
||||||
|
Ok(_) => state.dispatch(Action::ToggleSignIn),
|
||||||
|
Err(e) => state.dispatch(Action::SignInStatus(e.to_string())),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<nav id="topbar">
|
<nav id="topbar">
|
||||||
<ul id="tabs-main" class="tabs">
|
<ul id="tabs-main" class="tabs">
|
||||||
|
@ -75,7 +97,7 @@ pub fn TopBar() -> Html {
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
if state.sign_in {
|
if state.sign_in {
|
||||||
<SignIn/>
|
<SignIn status={state.sign_in_status.clone()} status_callback={sign_in_status}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.sign_up {
|
if state.sign_up {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::backend::Session;
|
use crate::backend::Session;
|
||||||
use crate::Callbacks;
|
use crate::Callbacks;
|
||||||
|
use anyhow::Result;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::HtmlInputElement;
|
use web_sys::HtmlInputElement;
|
||||||
|
@ -9,10 +10,17 @@ use yew::prelude::*;
|
||||||
struct Data {
|
struct Data {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
pub status: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
pub status: AttrValue,
|
||||||
|
pub status_callback: Callback<Result<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn SignIn() -> Html {
|
pub fn SignIn(props: &Props) -> Html {
|
||||||
let state = use_state(|| Data::default());
|
let state = use_state(|| Data::default());
|
||||||
let callbacks = use_context::<Callbacks>().expect("Callbacks context not available!");
|
let callbacks = use_context::<Callbacks>().expect("Callbacks context not available!");
|
||||||
let session = use_context::<Session>().unwrap_or_default();
|
let session = use_context::<Session>().unwrap_or_default();
|
||||||
|
@ -43,6 +51,7 @@ pub fn SignIn() -> Html {
|
||||||
cloned_state.set(data);
|
cloned_state.set(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let status_callback = props.status_callback.clone();
|
||||||
let onsubmit = Callback::from(move |event: SubmitEvent| {
|
let onsubmit = Callback::from(move |event: SubmitEvent| {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
|
|
||||||
|
@ -52,6 +61,7 @@ pub fn SignIn() -> Html {
|
||||||
state.username,
|
state.username,
|
||||||
state.password,
|
state.password,
|
||||||
callbacks.sign_in_callback.clone(),
|
callbacks.sign_in_callback.clone(),
|
||||||
|
status_callback.clone(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -70,6 +80,10 @@ pub fn SignIn() -> Html {
|
||||||
<div class="center-x">
|
<div class="center-x">
|
||||||
<button class="auth-submit" type="submit">{"Sign In"}</button>
|
<button class="auth-submit" type="submit">{"Sign In"}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="center-x">
|
||||||
|
<p class="auth-status">{props.status.clone()}</p>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,11 @@
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-status {
|
||||||
|
font: normal 14px Roboto;
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
.center-x {
|
.center-x {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
Loading…
Reference in New Issue