[yew-devel] [PATCH yew-comp 1/2] login_panel/realm_selector: use default realm provided by api

Shannon Sterz s.sterz at proxmox.com
Wed Oct 8 17:19:35 CEST 2025


when a user has not previously safed their username and realm, use the
default realm provided via the api. if no realm has been specified at
all, still fall back to "pam".

Signed-off-by: Shannon Sterz <s.sterz at proxmox.com>
---
 src/login_panel.rs    | 10 +++----
 src/realm_selector.rs | 65 +++++++++++++++++++++++++++++++++++++++----
 2 files changed, 64 insertions(+), 11 deletions(-)

diff --git a/src/login_panel.rs b/src/login_panel.rs
index 0b835e7..6c3aaa7 100644
--- a/src/login_panel.rs
+++ b/src/login_panel.rs
@@ -29,9 +29,9 @@ pub struct LoginPanel {
     pub on_login: Option<Callback<Authentication>>,
 
     /// Default realm.
-    #[prop_or(AttrValue::from("pam"))]
+    #[prop_or_default]
     #[builder]
-    pub default_realm: AttrValue,
+    pub default_realm: Option<AttrValue>,
 
     /// Mobile Layout
     ///
@@ -125,16 +125,16 @@ impl ProxmoxLoginPanel {
         });
     }
 
-    fn get_defaults(&self, props: &LoginPanel) -> (String, String) {
+    fn get_defaults(&self, props: &LoginPanel) -> (String, Option<AttrValue>) {
         let mut default_username = String::from("root");
-        let mut default_realm = props.default_realm.to_string();
+        let mut default_realm = props.default_realm.clone();
 
         if props.mobile || *self.save_username {
             let last_userid: String = (*self.last_username).to_string();
             if !last_userid.is_empty() {
                 if let Some((user, realm)) = last_userid.rsplit_once('@') {
                     default_username = user.to_owned();
-                    default_realm = realm.to_owned().into();
+                    default_realm = Some(AttrValue::from(realm.to_owned()));
                 }
             }
         }
diff --git a/src/realm_selector.rs b/src/realm_selector.rs
index 0c57bf6..b11e924 100644
--- a/src/realm_selector.rs
+++ b/src/realm_selector.rs
@@ -1,4 +1,4 @@
-use anyhow::format_err;
+use anyhow::{format_err, Error};
 use std::rc::Rc;
 
 use yew::html::IntoPropValue;
@@ -61,18 +61,36 @@ impl RealmSelector {
     }
 }
 
-pub struct ProxmoxRealmSelector {
+struct ProxmoxRealmSelector {
     store: Store<BasicRealmInfo>,
     validate: ValidateFn<(String, Store<BasicRealmInfo>)>,
     picker: RenderFn<SelectorRenderArgs<Store<BasicRealmInfo>>>,
+    loaded_default_realm: Option<AttrValue>,
+}
+
+impl ProxmoxRealmSelector {
+    async fn load_realms(url: AttrValue) -> Msg {
+        let response: Result<_, Error> = crate::http_get_full(url.to_string(), None).await;
+
+        match response {
+            Ok(data) => Msg::LoadComplete(data.data),
+            Err(_) => Msg::LoadFailed,
+        }
+    }
+}
+
+enum Msg {
+    LoadComplete(Vec<BasicRealmInfo>),
+    LoadFailed,
 }
 
 impl Component for ProxmoxRealmSelector {
-    type Message = ();
+    type Message = Msg;
     type Properties = RealmSelector;
 
     fn create(ctx: &Context<Self>) -> Self {
-        let store = Store::new().on_change(ctx.link().callback(|_| ())); // trigger redraw
+        let store = Store::new();
+        let url = ctx.props().path.clone();
 
         let validate = ValidateFn::new(|(realm, store): &(String, Store<BasicRealmInfo>)| {
             store
@@ -94,23 +112,58 @@ impl Component for ProxmoxRealmSelector {
                 .into()
         });
 
+        ctx.link().send_future(Self::load_realms(url));
+
         Self {
             store,
             validate,
             picker,
+            loaded_default_realm: None,
+        }
+    }
+
+    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
+        match msg {
+            Msg::LoadComplete(mut data) => {
+                data.sort_by(|a, b| a.realm.cmp(&b.realm));
+
+                let realm = ctx
+                    .props()
+                    .default
+                    .as_ref()
+                    .and_then(|d| data.iter().find(|r| &r.realm == d))
+                    .or_else(|| data.iter().find(|r| r.default.unwrap_or_default()))
+                    .or_else(|| data.iter().find(|r| r.ty == "pam"))
+                    .map(|r| AttrValue::from(r.realm.clone()));
+
+                self.loaded_default_realm = realm;
+                self.store.set_data(data);
+                true
+            }
+            // not much we can do here, so just don't re-render
+            Msg::LoadFailed => false,
         }
     }
 
     fn view(&self, ctx: &Context<Self>) -> Html {
         let props = ctx.props();
+        let store = self.store.clone();
+
+        let default = props
+            .default
+            .clone()
+            .or_else(|| self.loaded_default_realm.clone())
+            .unwrap_or(AttrValue::from("pam"));
 
         Selector::new(self.store.clone(), self.picker.clone())
             .with_std_props(&props.std_props)
             .with_input_props(&props.input_props)
             .required(true)
-            .default(props.default.as_deref().unwrap_or("pam").to_string())
-            .loader(props.path.clone())
+            .default(&default)
             .validate(self.validate.clone())
+            // force re-render of the selector after load; returning `true` in update does not
+            // re-render the selector by itself
+            .key(format!("realm-selector-{default}"))
             .into()
     }
 }
-- 
2.47.3





More information about the yew-devel mailing list