From b2cfc0ed03b41db25b49a03da31e57582c661c29 Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Wed, 8 Mar 2023 18:05:08 +0100 Subject: [PATCH] app: update yew to 0.19 This is a massive change to all the components, since the interface changed. There are opportunities to greatly simplify some components by turning them into functional_components, but this work has tried to stay as mechanical as possible. --- Cargo.lock | 206 +++++++------ app/Cargo.toml | 14 +- app/src/components/add_group_member.rs | 45 +-- app/src/components/add_user_to_group.rs | 45 +-- app/src/components/app.rs | 301 +++++++++---------- app/src/components/change_password.rs | 80 +++-- app/src/components/create_group.rs | 33 +- app/src/components/create_user.rs | 56 ++-- app/src/components/delete_group.rs | 46 ++- app/src/components/delete_user.rs | 46 +-- app/src/components/group_details.rs | 43 ++- app/src/components/group_table.rs | 32 +- app/src/components/login.rs | 66 ++-- app/src/components/logout.rs | 26 +- app/src/components/remove_user_from_group.rs | 37 +-- app/src/components/reset_password_step1.rs | 38 +-- app/src/components/reset_password_step2.rs | 72 ++--- app/src/components/router.rs | 42 ++- app/src/components/select.rs | 79 ++--- app/src/components/user_details.rs | 51 ++-- app/src/components/user_details_form.rs | 158 +++++----- app/src/components/user_table.rs | 33 +- app/src/infra/api.rs | 301 ++++++------------- app/src/infra/common_component.rs | 167 +++------- app/src/lib.rs | 3 +- 25 files changed, 893 insertions(+), 1127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 591cc19..98477c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,12 +352,6 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" -[[package]] -name = "anymap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" - [[package]] name = "arrayref" version = "0.3.6" @@ -660,12 +654,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg-match" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8100e46ff92eb85bf6dc2930c73f2a4f7176393c84a9446b3d501e1b354e7b34" - [[package]] name = "chrono" version = "0.4.23" @@ -1524,14 +1512,18 @@ checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "gloo" -version = "0.2.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ce6f2dfa9f57f15b848efa2aade5e1850dc72986b87a2b0752d44ca08f4967" +checksum = "23947965eee55e3e97a5cd142dd4c10631cc349b48cecca0ed230fd296f568cd" dependencies = [ - "gloo-console-timer", + "gloo-console", + "gloo-dialogs", "gloo-events", "gloo-file", + "gloo-render", + "gloo-storage", "gloo-timers", + "gloo-utils", ] [[package]] @@ -1548,11 +1540,12 @@ dependencies = [ ] [[package]] -name = "gloo-console-timer" -version = "0.1.0" +name = "gloo-dialogs" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b48675544b29ac03402c6dffc31a912f716e38d19f7e74b78b7e900ec3c941ea" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" dependencies = [ + "wasm-bindgen", "web-sys", ] @@ -1568,22 +1561,70 @@ dependencies = [ [[package]] name = "gloo-file" -version = "0.1.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9fecfe46b5dc3cc46f58e98ba580cc714f2c93860796d002eb3527a465ef49" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" dependencies = [ + "futures-channel", "gloo-events", "js-sys", "wasm-bindgen", "web-sys", ] +[[package]] +name = "gloo-net" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-timers" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ + "futures-channel", + "futures-core", "js-sys", "wasm-bindgen", ] @@ -2258,19 +2299,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - [[package]] name = "libc" version = "0.2.139" @@ -2388,6 +2416,8 @@ dependencies = [ "base64 0.13.1", "chrono", "gloo-console", + "gloo-file", + "gloo-net", "graphql_client 0.10.0", "http", "image", @@ -2401,12 +2431,12 @@ dependencies = [ "validator", "validator_derive 0.16.0", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", "yew", "yew-router", "yew_form", "yew_form_derive", - "yewtil", ] [[package]] @@ -2586,17 +2616,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff" -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "nom" version = "7.1.3" @@ -3287,6 +3306,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rsa" version = "0.6.1" @@ -3411,6 +3436,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "scoped-tls-hkt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e9d7eaddb227e8fbaaa71136ae0e1e913ca159b86c7da82f3e8f0044ad3a63" + [[package]] name = "scopeguard" version = "1.1.0" @@ -3576,6 +3607,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "618365e8e586c22123d692b72a7d791d5ee697817b65a218cdf12a98870af0f7" +dependencies = [ + "fnv", + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_bytes" version = "0.11.9" @@ -4480,8 +4523,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", - "serde", - "serde_json", "wasm-bindgen-macro", ] @@ -4727,26 +4768,17 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "yew" -version = "0.18.0" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4d5154faef86dddd2eb333d4755ea5643787d20aca683e58759b0e53351409f" +checksum = "2a1ccb53e57d3f7d847338cf5758befa811cabe207df07f543c06f502f9998cd" dependencies = [ - "anyhow", - "anymap", - "bincode", - "cfg-if", - "cfg-match", "console_error_panic_hook", "gloo", - "http", + "gloo-utils", "indexmap", "js-sys", - "log", - "ryu", - "serde", - "serde_json", + "scoped-tls-hkt", "slab", - "thiserror", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -4755,12 +4787,13 @@ dependencies = [ [[package]] name = "yew-macro" -version = "0.18.0" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6e23bfe3dc3933fbe9592d149c9985f3047d08c637a884b9344c21e56e092ef" +checksum = "5fab79082b556d768d6e21811869c761893f0450e1d550a67892b9bce303b7bb" dependencies = [ "boolinator", "lazy_static", + "proc-macro-error", "proc-macro2", "quote", "syn", @@ -4768,60 +4801,52 @@ dependencies = [ [[package]] name = "yew-router" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27666236d9597eac9be560e841e415e20ba67020bc8cd081076be178e159c8bc" +checksum = "155804f6f3aa309f596d5c3fa14486a94e7756f1edd7634569949e401d5099f2" dependencies = [ - "cfg-if", - "cfg-match", "gloo", + "gloo-utils", "js-sys", - "log", - "nom 5.1.2", + "route-recognizer", "serde", - "serde_json", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror", "wasm-bindgen", "web-sys", "yew", "yew-router-macro", - "yew-router-route-parser", ] [[package]] name = "yew-router-macro" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0ace2924b7a175e2d1c0e62ee7022a5ad840040dcd52414ce5f410ab322dba" +checksum = "39049d193b52eaad4ffc80916bf08806d142c90b5edcebd527644de438a7e19a" dependencies = [ "proc-macro2", "quote", "syn", - "yew-router-route-parser", -] - -[[package]] -name = "yew-router-route-parser" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de4a67208fb46b900af18a7397938b01f379dfc18da34799cfa8347eec715697" -dependencies = [ - "nom 5.1.2", ] [[package]] name = "yew_form" version = "0.1.8" -source = "git+https://github.com/jfbilodeau/yew_form?rev=67050812695b7a8a90b81b0637e347fc6629daed#67050812695b7a8a90b81b0637e347fc6629daed" +source = "git+https://github.com/jfbilodeau/yew_form?rev=4b9fabffb63393ec7626a4477fd36de12a07fac9#4b9fabffb63393ec7626a4477fd36de12a07fac9" dependencies = [ + "gloo-console", "validator", "validator_derive 0.14.0", + "wasm-bindgen", + "web-sys", "yew", ] [[package]] name = "yew_form_derive" version = "0.1.8" -source = "git+https://github.com/jfbilodeau/yew_form?rev=67050812695b7a8a90b81b0637e347fc6629daed#67050812695b7a8a90b81b0637e347fc6629daed" +source = "git+https://github.com/jfbilodeau/yew_form?rev=4b9fabffb63393ec7626a4477fd36de12a07fac9#4b9fabffb63393ec7626a4477fd36de12a07fac9" dependencies = [ "proc-macro2", "quote", @@ -4829,19 +4854,6 @@ dependencies = [ "yew_form", ] -[[package]] -name = "yewtil" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8543663ac49cd613df079282a1d8bdbdebdad6e02bac229f870fd4237b5d9aaa" -dependencies = [ - "log", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "yew", -] - [[package]] name = "zeroize" version = "1.5.7" diff --git a/app/Cargo.toml b/app/Cargo.toml index c5573f1..cc489be 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -9,22 +9,24 @@ include = ["src/**/*", "queries/**/*", "Cargo.toml", "../schema.graphql"] anyhow = "1" base64 = "0.13" gloo-console = "0.2.3" +gloo-file = "0.2.3" +gloo-net = "*" graphql_client = "0.10" http = "0.2" jwt = "0.13" rand = "0.8" serde = "1" serde_json = "1" +url-escape = "0.1.1" validator = "=0.14" validator_derive = "*" wasm-bindgen = "0.2" -yew = "0.18" -yewtil = "*" -yew-router = "0.15" +wasm-bindgen-futures = "*" +yew = "0.19.3" +yew-router = "0.16" # Needed because of https://github.com/tkaitchuck/aHash/issues/95 indexmap = "=1.6.2" -url-escape = "0.1.1" [dependencies.web-sys] version = "0.3" @@ -57,11 +59,11 @@ version = "0.24" [dependencies.yew_form] git = "https://github.com/jfbilodeau/yew_form" -rev = "67050812695b7a8a90b81b0637e347fc6629daed" +rev = "4b9fabffb63393ec7626a4477fd36de12a07fac9" [dependencies.yew_form_derive] git = "https://github.com/jfbilodeau/yew_form" -rev = "67050812695b7a8a90b81b0637e347fc6629daed" +rev = "4b9fabffb63393ec7626a4477fd36de12a07fac9" [lib] crate-type = ["cdylib"] diff --git a/app/src/components/add_group_member.rs b/app/src/components/add_group_member.rs index 0ac4e23..79c056c 100644 --- a/app/src/components/add_group_member.rs +++ b/app/src/components/add_group_member.rs @@ -52,23 +52,25 @@ pub struct Props { } impl CommonComponent for AddGroupMemberComponent { - fn handle_msg(&mut self, msg: ::Message) -> Result { + fn handle_msg( + &mut self, + ctx: &Context, + msg: ::Message, + ) -> Result { match msg { Msg::UserListResponse(response) => { self.user_list = Some(response?.users); - self.common.cancel_task(); } - Msg::SubmitAddMember => return self.submit_add_member(), + Msg::SubmitAddMember => return self.submit_add_member(ctx), Msg::AddMemberResponse(response) => { response?; - self.common.cancel_task(); let user = self .selected_user .as_ref() .expect("Could not get selected user") .clone(); // Remove the user from the dropdown. - self.common.on_user_added_to_group.emit(user); + ctx.props().on_user_added_to_group.emit(user); } Msg::SelectionChanged(option_props) => { let was_some = self.selected_user.is_some(); @@ -88,23 +90,25 @@ impl CommonComponent for AddGroupMemberComponent { } impl AddGroupMemberComponent { - fn get_user_list(&mut self) { + fn get_user_list(&mut self, ctx: &Context) { self.common.call_graphql::( + ctx, list_user_names::Variables { filters: None }, Msg::UserListResponse, "Error trying to fetch user list", ); } - fn submit_add_member(&mut self) -> Result { + fn submit_add_member(&mut self, ctx: &Context) -> Result { let user_id = match self.selected_user.clone() { None => return Ok(false), Some(user) => user.id, }; self.common.call_graphql::( + ctx, add_user_to_group::Variables { user: user_id, - group: self.common.group_id, + group: ctx.props().group_id, }, Msg::AddMemberResponse, "Error trying to initiate adding the user to a group", @@ -112,8 +116,8 @@ impl AddGroupMemberComponent { Ok(true) } - fn get_selectable_user_list(&self, user_list: &[User]) -> Vec { - let user_groups = self.common.users.iter().collect::>(); + fn get_selectable_user_list(&self, ctx: &Context, user_list: &[User]) -> Vec { + let user_groups = ctx.props().users.iter().collect::>(); user_list .iter() .filter(|u| !user_groups.contains(u)) @@ -126,32 +130,29 @@ impl Component for AddGroupMemberComponent { type Message = Msg; type Properties = Props; - fn create(props: Self::Properties, link: ComponentLink) -> Self { + fn create(ctx: &Context) -> Self { let mut res = Self { - common: CommonComponentParts::::create(props, link), + common: CommonComponentParts::::create(), user_list: None, selected_user: None, }; - res.get_user_list(); + res.get_user_list(ctx); res } - fn update(&mut self, msg: Self::Message) -> ShouldRender { + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { CommonComponentParts::::update_and_report_error( self, + ctx, msg, - self.common.on_error.clone(), + ctx.props().on_error.clone(), ) } - fn change(&mut self, props: Self::Properties) -> ShouldRender { - self.common.change(props) - } - - fn view(&self) -> Html { - let link = &self.common; + fn view(&self, ctx: &Context) -> Html { + let link = ctx.link(); if let Some(user_list) = &self.user_list { - let to_add_user_list = self.get_selectable_user_list(user_list); + let to_add_user_list = self.get_selectable_user_list(ctx, user_list); #[allow(unused_braces)] let make_select_option = |user: User| { html_nested! { diff --git a/app/src/components/add_user_to_group.rs b/app/src/components/add_user_to_group.rs index 1130ffb..7e8ce81 100644 --- a/app/src/components/add_user_to_group.rs +++ b/app/src/components/add_user_to_group.rs @@ -64,16 +64,18 @@ pub struct Props { } impl CommonComponent for AddUserToGroupComponent { - fn handle_msg(&mut self, msg: ::Message) -> Result { + fn handle_msg( + &mut self, + ctx: &Context, + msg: ::Message, + ) -> Result { match msg { Msg::GroupListResponse(response) => { self.group_list = Some(response?.groups.into_iter().map(Into::into).collect()); - self.common.cancel_task(); } - Msg::SubmitAddGroup => return self.submit_add_group(), + Msg::SubmitAddGroup => return self.submit_add_group(ctx), Msg::AddGroupResponse(response) => { response?; - self.common.cancel_task(); // Adding the user to the group succeeded, we're not in the process of adding a // group anymore. let group = self @@ -82,7 +84,7 @@ impl CommonComponent for AddUserToGroupComponent { .expect("Could not get selected group") .clone(); // Remove the group from the dropdown. - self.common.on_user_added_to_group.emit(group); + ctx.props().on_user_added_to_group.emit(group); } Msg::SelectionChanged(option_props) => { let was_some = self.selected_group.is_some(); @@ -102,22 +104,24 @@ impl CommonComponent for AddUserToGroupComponent { } impl AddUserToGroupComponent { - fn get_group_list(&mut self) { + fn get_group_list(&mut self, ctx: &Context) { self.common.call_graphql::( + ctx, get_group_list::Variables, Msg::GroupListResponse, "Error trying to fetch group list", ); } - fn submit_add_group(&mut self) -> Result { + fn submit_add_group(&mut self, ctx: &Context) -> Result { let group_id = match &self.selected_group { None => return Ok(false), Some(group) => group.id, }; self.common.call_graphql::( + ctx, add_user_to_group::Variables { - user: self.common.username.clone(), + user: ctx.props().username.clone(), group: group_id, }, Msg::AddGroupResponse, @@ -126,8 +130,8 @@ impl AddUserToGroupComponent { Ok(true) } - fn get_selectable_group_list(&self, group_list: &[Group]) -> Vec { - let user_groups = self.common.groups.iter().collect::>(); + fn get_selectable_group_list(&self, props: &Props, group_list: &[Group]) -> Vec { + let user_groups = props.groups.iter().collect::>(); group_list .iter() .filter(|g| !user_groups.contains(g)) @@ -139,32 +143,29 @@ impl AddUserToGroupComponent { impl Component for AddUserToGroupComponent { type Message = Msg; type Properties = Props; - fn create(props: Self::Properties, link: ComponentLink) -> Self { + fn create(ctx: &Context) -> Self { let mut res = Self { - common: CommonComponentParts::::create(props, link), + common: CommonComponentParts::::create(), group_list: None, selected_group: None, }; - res.get_group_list(); + res.get_group_list(ctx); res } - fn update(&mut self, msg: Self::Message) -> ShouldRender { + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { CommonComponentParts::::update_and_report_error( self, + ctx, msg, - self.common.on_error.clone(), + ctx.props().on_error.clone(), ) } - fn change(&mut self, props: Self::Properties) -> ShouldRender { - self.common.change(props) - } - - fn view(&self) -> Html { - let link = &self.common; + fn view(&self, ctx: &Context) -> Html { + let link = ctx.link(); if let Some(group_list) = &self.group_list { - let to_add_group_list = self.get_selectable_group_list(group_list); + let to_add_group_list = self.get_selectable_group_list(ctx.props(), group_list); #[allow(unused_braces)] let make_select_option = |group: Group| { html_nested! { diff --git a/app/src/components/app.rs b/app/src/components/app.rs index ed0d387..72a57a7 100644 --- a/app/src/components/app.rs +++ b/app/src/components/app.rs @@ -9,7 +9,7 @@ use crate::{ logout::LogoutButton, reset_password_step1::ResetPasswordStep1Form, reset_password_step2::ResetPasswordStep2Form, - router::{AppRoute, Link, NavButton}, + router::{AppRoute, Link, Redirect}, user_details::UserDetails, user_table::UserTable, }, @@ -17,21 +17,31 @@ use crate::{ }; use gloo_console::error; -use yew::{prelude::*, services::fetch::FetchTask}; +use yew::{ + function_component, + html::Scope, + prelude::{html, Component, Html}, + Context, +}; use yew_router::{ - agent::{RouteAgentDispatcher, RouteRequest}, - route::Route, - router::Router, - service::RouteService, + prelude::{History, Location}, + scope_ext::RouterScopeExt, + BrowserRouter, Switch, }; +#[function_component(AppContainer)] +pub fn app_container() -> Html { + html! { + + + + } +} + pub struct App { - link: ComponentLink, user_info: Option<(String, bool)>, redirect_to: Option, - route_dispatcher: RouteAgentDispatcher, password_reset_enabled: Option, - task: Option, } pub enum Msg { @@ -44,9 +54,8 @@ impl Component for App { type Message = Msg; type Properties = (); - fn create(_: Self::Properties, link: ComponentLink) -> Self { - let mut app = Self { - link, + fn create(ctx: &Context) -> Self { + let app = Self { user_info: get_cookie("user_id") .unwrap_or_else(|e| { error!(&e.to_string()); @@ -60,48 +69,40 @@ impl Component for App { None }) }), - redirect_to: Self::get_redirect_route(), - route_dispatcher: RouteAgentDispatcher::new(), + redirect_to: Self::get_redirect_route(ctx), password_reset_enabled: None, - task: None, }; - app.task = Some( - HostService::probe_password_reset( - app.link.callback_once(Msg::PasswordResetProbeFinished), - ) - .unwrap(), - ); - app.apply_initial_redirections(); + ctx.link().send_future(async move { + Msg::PasswordResetProbeFinished(HostService::probe_password_reset().await) + }); + app.apply_initial_redirections(ctx); app } - fn update(&mut self, msg: Self::Message) -> ShouldRender { + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { + let history = ctx.link().history().unwrap(); match msg { Msg::Login((user_name, is_admin)) => { self.user_info = Some((user_name.clone(), is_admin)); - self.route_dispatcher - .send(RouteRequest::ChangeRoute(Route::from( - self.redirect_to.take().unwrap_or_else(|| { - if is_admin { - AppRoute::ListUsers - } else { - AppRoute::UserDetails(user_name.clone()) - } - }), - ))); + history.push(self.redirect_to.take().unwrap_or_else(|| { + if is_admin { + AppRoute::ListUsers + } else { + AppRoute::UserDetails { + user_id: user_name.clone(), + } + } + })); } Msg::Logout => { self.user_info = None; self.redirect_to = None; - self.route_dispatcher - .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::Login))); + history.push(AppRoute::Login); } Msg::PasswordResetProbeFinished(Ok(enabled)) => { - self.task = None; self.password_reset_enabled = Some(enabled); } Msg::PasswordResetProbeFinished(Err(err)) => { - self.task = None; self.password_reset_enabled = Some(false); error!(&format!( "Could not probe for password reset support: {err:#}" @@ -111,24 +112,20 @@ impl Component for App { true } - fn change(&mut self, _: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - let link = self.link.clone(); + fn view(&self, ctx: &Context) -> Html { + let link = ctx.link().clone(); let is_admin = self.is_admin(); let password_reset_enabled = self.password_reset_enabled; html! {
- {self.view_banner()} + {self.view_banner(ctx)}
-
- - render={Router::render(move |s| Self::dispatch_route(s, &link, is_admin, password_reset_enabled))} +
+ + render={Switch::render(move |routes| Self::dispatch_route(routes, &link, is_admin, password_reset_enabled))} /> -
+
{self.view_footer()}
@@ -138,59 +135,50 @@ impl Component for App { } impl App { - fn get_redirect_route() -> Option { - let route_service = RouteService::<()>::new(); - let current_route = route_service.get_path(); - if current_route.is_empty() - || current_route == "/" - || current_route.contains("login") - || current_route.contains("reset-password") - { - None - } else { - use yew_router::Switch; - AppRoute::from_route_part::<()>(current_route, None).0 - } + // Get the page to land on after logging in, defaulting to the index. + fn get_redirect_route(ctx: &Context) -> Option { + let route = ctx.link().history().unwrap().location().route::(); + route.filter(|route| { + !matches!( + route, + AppRoute::Index + | AppRoute::Login + | AppRoute::StartResetPassword + | AppRoute::FinishResetPassword { token: _ } + ) + }) } - fn apply_initial_redirections(&mut self) { - let route_service = RouteService::<()>::new(); - let current_route = route_service.get_path(); - if current_route.contains("reset-password") { - if self.password_reset_enabled == Some(false) { - self.route_dispatcher - .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::Login))); - } - return; - } - match &self.user_info { - None => { - self.route_dispatcher - .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::Login))); - } - Some((user_name, is_admin)) => match &self.redirect_to { - Some(url) => { - self.route_dispatcher - .send(RouteRequest::ReplaceRoute(Route::from(url.clone()))); + fn apply_initial_redirections(&self, ctx: &Context) { + let history = ctx.link().history().unwrap(); + let route = history.location().route::(); + let redirection = match (route, &self.user_info, &self.redirect_to) { + ( + Some(AppRoute::StartResetPassword | AppRoute::FinishResetPassword { token: _ }), + _, + _, + ) if self.password_reset_enabled == Some(false) => Some(AppRoute::Login), + (None, _, _) | (_, None, _) => Some(AppRoute::Login), + // User is logged in, a URL was given, don't redirect. + (_, Some(_), Some(_)) => None, + (_, Some((user_name, is_admin)), None) => { + if *is_admin { + Some(AppRoute::ListUsers) + } else { + Some(AppRoute::UserDetails { + user_id: user_name.clone(), + }) } - None => { - if *is_admin { - self.route_dispatcher - .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::ListUsers))); - } else { - self.route_dispatcher - .send(RouteRequest::ReplaceRoute(Route::from( - AppRoute::UserDetails(user_name.clone()), - ))); - } - } - }, + } + }; + if let Some(redirect_to) = redirection { + history.push(redirect_to); } } fn dispatch_route( - switch: AppRoute, - link: &ComponentLink, + switch: &AppRoute, + link: &Scope, is_admin: bool, password_reset_enabled: Option, ) -> Html { @@ -204,10 +192,10 @@ impl App { AppRoute::Index | AppRoute::ListUsers => html! {
- + {"Create a user"} - +
}, AppRoute::CreateGroup => html! { @@ -216,41 +204,40 @@ impl App { AppRoute::ListGroups => html! {
- + {"Create a group"} - +
}, - AppRoute::GroupDetails(group_id) => html! { - + AppRoute::GroupDetails { group_id } => html! { + }, - AppRoute::UserDetails(username) => html! { - + AppRoute::UserDetails { user_id } => html! { + }, - AppRoute::ChangePassword(username) => html! { - + AppRoute::ChangePassword { user_id } => html! { + }, AppRoute::StartResetPassword => match password_reset_enabled { Some(true) => html! { }, Some(false) => { - App::dispatch_route(AppRoute::Login, link, is_admin, password_reset_enabled) + html! { } } None => html! {}, }, - AppRoute::FinishResetPassword(token) => match password_reset_enabled { - Some(true) => html! { }, + AppRoute::FinishResetPassword { token } => match password_reset_enabled { + Some(true) => html! { }, Some(false) => { - App::dispatch_route(AppRoute::Login, link, is_admin, password_reset_enabled) + html! { } } None => html! {}, }, } } - fn view_banner(&self) -> Html { - let link = &self.link; + fn view_banner(&self, ctx: &Context) -> Html { html! {
@@ -265,7 +252,7 @@ impl App {
  • + to={AppRoute::ListUsers}> {"Users"} @@ -273,7 +260,7 @@ impl App {
  • + to={AppRoute::ListGroups}> {"Groups"} @@ -281,55 +268,59 @@ impl App { } } else { html!{} } } - - { - if let Some((user_id, _)) = &self.user_info { - html! { - - } - } else { html!{} } - } + { self.view_user_menu(ctx) }
  • } } + fn view_user_menu(&self, ctx: &Context) -> Html { + if let Some((user_id, _)) = &self.user_info { + let link = ctx.link(); + html! { + + } + } else { + html! {} + } + } + fn view_footer(&self) -> Html { html! {