From d082d10c7a902b6fe7471650101154fc41e4f4d3 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Sat, 12 Jun 2021 14:52:55 +0000 Subject: [PATCH] Implement basic login component. With funny arc stuff. WTB functional yewdux matching release version of yew. --- Cargo.lock | 1 + web-ui/crate/Cargo.toml | 1 + web-ui/crate/src/api/auth.rs | 17 ++-- web-ui/crate/src/components/login.rs | 137 +++++++++++++++++++++++++++ web-ui/crate/src/components/mod.rs | 1 + web-ui/crate/src/error.rs | 3 + web-ui/crate/src/lib.rs | 7 +- web-ui/crate/src/rooms.rs | 17 +--- 8 files changed, 160 insertions(+), 24 deletions(-) create mode 100644 web-ui/crate/src/components/login.rs create mode 100644 web-ui/crate/src/components/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 169cadf..e24a5a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3578,6 +3578,7 @@ dependencies = [ "graphql_client_web", "js-sys", "serde", + "serde_json", "tenebrous-api", "thiserror", "wasm-bindgen", diff --git a/web-ui/crate/Cargo.toml b/web-ui/crate/Cargo.toml index 73d07b0..6c51a42 100644 --- a/web-ui/crate/Cargo.toml +++ b/web-ui/crate/Cargo.toml @@ -30,6 +30,7 @@ js-sys = "0.3" graphql_client = { git = "https://github.com/graphql-rust/graphql-client", branch = "master" } graphql_client_web = { git = "https://github.com/graphql-rust/graphql-client", branch = "master" } serde = { version = "1.0.67", features = ["derive"] } +serde_json = {version = "1.0" } thiserror = "1.0" [dependencies.web-sys] diff --git a/web-ui/crate/src/api/auth.rs b/web-ui/crate/src/api/auth.rs index defefe0..1d0c5fb 100644 --- a/web-ui/crate/src/api/auth.rs +++ b/web-ui/crate/src/api/auth.rs @@ -17,21 +17,25 @@ async fn make_request(request: Request) -> Result { let resp: Response = resp_value.dyn_into().unwrap(); let json = JsFuture::from(resp.json()?).await?; + console::log_1(&json); Ok(json) } -pub async fn fetch_jwt() -> Result { +pub async fn login(username: &str, password: &str) -> Result { let mut opts = RequestInit::new(); opts.method("POST"); opts.mode(RequestMode::Cors); opts.credentials(RequestCredentials::Include); - opts.body(Some(&JsValue::from_str( - r#" - { "username": "@projectmoon:agnos.is", "password": "lolol" } - "#, - ))); + let body = JsValue::from_str( + &serde_json::json!({ + "username": username, + "password": password + }) + .to_string(), + ); + opts.body(Some(&body)); let url = format!("http://localhost:10000/login"); let request = Request::new_with_str_and_init(&url, &opts)?; @@ -59,7 +63,6 @@ pub async fn refresh_jwt() -> Result { //TODO don't unwrap the response. OR... change it so we have a standard response. let response = make_request(request).await?; - console::log_1(&response); let response: LoginResponse = response.into_serde().unwrap(); Ok(response.jwt_token) diff --git a/web-ui/crate/src/components/login.rs b/web-ui/crate/src/components/login.rs new file mode 100644 index 0000000..4fe695e --- /dev/null +++ b/web-ui/crate/src/components/login.rs @@ -0,0 +1,137 @@ +use crate::error::UiError; +use crate::state::{Action, Room, WebUiDispatcher}; +use crate::{api, state::WebUiState}; +use std::sync::Arc; +use wasm_bindgen_futures::spawn_local; +use web_sys::console; +use web_sys::FocusEvent; +use yew::prelude::*; +use yewdux::dispatch::Dispatcher; +use yewdux::prelude::*; +use yewtil::NeqAssign; + +#[doc(hidden)] +pub(crate) struct YewduxLogin { + dispatch: Arc, + link: ComponentLink, + username: String, + password: String, +} + +pub enum LoginAction { + UpdateUsername(String), + UpdatePassword(String), + Login, + Noop, +} + +pub(crate) type Login = WithDispatch; +async fn do_login( + dispatch: &WebUiDispatcher, + username: &str, + password: &str, +) -> Result<(), UiError> { + let jwt = api::auth::login(username, password).await?; + //state.jwt_token = Some(jwt); + dispatch.send(Action::UpdateJwt(jwt)); + Ok(()) +} + +async fn do_refresh_jwt(dispatch: &WebUiDispatcher) -> Result<(), UiError> { + let jwt = api::auth::refresh_jwt().await?; + dispatch.send(Action::UpdateJwt(jwt)); + Ok(()) +} + +impl Component for YewduxLogin { + type Message = LoginAction; + type Properties = WebUiDispatcher; + + fn create(dispatch: Self::Properties, link: ComponentLink) -> Self { + Self { + dispatch: Arc::new(dispatch), + link, + username: "".to_string(), + password: "".to_string(), + } + } + + fn update(&mut self, action: Self::Message) -> ShouldRender { + match action { + LoginAction::UpdateUsername(username) => self.username = username, + LoginAction::UpdatePassword(password) => self.password = password, + LoginAction::Login => { + let dispatch = self.dispatch.clone(); + let username = self.username.clone(); + let password = self.password.clone(); + + spawn_local(async move { + do_login(&*dispatch, &username, &password).await; + }); + } + _ => (), + }; + + false + } + + fn change(&mut self, dispatch: Self::Properties) -> ShouldRender { + self.dispatch.neq_assign(Arc::new(dispatch)) + } + + fn rendered(&mut self, first_render: bool) { + if first_render { + // + } + } + + fn view(&self) -> Html { + let dispatch = Arc::new(self.dispatch.clone()); + let dispatch2 = dispatch.clone(); + + // let do_the_login = self.dispatch.reduce_callback(|state| { + // spawn_local(async move { + // do_login(state, "user", "pw").await; + // }); + // }); + + let do_the_login = self.link.callback(move |e: FocusEvent| { + e.prevent_default(); + LoginAction::Login + }); + + // let refresh_jwt = self.link.callback(move |_| { + // let dispatch = dispatch2.clone(); + // spawn_local(async move { + // do_refresh_jwt(&*dispatch).await; + // }); + // }); + + let update_username = self + .link + .callback(|e: InputData| LoginAction::UpdateUsername(e.value)); + + let update_password = self + .link + .callback(|e: InputData| LoginAction::UpdatePassword(e.value)); + + html! { +
+
+ + + + + +
+ // +
+ { "Current JWT: " } + { self.dispatch.state().jwt_token.as_ref().unwrap_or(&"[not set]".to_string()) } +
+
+ } + } + + fn destroy(&mut self) {} +} diff --git a/web-ui/crate/src/components/mod.rs b/web-ui/crate/src/components/mod.rs new file mode 100644 index 0000000..320cbbb --- /dev/null +++ b/web-ui/crate/src/components/mod.rs @@ -0,0 +1 @@ +pub mod login; diff --git a/web-ui/crate/src/error.rs b/web-ui/crate/src/error.rs index c7184e0..a4f9d16 100644 --- a/web-ui/crate/src/error.rs +++ b/web-ui/crate/src/error.rs @@ -15,6 +15,9 @@ pub enum UiError { #[error("error: {0}")] JsError(String), + + #[error("(de)serialization error: {0}")] + SerdeError(#[from] serde_json::Error), } impl From for UiError { diff --git a/web-ui/crate/src/lib.rs b/web-ui/crate/src/lib.rs index 9222a72..7efcd90 100644 --- a/web-ui/crate/src/lib.rs +++ b/web-ui/crate/src/lib.rs @@ -1,3 +1,4 @@ +use crate::components::login::Login; use rooms::RoomList; use rooms::YewduxRoomList; use wasm_bindgen::prelude::*; @@ -6,6 +7,7 @@ use yew_router::prelude::*; use yewdux::prelude::*; pub mod api; +pub mod components; pub mod error; pub mod grpc; pub mod rooms; @@ -38,7 +40,10 @@ fn render_route(routes: &AppRoute) -> Html { } AppRoute::Index => { html! { - +
+ + +
} } } diff --git a/web-ui/crate/src/rooms.rs b/web-ui/crate/src/rooms.rs index 569dfdb..905c251 100644 --- a/web-ui/crate/src/rooms.rs +++ b/web-ui/crate/src/rooms.rs @@ -44,12 +44,6 @@ async fn load_rooms(dispatch: &WebUiDispatcher) -> Result<(), UiError> { Ok(()) } -async fn do_jwt_stuff(dispatch: &WebUiDispatcher) -> Result<(), UiError> { - let jwt = api::auth::fetch_jwt().await?; - dispatch.send(Action::UpdateJwt(jwt)); - Ok(()) -} - async fn do_refresh_jwt(dispatch: &WebUiDispatcher) -> Result<(), UiError> { let jwt = api::auth::refresh_jwt().await?; dispatch.send(Action::UpdateJwt(jwt)); @@ -105,13 +99,6 @@ impl Component for YewduxRoomList { }); }); - let jwt_update = self.link.callback(move |_| { - let dispatch = dispatch2.clone(); - spawn_local(async move { - do_jwt_stuff(&*dispatch).await; - }); - }); - let refresh_jwt = self.link.callback(move |_| { let dispatch = dispatch3.clone(); spawn_local(async move { @@ -121,10 +108,8 @@ impl Component for YewduxRoomList { html! {
- - + -
{ "Current JWT: " } { self.dispatch.state().jwt_token.as_ref().unwrap_or(&"[not set]".to_string()) }
    { for self.dispatch.state().rooms.iter().map(|room| {