[fractal] login: Split in pages and cleanup



commit 0487df15abd626325a9f1f454d46f9a21a55e9d2
Author: Kévin Commaille <zecakeh tedomum fr>
Date:   Wed Oct 5 21:13:57 2022 +0200

    login: Split in pages and cleanup

 data/resources/resources.gresource.xml             |   3 +
 data/resources/ui/login-homeserver-page.ui         |  70 +++++
 data/resources/ui/login-idp-button.ui              |   2 +-
 data/resources/ui/login-method-page.ui             | 112 +++++++
 data/resources/ui/login-sso-page.ui                |  32 ++
 data/resources/ui/login.ui                         | 179 +----------
 po/POTFILES.in                                     |   5 +
 ...login_advanced_dialog.rs => advanced_dialog.rs} |   0
 src/login/homeserver_page.rs                       | 184 +++++++++++
 src/login/idp_button.rs                            |  40 +--
 src/login/method_page.rs                           | 219 +++++++++++++
 src/login/mod.rs                                   | 343 +++++++--------------
 src/login/sso_page.rs                              |  51 +++
 13 files changed, 811 insertions(+), 429 deletions(-)
---
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index 73bd9ead8..1318cd5ba 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -113,7 +113,10 @@
     <file compressed="true" preprocess="xml-stripblanks" 
alias="identity-verification-widget.ui">ui/identity-verification-widget.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="join-room-dialog.ui">ui/join-room-dialog.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="login-advanced-dialog.ui">ui/login-advanced-dialog.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="login-homeserver-page.ui">ui/login-homeserver-page.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="login-idp-button.ui">ui/login-idp-button.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="login-method-page.ui">ui/login-method-page.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="login-sso-page.ui">ui/login-sso-page.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" alias="login.ui">ui/login.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" alias="media-viewer.ui">ui/media-viewer.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" alias="member-menu.ui">ui/member-menu.ui</file>
diff --git a/data/resources/ui/login-homeserver-page.ui b/data/resources/ui/login-homeserver-page.ui
new file mode 100644
index 000000000..113d53c48
--- /dev/null
+++ b/data/resources/ui/login-homeserver-page.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="LoginHomeserverPage" parent="AdwBin">
+    <property name="child">
+      <object class="GtkScrolledWindow" id="scrolled_window">
+        <property name="hscrollbar-policy">never</property>
+        <property name="propagate-natural-height">True</property>
+        <property name="child">
+          <object class="AdwClamp">
+            <property name="maximum-size">360</property>
+            <property name="margin-top">24</property>
+            <property name="margin-bottom">24</property>
+            <property name="margin-start">12</property>
+            <property name="margin-end">12</property>
+            <property name="child">
+              <object class="GtkBox">
+                <property name="orientation">vertical</property>
+                <property name="valign">center</property>
+                <property name="spacing">24</property>
+                <child>
+                  <object class="GtkPicture">
+                    <property name="file">resource:///org/gnome/Fractal/assets/homeserver.svg</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="AdwEntryRow" id="homeserver_entry">
+                        <style>
+                          <class name="card"/>
+                        </style>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="homeserver_help">
+                        <style>
+                          <class name="caption"/>
+                          <class name="dim-label"/>
+                        </style>
+                        <property name="justify">left</property>
+                        <property name="xalign">0.0</property>
+                        <property name="margin-start">6</property>
+                        <property name="margin-end">6</property>
+                        <property name="wrap">true</property>
+                        <property name="use-markup">true</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton">
+                    <style>
+                      <class name="pill"/>
+                    </style>
+                    <property name="halign">center</property>
+                    <!-- Translators: As in 'Advanced Settings'. -->
+                    <property name="label" translatable="yes">Advanced…</property>
+                    <property name="action-name">login.open-advanced</property>
+                  </object>
+                </child>
+              </object>
+            </property>
+          </object>
+        </property>
+      </object>
+    </property>
+  </template>
+</interface>
diff --git a/data/resources/ui/login-idp-button.ui b/data/resources/ui/login-idp-button.ui
index 45f237bcb..732a1dc30 100644
--- a/data/resources/ui/login-idp-button.ui
+++ b/data/resources/ui/login-idp-button.ui
@@ -5,6 +5,6 @@
       <class name="card"/>
       <class name="sso-button"/>
     </style>
+    <property name="action-name">login.sso</property>
   </template>
 </interface>
-
diff --git a/data/resources/ui/login-method-page.ui b/data/resources/ui/login-method-page.ui
new file mode 100644
index 000000000..635b3cda1
--- /dev/null
+++ b/data/resources/ui/login-method-page.ui
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="LoginMethodPage" parent="AdwBin">
+    <property name="child">
+      <object class="GtkScrolledWindow" id="scrolled_window">
+        <property name="hscrollbar-policy">never</property>
+        <property name="propagate-natural-height">True</property>
+        <property name="child">
+          <object class="AdwClamp">
+            <property name="maximum-size">360</property>
+            <property name="margin-top">24</property>
+            <property name="margin-bottom">24</property>
+            <property name="margin-start">12</property>
+            <property name="margin-end">12</property>
+            <property name="child">
+              <object class="GtkBox">
+                <property name="orientation">vertical</property>
+                <property name="valign">center</property>
+                <property name="spacing">30</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <property name="halign">center</property>
+                    <child>
+                      <object class="GtkLabel" id="title">
+                        <style>
+                          <class name="title-4"/>
+                        </style>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="spacing">6</property>
+                        <property name="halign">center</property>
+                        <property name="visible" bind-source="LoginMethodPage" bind-property="autodiscovery" 
bind-flags="sync-create"/>
+                        <property name="tooltip-text" translatable="yes">Homeserver URL</property>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="icon-name">user-home-symbolic</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <style>
+                              <class name="body"/>
+                            </style>
+                            <property name="label" bind-source="LoginMethodPage" bind-property="homeserver" 
bind-flags="sync-create"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="AdwEntryRow" id="username_entry">
+                    <style>
+                      <class name="card"/>
+                    </style>
+                    <property name="title" translatable="true">Matrix Username</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="AdwPasswordEntryRow" id="password_entry">
+                        <style>
+                          <class name="card"/>
+                        </style>
+                        <property name="title" translatable="true">Password</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLinkButton" id="forgot_password">
+                        <property name="use_underline">True</property>
+                        <property name="label" translatable="yes">_Forgot Password?</property>
+                        <property name="uri">https://app.element.io/#/forgot_password</property>
+                        <property name="halign">center</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkBox" id="sso_idp_box">
+                    <property name="visible">false</property>
+                    <property name="spacing">12</property>
+                    <property name="homogeneous">true</property>
+                    <property name="hexpand">true</property>
+                    <property name="vexpand">true</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="more_sso_option">
+                    <style>
+                      <class name="pill"/>
+                    </style>
+                    <property name="halign">center</property>
+                    <property name="label" translatable="yes">More SSO Providers</property>
+                    <property name="action-name">login.sso</property>
+                    <property name="action-target">@ms nothing</property>
+                  </object>
+                </child>
+              </object>
+            </property>
+          </object>
+        </property>
+      </object>
+    </property>
+  </template>
+</interface>
diff --git a/data/resources/ui/login-sso-page.ui b/data/resources/ui/login-sso-page.ui
new file mode 100644
index 000000000..9a41fb32c
--- /dev/null
+++ b/data/resources/ui/login-sso-page.ui
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="LoginSsoPage" parent="AdwBin">
+    <property name="child">
+      <object class="GtkScrolledWindow" id="scrolled_window">
+        <property name="hscrollbar-policy">never</property>
+        <property name="propagate-natural-height">True</property>
+        <property name="child">
+          <object class="AdwClamp">
+            <property name="maximum-size">360</property>
+            <property name="margin-top">24</property>
+            <property name="margin-bottom">24</property>
+            <property name="margin-start">12</property>
+            <property name="margin-end">12</property>
+            <property name="child">
+              <object class="GtkLabel">
+                <property name="valign">center</property>
+                <property name="wrap">True</property>
+                <property name="wrap-mode">word-char</property>
+                <property name="justify">center</property>
+                <property name="label" translatable="yes">Please follow the steps in the browser.</property>
+                <style>
+                  <class name="title-2"/>
+                </style>
+              </object>
+            </property>
+          </object>
+        </property>
+      </object>
+    </property>
+  </template>
+</interface>
diff --git a/data/resources/ui/login.ui b/data/resources/ui/login.ui
index 445d9a489..3635e8b28 100644
--- a/data/resources/ui/login.ui
+++ b/data/resources/ui/login.ui
@@ -54,191 +54,28 @@
               <object class="GtkStackPage">
                 <property name="name">homeserver</property>
                 <property name="child">
-                  <object class="AdwClamp">
-                    <property name="maximum-size">360</property>
-                    <property name="margin-top">24</property>
-                    <property name="margin-bottom">24</property>
-                    <property name="margin-start">12</property>
-                    <property name="margin-end">12</property>
-                    <property name="child">
-                      <object class="GtkBox">
-                        <property name="orientation">vertical</property>
-                        <property name="valign">center</property>
-                        <property name="spacing">24</property>
-                        <child>
-                          <object class="GtkPicture">
-                            <property 
name="file">resource:///org/gnome/Fractal/assets/homeserver.svg</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="orientation">vertical</property>
-                            <property name="spacing">6</property>
-                            <child>
-                              <object class="AdwEntryRow" id="homeserver_entry">
-                                <style>
-                                  <class name="card"/>
-                                </style>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="homeserver_help">
-                                <style>
-                                  <class name="caption"/>
-                                  <class name="dim-label"/>
-                                </style>
-                                <property name="justify">left</property>
-                                <property name="xalign">0.0</property>
-                                <property name="margin-start">6</property>
-                                <property name="margin-end">6</property>
-                                <property name="wrap">true</property>
-                                <property name="use-markup">true</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton">
-                            <style>
-                              <class name="pill"/>
-                            </style>
-                            <property name="halign">center</property>
-                            <!-- Translators: As in 'Advanced Settings'. -->
-                            <property name="label" translatable="yes">Advanced…</property>
-                            <property name="action-name">login.open-advanced</property>
-                          </object>
-                        </child>
-                      </object>
-                    </property>
+                  <object class="LoginHomeserverPage" id="homeserver_page">
+                    <property name="autodiscovery" bind-source="Login" bind-property="autodiscovery" 
bind-flags="sync-create"/>
                   </object>
                 </property>
               </object>
             </child>
             <child>
               <object class="GtkStackPage">
-                <property name="name">password</property>
+                <property name="name">method</property>
                 <property name="child">
-                  <object class="AdwClamp">
-                    <property name="maximum-size">360</property>
-                    <property name="margin-top">24</property>
-                    <property name="margin-bottom">24</property>
-                    <property name="margin-start">12</property>
-                    <property name="margin-end">12</property>
-                    <property name="valign">center</property>
-                    <child>
-                      <object class="GtkBox">
-                        <property name="orientation">vertical</property>
-                        <property name="spacing">30</property>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="orientation">vertical</property>
-                            <property name="spacing">6</property>
-                            <property name="halign">center</property>
-                            <child>
-                              <object class="GtkLabel" id="password_title">
-                                <style>
-                                  <class name="title-4"/>
-                                </style>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkBox">
-                                <property name="spacing">6</property>
-                                <property name="halign">center</property>
-                                <property name="visible" bind-source="Login" bind-property="autodiscovery" 
bind-flags="sync-create"/>
-                                <property name="tooltip-text" translatable="yes">Homeserver URL</property>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="icon-name">user-home-symbolic</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <style>
-                                      <class name="body"/>
-                                    </style>
-                                    <property name="label" bind-source="Login" bind-property="homeserver" 
bind-flags="sync-create"/>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="AdwEntryRow" id="username_entry">
-                            <style>
-                              <class name="card"/>
-                            </style>
-                            <property name="title" translatable="true">Matrix Username</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkBox">
-                            <property name="orientation">vertical</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="AdwPasswordEntryRow" id="password_entry">
-                                <style>
-                                  <class name="card"/>
-                                </style>
-                                <property name="title" translatable="true">Password</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLinkButton" id="forgot_password">
-                                <property name="use_underline">True</property>
-                                <property name="label" translatable="yes">_Forgot Password?</property>
-                                <property name="uri">https://app.element.io/#/forgot_password</property>
-                                <property name="halign">center</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkBox" id="sso_box">
-                            <property name="visible">false</property>
-                            <property name="spacing">12</property>
-                            <property name="homogeneous">true</property>
-                            <property name="hexpand">true</property>
-                            <property name="vexpand">true</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton" id="more_sso_option">
-                            <style>
-                              <class name="pill"/>
-                            </style>
-                            <property name="halign">center</property>
-                            <property name="label" translatable="yes">More SSO Providers</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
+                  <object class="LoginMethodPage" id="method_page">
+                    <property name="homeserver" bind-source="Login" bind-property="homeserver" 
bind-flags="sync-create"/>
+                    <property name="autodiscovery" bind-source="Login" bind-property="autodiscovery" 
bind-flags="sync-create"/>
                   </object>
                 </property>
               </object>
             </child>
             <child>
               <object class="GtkStackPage">
-                <property name="name">sso_message_page</property>
+                <property name="name">sso</property>
                 <property name="child">
-                  <object class="AdwClamp">
-                    <property name="maximum-size">360</property>
-                    <property name="tightening-threshold">360</property>
-                    <property name="valign">center</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="valign">center</property>
-                        <property name="wrap">True</property>
-                        <property name="wrap-mode">word-char</property>
-                        <property name="justify">center</property>
-                        <property name="label" translatable="yes">Please follow the steps in the 
browser.</property>
-                        <style>
-                          <class name="title-2"/>
-                        </style>
-                      </object>
-                    </child>
-                  </object>
+                  <object class="LoginSsoPage" id="sso_page"/>
                 </property>
               </object>
             </child>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4b9177286..32510c079 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,6 +36,9 @@ data/resources/ui/greeter.ui
 data/resources/ui/identity-verification-widget.ui
 data/resources/ui/join-room-dialog.ui
 data/resources/ui/login-advanced-dialog.ui
+data/resources/ui/login-homeserver-page.ui
+data/resources/ui/login-method-page.ui
+data/resources/ui/login-sso-page.ui
 data/resources/ui/login.ui
 data/resources/ui/member-menu.ui
 data/resources/ui/room-creation.ui
@@ -51,6 +54,8 @@ src/components/location_viewer.rs
 src/components/media_content_viewer.rs
 src/error_page.rs
 src/greeter.rs
+src/login/homeserver_page.rs
+src/login/method_page.rs
 src/login/mod.rs
 src/secret.rs
 src/session/account_settings/devices_page/device_list.rs
diff --git a/src/login/login_advanced_dialog.rs b/src/login/advanced_dialog.rs
similarity index 100%
rename from src/login/login_advanced_dialog.rs
rename to src/login/advanced_dialog.rs
diff --git a/src/login/homeserver_page.rs b/src/login/homeserver_page.rs
new file mode 100644
index 000000000..74131798d
--- /dev/null
+++ b/src/login/homeserver_page.rs
@@ -0,0 +1,184 @@
+use adw::{prelude::*, subclass::prelude::BinImpl};
+use gettextrs::gettext;
+use gtk::{self, glib, glib::clone, subclass::prelude::*, CompositeTemplate};
+use ruma::{IdParseError, OwnedServerName, ServerName};
+use url::{ParseError, Url};
+
+use crate::gettext_f;
+
+mod imp {
+    use std::cell::Cell;
+
+    use glib::subclass::InitializingObject;
+    use once_cell::sync::Lazy;
+
+    use super::*;
+
+    #[derive(Debug, Default, CompositeTemplate)]
+    #[template(resource = "/org/gnome/Fractal/login-homeserver-page.ui")]
+    pub struct LoginHomeserverPage {
+        #[template_child]
+        pub homeserver_entry: TemplateChild<adw::EntryRow>,
+        #[template_child]
+        pub homeserver_help: TemplateChild<gtk::Label>,
+        /// Whether homeserver auto-discovery is enabled.
+        pub autodiscovery: Cell<bool>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for LoginHomeserverPage {
+        const NAME: &'static str = "LoginHomeserverPage";
+        type Type = super::LoginHomeserverPage;
+        type ParentType = adw::Bin;
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        fn instance_init(obj: &InitializingObject<Self>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for LoginHomeserverPage {
+        fn properties() -> &'static [glib::ParamSpec] {
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![glib::ParamSpecBoolean::new(
+                    "autodiscovery",
+                    "Auto-discovery",
+                    "Whether homeserver auto-discovery is enabled",
+                    false,
+                    glib::ParamFlags::READWRITE,
+                )]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "autodiscovery" => obj.autodiscovery().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn set_property(
+            &self,
+            obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "autodiscovery" => obj.set_autodiscovery(value.get().unwrap()),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn constructed(&self, obj: &Self::Type) {
+            self.parent_constructed(obj);
+
+            self.homeserver_entry
+                .connect_entry_activated(clone!(@weak obj => move|_| {
+                    let _ = obj.activate_action("login.next", None);
+                }));
+            self.homeserver_entry
+                .connect_changed(clone!(@weak obj => move |_| {
+                    let _ = obj.activate_action("login.update-next", None);
+                }));
+        }
+    }
+
+    impl WidgetImpl for LoginHomeserverPage {}
+    impl BinImpl for LoginHomeserverPage {}
+}
+
+glib::wrapper! {
+    /// The login page to provide the homeserver and login settings.
+    pub struct LoginHomeserverPage(ObjectSubclass<imp::LoginHomeserverPage>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl LoginHomeserverPage {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create LoginHomeserverPage")
+    }
+
+    pub fn autodiscovery(&self) -> bool {
+        self.imp().autodiscovery.get()
+    }
+
+    fn set_autodiscovery(&self, autodiscovery: bool) {
+        let priv_ = self.imp();
+
+        priv_.autodiscovery.set(autodiscovery);
+
+        if autodiscovery {
+            priv_.homeserver_entry.set_title(&gettext("Domain Name"));
+            priv_.homeserver_help.set_markup(&gettext(
+                "The domain of your Matrix homeserver, for example gnome.org",
+            ));
+        } else {
+            priv_.homeserver_entry.set_title(&gettext("Homeserver URL"));
+            priv_.homeserver_help.set_markup(&gettext_f(
+                // Translators: Do NOT translate the content between '{' and '}', this is a
+                // variable name.
+                "The URL of your Matrix homeserver, for example {address}",
+                &[(
+                    "address",
+                    "<span segment=\"word\">https://gnome.modular.im</span>",
+                )],
+            ));
+        }
+    }
+
+    /// The server name entered by the user, if any.
+    pub fn server_name(&self) -> Option<OwnedServerName> {
+        build_server_name(self.imp().homeserver_entry.text().as_str()).ok()
+    }
+
+    /// The homeserver URL entered by the user, if any.
+    pub fn homeserver_url(&self) -> Option<Url> {
+        build_homeserver_url(self.imp().homeserver_entry.text().as_str()).ok()
+    }
+
+    pub fn can_go_next(&self) -> bool {
+        let homeserver = self.imp().homeserver_entry.text();
+
+        if self.autodiscovery() {
+            build_server_name(homeserver.as_str()).is_ok()
+        } else {
+            build_homeserver_url(homeserver.as_str()).is_ok()
+        }
+    }
+
+    pub fn focus_default(&self) {
+        self.imp().homeserver_entry.grab_focus();
+    }
+
+    pub fn clean(&self) {
+        self.imp().homeserver_entry.set_text("");
+    }
+}
+
+impl Default for LoginHomeserverPage {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+fn build_server_name(server: &str) -> Result<OwnedServerName, IdParseError> {
+    let server = server
+        .strip_prefix("http://";)
+        .or_else(|| server.strip_prefix("https://";))
+        .unwrap_or(server);
+    ServerName::parse(server)
+}
+
+fn build_homeserver_url(server: &str) -> Result<Url, ParseError> {
+    if server.starts_with("http://";) || server.starts_with("https://";) {
+        Url::parse(server)
+    } else {
+        Url::parse(&format!("https://{}";, server))
+    }
+}
diff --git a/src/login/idp_button.rs b/src/login/idp_button.rs
index 0d67cd2c6..a6948d424 100644
--- a/src/login/idp_button.rs
+++ b/src/login/idp_button.rs
@@ -2,7 +2,6 @@ use gtk::{self, glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTe
 use matrix_sdk::ruma::api::client::session::get_login_types::v3::{
     IdentityProvider, IdentityProviderBrand,
 };
-use url::Url;
 
 #[derive(Hash, Debug, Eq, PartialEq, Clone, Copy, glib::Enum)]
 #[repr(i32)]
@@ -104,7 +103,6 @@ mod imp {
     pub struct IdpButton {
         pub brand: Cell<IdpBrand>,
         pub id: RefCell<Option<String>>,
-        pub homeserver: RefCell<Option<String>>,
     }
 
     #[glib::object_subclass]
@@ -142,13 +140,6 @@ mod imp {
                         None,
                         glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
                     ),
-                    glib::ParamSpecString::new(
-                        "homeserver",
-                        "homeserver",
-                        "The homeserver url",
-                        None,
-                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
-                    ),
                 ]
             });
 
@@ -159,7 +150,6 @@ mod imp {
             match pspec.name() {
                 "id" => obj.id().unwrap().to_value(),
                 "brand" => obj.brand().to_value(),
-                "homeserver" => obj.homeserver().to_value(),
                 _ => unimplemented!(),
             }
         }
@@ -178,9 +168,6 @@ mod imp {
                 "id" => {
                     obj.set_id(value.get().unwrap());
                 }
-                "homeserver" => {
-                    obj.set_homeserver(value.get().unwrap());
-                }
                 _ => unimplemented!(),
             };
         }
@@ -194,13 +181,13 @@ mod imp {
     }
 
     impl WidgetImpl for IdpButton {}
-
     impl ButtonImpl for IdpButton {}
 }
 
 glib::wrapper! {
     pub struct IdpButton(ObjectSubclass<imp::IdpButton>)
-        @extends gtk::Widget, gtk::Button, @implements gtk::Accessible;
+        @extends gtk::Widget, gtk::Button,
+        @implements gtk::Accessible, gtk::Actionable;
 }
 
 impl IdpButton {
@@ -209,13 +196,10 @@ impl IdpButton {
     }
 
     pub fn set_id(&self, id: String) {
+        self.set_action_target_value(Some(&Some(&id).to_variant()));
         self.imp().id.replace(Some(id));
     }
 
-    pub fn set_homeserver(&self, url: String) {
-        self.imp().homeserver.replace(Some(url));
-    }
-
     pub fn set_brand(&self, brand: IdpBrand) {
         self.imp().brand.replace(brand);
     }
@@ -224,22 +208,16 @@ impl IdpButton {
         self.imp().id.borrow().clone()
     }
 
-    pub fn homeserver(&self) -> Option<String> {
-        self.imp().homeserver.borrow().clone()
-    }
-
     pub fn brand(&self) -> IdpBrand {
         self.imp().brand.get()
     }
 
-    pub fn new_from_identity_provider(homeserver: Url, idp: &IdentityProvider) -> Option<Self> {
+    pub fn new_from_identity_provider(idp: &IdentityProvider) -> Option<Self> {
         let gidp: IdpBrand = idp.brand.as_ref()?.try_into().ok()?;
-        let ret: IdpButton = glib::Object::new(&[
-            ("brand", &gidp),
-            ("id", &idp.id),
-            ("homeserver", &homeserver.as_str()),
-        ])
-        .expect("Failed to create IdpButton");
-        Some(ret)
+
+        Some(
+            glib::Object::new(&[("brand", &gidp), ("id", &idp.id)])
+                .expect("Failed to create IdpButton"),
+        )
     }
 }
diff --git a/src/login/method_page.rs b/src/login/method_page.rs
new file mode 100644
index 000000000..1c792e999
--- /dev/null
+++ b/src/login/method_page.rs
@@ -0,0 +1,219 @@
+use adw::{prelude::*, subclass::prelude::BinImpl};
+use gtk::{self, glib, glib::clone, subclass::prelude::*, CompositeTemplate};
+use ruma::api::client::session::get_login_types::v3::SsoLoginType;
+
+use super::idp_button::IdpButton;
+use crate::i18n::gettext_f;
+
+mod imp {
+    use std::cell::{Cell, RefCell};
+
+    use glib::subclass::InitializingObject;
+    use once_cell::sync::Lazy;
+
+    use super::*;
+
+    #[derive(Debug, Default, CompositeTemplate)]
+    #[template(resource = "/org/gnome/Fractal/login-method-page.ui")]
+    pub struct LoginMethodPage {
+        #[template_child]
+        pub title: TemplateChild<gtk::Label>,
+        #[template_child]
+        pub username_entry: TemplateChild<adw::EntryRow>,
+        #[template_child]
+        pub password_entry: TemplateChild<adw::PasswordEntryRow>,
+        #[template_child]
+        pub sso_idp_box: TemplateChild<gtk::Box>,
+        #[template_child]
+        pub more_sso_option: TemplateChild<gtk::Button>,
+        /// The homeserver to log into.
+        pub homeserver: RefCell<Option<String>>,
+        /// Whether homeserver auto-discovery is enabled.
+        pub autodiscovery: Cell<bool>,
+    }
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for LoginMethodPage {
+        const NAME: &'static str = "LoginMethodPage";
+        type Type = super::LoginMethodPage;
+        type ParentType = adw::Bin;
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        fn instance_init(obj: &InitializingObject<Self>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for LoginMethodPage {
+        fn properties() -> &'static [glib::ParamSpec] {
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![
+                    glib::ParamSpecString::new(
+                        "homeserver",
+                        "Homeserver",
+                        "The homeserver to log into",
+                        None,
+                        glib::ParamFlags::READWRITE,
+                    ),
+                    glib::ParamSpecBoolean::new(
+                        "autodiscovery",
+                        "Auto-discovery",
+                        "Whether homeserver auto-discovery is enabled",
+                        false,
+                        glib::ParamFlags::READWRITE,
+                    ),
+                ]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "homeserver" => self.homeserver.borrow().to_value(),
+                "autodiscovery" => self.autodiscovery.get().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn set_property(
+            &self,
+            _obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "homeserver" => {
+                    self.homeserver.replace(value.get().ok());
+                }
+                "autodiscovery" => self.autodiscovery.set(value.get().unwrap()),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn constructed(&self, obj: &Self::Type) {
+            self.parent_constructed(obj);
+
+            self.username_entry
+                .connect_entry_activated(clone!(@weak obj => move|_| {
+                    let _ = obj.activate_action("login.next", None);
+                }));
+            self.username_entry
+                .connect_changed(clone!(@weak obj => move |_| {
+                    let _ = obj.activate_action("login.update-next", None);
+                }));
+
+            self.password_entry
+                .connect_entry_activated(clone!(@weak obj => move|_| {
+                    let _ = obj.activate_action("login.next", None);
+                }));
+            self.password_entry
+                .connect_changed(clone!(@weak obj => move |_| {
+                    let _ = obj.activate_action("login.update-next", None);
+                }));
+        }
+    }
+
+    impl WidgetImpl for LoginMethodPage {}
+
+    impl BinImpl for LoginMethodPage {}
+}
+
+glib::wrapper! {
+    /// A widget handling the login flows.
+    pub struct LoginMethodPage(ObjectSubclass<imp::LoginMethodPage>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl LoginMethodPage {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create LoginMethodPage")
+    }
+
+    /// The username entered by the user.
+    pub fn username(&self) -> String {
+        self.imp().username_entry.text().into()
+    }
+
+    /// The password entered by the user.
+    pub fn password(&self) -> String {
+        self.imp().password_entry.text().into()
+    }
+
+    /// Set the domain name to show in the title.
+    pub fn set_domain_name(&self, domain_name: &str) {
+        self.imp().title.set_markup(&gettext_f(
+            // Translators: Do NOT translate the content between '{' and '}', this is a variable
+            // name.
+            "Connecting to {domain_name}",
+            &[(
+                "domain_name",
+                &format!("<span segment=\"word\">{}</span>", domain_name),
+            )],
+        ))
+    }
+
+    pub fn update_sso(&self, login_types: Option<&SsoLoginType>) {
+        let priv_ = self.imp();
+
+        let login_types = match login_types {
+            Some(t) => t,
+            None => {
+                priv_.sso_idp_box.hide();
+                priv_.more_sso_option.hide();
+                return;
+            }
+        };
+
+        let mut has_unknown_methods = false;
+        let mut has_known_methods = false;
+
+        for provider in &login_types.identity_providers {
+            let btn = IdpButton::new_from_identity_provider(provider);
+
+            if let Some(btn) = btn {
+                priv_.sso_idp_box.append(&btn);
+                has_known_methods = true;
+            } else {
+                has_unknown_methods = true;
+            }
+        }
+
+        priv_.sso_idp_box.set_visible(has_known_methods);
+        priv_.more_sso_option.set_visible(has_unknown_methods);
+    }
+
+    pub fn can_go_next(&self) -> bool {
+        let priv_ = self.imp();
+        let username_length = priv_.username_entry.text().len();
+        let password_length = priv_.password_entry.text().len();
+        username_length != 0 && password_length != 0
+    }
+
+    pub fn clean(&self) {
+        let priv_ = self.imp();
+        priv_.username_entry.set_text("");
+        priv_.password_entry.set_text("");
+
+        // Empty the identity providers box.
+        let mut child = priv_.sso_idp_box.first_child();
+        while child.is_some() {
+            priv_.sso_idp_box.remove(&child.unwrap());
+            child = priv_.sso_idp_box.first_child();
+        }
+    }
+
+    pub fn focus_default(&self) {
+        self.imp().username_entry.grab_focus();
+    }
+}
+
+impl Default for LoginMethodPage {
+    fn default() -> Self {
+        Self::new()
+    }
+}
diff --git a/src/login/mod.rs b/src/login/mod.rs
index 49a2b9bd6..8b3714223 100644
--- a/src/login/mod.rs
+++ b/src/login/mod.rs
@@ -3,27 +3,23 @@ use gettextrs::gettext;
 use gtk::{self, gio, glib, glib::clone, subclass::prelude::*, CompositeTemplate};
 use log::{debug, warn};
 use matrix_sdk::{
-    config::RequestConfig,
-    ruma::{
-        api::client::session::get_login_types::v3::{
-            LoginType::{Password, Sso},
-            SsoLoginType,
-        },
-        IdParseError, OwnedServerName, ServerName,
-    },
-    Client,
+    config::RequestConfig, ruma::api::client::session::get_login_types::v3::LoginType, Client,
 };
-use url::{ParseError, Url};
+use url::Url;
 
+mod advanced_dialog;
+mod homeserver_page;
 mod idp_button;
-mod login_advanced_dialog;
-
-use idp_button::IdpButton;
-use login_advanced_dialog::LoginAdvancedDialog;
+mod method_page;
+mod sso_page;
 
+use self::{
+    advanced_dialog::LoginAdvancedDialog, homeserver_page::LoginHomeserverPage,
+    method_page::LoginMethodPage, sso_page::LoginSsoPage,
+};
 use crate::{
-    components::SpinnerButton, gettext_f, spawn, spawn_tokio, toast,
-    user_facing_error::UserFacingError, Session,
+    components::SpinnerButton, spawn, spawn_tokio, toast, user_facing_error::UserFacingError,
+    Session,
 };
 
 mod imp {
@@ -48,19 +44,11 @@ mod imp {
         #[template_child]
         pub main_stack: TemplateChild<gtk::Stack>,
         #[template_child]
-        pub homeserver_entry: TemplateChild<adw::EntryRow>,
-        #[template_child]
-        pub homeserver_help: TemplateChild<gtk::Label>,
-        #[template_child]
-        pub password_title: TemplateChild<gtk::Label>,
-        #[template_child]
-        pub username_entry: TemplateChild<adw::EntryRow>,
-        #[template_child]
-        pub password_entry: TemplateChild<adw::PasswordEntryRow>,
+        pub homeserver_page: TemplateChild<LoginHomeserverPage>,
         #[template_child]
-        pub sso_box: TemplateChild<gtk::Box>,
+        pub method_page: TemplateChild<LoginMethodPage>,
         #[template_child]
-        pub more_sso_option: TemplateChild<gtk::Button>,
+        pub sso_page: TemplateChild<LoginSsoPage>,
         #[template_child]
         pub offline_info_bar: TemplateChild<gtk::InfoBar>,
         #[template_child]
@@ -85,8 +73,16 @@ mod imp {
             Self::bind_template(klass);
             klass.set_css_name("login");
             klass.set_accessible_role(gtk::AccessibleRole::Group);
-            klass.install_action("login.next", None, move |widget, _, _| widget.forward());
-            klass.install_action("login.prev", None, move |widget, _, _| widget.backward());
+
+            klass.install_action("login.update-next", None, move |widget, _, _| {
+                widget.update_next_state()
+            });
+            klass.install_action("login.next", None, move |widget, _, _| widget.go_next());
+            klass.install_action("login.prev", None, move |widget, _, _| widget.go_previous());
+            klass.install_action("login.sso", Some("ms"), move |widget, _, variant| {
+                let idp_id = variant.and_then(|v| v.get::<Option<String>>()).flatten();
+                widget.login_with_sso(idp_id);
+            });
             klass.install_action("login.open-advanced", None, move |widget, _, _| {
                 spawn!(clone!(@weak widget => async move {
                     widget.open_advanced_dialog().await;
@@ -127,7 +123,9 @@ mod imp {
                         "Auto-discovery",
                         "Whether auto-discovery is enabled",
                         true,
-                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT,
+                        glib::ParamFlags::READWRITE
+                            | glib::ParamFlags::CONSTRUCT
+                            | glib::ParamFlags::EXPLICIT_NOTIFY,
                     ),
                 ]
             });
@@ -166,35 +164,13 @@ mod imp {
                 obj.update_network_state();
             }));
 
-            obj.update_network_state();
-
             self.main_stack
                 .connect_visible_child_notify(clone!(@weak obj => move |_|
-                    obj.update_next_action();
+                    obj.update_next_state();
                     obj.focus_default();
                 ));
-            obj.update_next_action();
 
-            self.homeserver_entry
-                .connect_entry_activated(clone!(@weak obj => move|_| {
-                    obj.default_widget().activate();
-                }));
-            self.homeserver_entry
-                .connect_changed(clone!(@weak obj => move |_| obj.update_next_action()));
-            self.username_entry
-                .connect_entry_activated(clone!(@weak obj => move|_| {
-                    obj.default_widget().activate();
-                }));
-            self.username_entry
-                .connect_changed(clone!(@weak obj => move |_| obj.update_next_action()));
-            self.password_entry
-                .connect_entry_activated(clone!(@weak obj => move|_| {
-                    obj.default_widget().activate();
-                }));
-            self.password_entry
-                .connect_changed(clone!(@weak obj => move |_| obj.update_next_action()));
-            self.more_sso_option
-                .connect_clicked(clone!(@weak obj => move |_| obj.login_with_sso(None)));
+            obj.update_network_state();
         }
     }
 
@@ -218,37 +194,6 @@ impl Login {
         self.imp().homeserver.borrow().clone()
     }
 
-    fn reload_sso_panel(&self, login_types: &SsoLoginType) {
-        let priv_ = &mut imp::Login::from_instance(self);
-        let mut child = priv_.sso_box.first_child();
-        while child.is_some() {
-            priv_.sso_box.remove(&child.unwrap());
-            child = priv_.sso_box.first_child();
-        }
-        let mut has_unknown_methods = false;
-        let mut has_known_methods = false;
-        let homeserver: Url = self.homeserver().unwrap();
-        for provider in login_types.identity_providers.iter() {
-            let opt_brand = provider.brand.as_ref();
-            if opt_brand.is_none() {
-                has_unknown_methods = true;
-                continue;
-            }
-            let btn = IdpButton::new_from_identity_provider(homeserver.clone(), provider);
-            if let Some(real) = btn {
-                self.imp().sso_box.append(&real);
-                real.connect_clicked(
-                    clone!(@weak self as obj => move |btn| obj.login_with_sso(btn.id())),
-                );
-                has_known_methods = true;
-            } else {
-                has_unknown_methods = true;
-            }
-        }
-        priv_.sso_box.set_visible(has_known_methods);
-        priv_.more_sso_option.set_visible(has_unknown_methods);
-    }
-
     pub fn homeserver_pretty(&self) -> Option<String> {
         let homeserver = self.homeserver();
         homeserver
@@ -268,6 +213,20 @@ impl Login {
         self.notify("homeserver");
     }
 
+    pub fn autodiscovery(&self) -> bool {
+        self.imp().autodiscovery.get()
+    }
+
+    pub fn set_autodiscovery(&self, autodiscovery: bool) {
+        if self.autodiscovery() == autodiscovery {
+            return;
+        }
+
+        self.imp().autodiscovery.set(autodiscovery);
+        self.notify("autodiscovery");
+        self.update_next_state();
+    }
+
     fn visible_child(&self) -> String {
         let priv_ = imp::Login::from_instance(self);
         priv_.main_stack.visible_child_name().unwrap().into()
@@ -278,31 +237,15 @@ impl Login {
         priv_.main_stack.set_visible_child_name(visible_child);
     }
 
-    fn update_next_action(&self) {
+    fn update_next_state(&self) {
         let priv_ = imp::Login::from_instance(self);
         match self.visible_child().as_ref() {
             "homeserver" => {
-                let homeserver = priv_.homeserver_entry.text();
-                let enabled = if self.autodiscovery() {
-                    build_server_name(homeserver.as_str()).is_ok()
-                } else {
-                    build_homeserver_url(homeserver.as_str()).is_ok()
-                };
-                self.action_set_enabled(
-                    "login.next",
-                    enabled && gio::NetworkMonitor::default().is_network_available(),
-                );
+                self.enable_next_action(priv_.homeserver_page.can_go_next());
                 priv_.next_button.set_visible(true);
             }
-            "password" => {
-                let username_length = priv_.username_entry.text().len();
-                let password_length = priv_.password_entry.text().len();
-                self.action_set_enabled(
-                    "login.next",
-                    username_length != 0
-                        && password_length != 0
-                        && gio::NetworkMonitor::default().is_network_available(),
-                );
+            "method" => {
+                self.enable_next_action(priv_.method_page.can_go_next());
                 priv_.next_button.set_visible(true);
             }
             _ => {
@@ -311,7 +254,14 @@ impl Login {
         }
     }
 
-    fn forward(&self) {
+    fn enable_next_action(&self, enabled: bool) {
+        self.action_set_enabled(
+            "login.next",
+            enabled && gio::NetworkMonitor::default().is_network_available(),
+        );
+    }
+
+    fn go_next(&self) {
         match self.visible_child().as_ref() {
             "homeserver" => {
                 if self.autodiscovery() {
@@ -320,17 +270,17 @@ impl Login {
                     self.check_homeserver();
                 }
             }
-            "password" => self.login_with_password(),
+            "method" => self.login_with_password(),
             _ => {}
         }
     }
 
-    fn backward(&self) {
+    fn go_previous(&self) {
         match self.visible_child().as_ref() {
-            "password" => self.set_visible_child("homeserver"),
-            "sso_message_page" => {
+            "method" => self.set_visible_child("homeserver"),
+            "sso" => {
                 self.set_visible_child(if self.imp().supports_password.get() {
-                    "password"
+                    "method"
                 } else {
                     "homeserver"
                 });
@@ -341,34 +291,6 @@ impl Login {
         }
     }
 
-    pub fn autodiscovery(&self) -> bool {
-        self.imp().autodiscovery.get()
-    }
-
-    fn set_autodiscovery(&self, autodiscovery: bool) {
-        let priv_ = self.imp();
-
-        priv_.autodiscovery.set(autodiscovery);
-        if autodiscovery {
-            priv_.homeserver_entry.set_title(&gettext("Domain Name"));
-            priv_.homeserver_help.set_markup(&gettext(
-                "The domain of your Matrix homeserver, for example gnome.org",
-            ));
-        } else {
-            priv_.homeserver_entry.set_title(&gettext("Homeserver URL"));
-            priv_.homeserver_help.set_markup(&gettext_f(
-                // Translators: Do NOT translate the content between '{' and '}', this is a
-                // variable name.
-                "The URL of your Matrix homeserver, for example {address}",
-                &[(
-                    "address",
-                    "<span segment=\"word\">https://gnome.modular.im</span>",
-                )],
-            ));
-        }
-        self.update_next_action();
-    }
-
     async fn open_advanced_dialog(&self) {
         let dialog =
             LoginAdvancedDialog::new(self.root().unwrap().downcast_ref::<gtk::Window>().unwrap());
@@ -379,7 +301,7 @@ impl Login {
     }
 
     fn try_autodiscovery(&self) {
-        let server = build_server_name(self.imp().homeserver_entry.text().as_str()).unwrap();
+        let server = self.imp().homeserver_page.server_name().unwrap();
 
         self.freeze();
 
@@ -405,44 +327,8 @@ impl Login {
         );
     }
 
-    fn switch_off_sso(&self) {
-        let priv_ = self.imp();
-        priv_.sso_box.set_visible(false);
-        priv_.more_sso_option.set_visible(false);
-    }
-
-    async fn check_login_types(&self, client: Client) {
-        let login_types = spawn_tokio!(async move { client.get_login_types().await })
-            .await
-            .unwrap()
-            .unwrap();
-        let sso = login_types
-            .flows
-            .iter()
-            .find(|flow| matches!(flow, Sso(_sso_providers)));
-        let password = login_types
-            .flows
-            .iter()
-            .find(|flow| matches!(flow, Password(_)));
-        let has_sso = sso.is_some();
-        let has_password = password.is_some();
-        self.imp().supports_password.replace(has_password);
-        if has_sso && has_password {
-            if let Sso(login_type) = sso.unwrap() {
-                self.reload_sso_panel(login_type);
-            }
-        } else if !has_sso {
-            self.switch_off_sso();
-        }
-        if has_password {
-            self.show_password_page();
-        } else {
-            self.login_with_sso(None);
-        }
-    }
-
     fn check_homeserver(&self) {
-        let homeserver = build_homeserver_url(self.imp().homeserver_entry.text().as_str()).unwrap();
+        let homeserver = self.imp().homeserver_page.homeserver_url().unwrap();
         let homeserver_clone = homeserver.clone();
 
         self.freeze();
@@ -473,33 +359,59 @@ impl Login {
         );
     }
 
-    fn show_password_page(&self) {
+    async fn check_login_types(&self, client: Client) {
+        let handle = spawn_tokio!(async move { client.get_login_types().await });
+
+        let login_types = match handle.await.unwrap() {
+            Ok(res) => res,
+            Err(error) => {
+                warn!("Failed to get available login types: {error}");
+                toast!(self, "Failed to get available login types.");
+                return;
+            }
+        };
+
+        let sso = login_types.flows.iter().find_map(|flow| {
+            if let LoginType::Sso(sso) = flow {
+                Some(sso)
+            } else {
+                None
+            }
+        });
+
+        let has_password = login_types
+            .flows
+            .iter()
+            .any(|flow| matches!(flow, LoginType::Password(_)));
+
+        self.imp().supports_password.replace(has_password);
+
+        if has_password {
+            self.imp().method_page.update_sso(sso);
+            self.show_login_methods();
+        } else {
+            self.login_with_sso(None);
+        }
+    }
+
+    fn show_login_methods(&self) {
         let priv_ = self.imp();
 
         let domain_name = if self.autodiscovery() {
-            priv_.homeserver_entry.text().to_string()
+            priv_.homeserver_page.server_name().unwrap().to_string()
         } else {
             self.homeserver_pretty().unwrap()
         };
+        priv_.method_page.set_domain_name(&domain_name);
 
-        priv_.password_title.set_markup(&gettext_f(
-            // Translators: Do NOT translate the content between '{' and '}', this is a variable
-            // name.
-            "Connecting to {domain_name}",
-            &[(
-                "domain_name",
-                &format!("<span segment=\"word\">{}</span>", domain_name),
-            )],
-        ));
-
-        self.set_visible_child("password");
+        self.set_visible_child("method");
     }
 
     fn login_with_password(&self) {
         let priv_ = self.imp();
         let homeserver = self.homeserver().unwrap();
-        let username = priv_.username_entry.text().to_string();
-        let password = priv_.password_entry.text().to_string();
+        let username = priv_.method_page.username();
+        let password = priv_.method_page.password();
         let autodiscovery = self.autodiscovery();
 
         self.freeze();
@@ -519,7 +431,7 @@ impl Login {
     fn login_with_sso(&self, idp_id: Option<String>) {
         let priv_ = imp::Login::from_instance(self);
         let homeserver = self.homeserver().unwrap();
-        self.set_visible_child("sso_message_page");
+        self.set_visible_child("sso");
 
         let session = Session::new();
         self.set_handler_for_prepared_session(&session);
@@ -535,10 +447,9 @@ impl Login {
 
     pub fn clean(&self) {
         let priv_ = self.imp();
-        priv_.homeserver_entry.set_text("");
-        priv_.username_entry.set_text("");
-        priv_.password_entry.set_text("");
-        priv_.autodiscovery.set(true);
+        priv_.homeserver_page.clean();
+        priv_.method_page.clean();
+        self.set_autodiscovery(true);
         priv_.homeserver.take();
         priv_.main_stack.set_visible_child_name("homeserver");
         self.unfreeze();
@@ -558,7 +469,7 @@ impl Login {
 
         priv_.next_button.set_loading(false);
         priv_.main_stack.set_sensitive(true);
-        self.update_next_action();
+        self.update_next_state();
     }
 
     pub fn connect_new_session<F: Fn(&Self, Session) + 'static>(
@@ -600,10 +511,10 @@ impl Login {
         let priv_ = self.imp();
         match self.visible_child().as_ref() {
             "homeserver" => {
-                priv_.homeserver_entry.grab_focus();
+                priv_.homeserver_page.focus_default();
             }
-            "password" => {
-                priv_.username_entry.grab_focus();
+            "method" => {
+                priv_.method_page.focus_default();
             }
             _ => {}
         }
@@ -660,23 +571,19 @@ impl Login {
                 .offline_info_bar_label
                 .set_label(&gettext("No network connection"));
             priv_.offline_info_bar.set_revealed(true);
-            self.update_next_action();
-            priv_.sso_box.set_sensitive(false);
-            priv_.more_sso_option.set_sensitive(false);
+            self.action_set_enabled("login.sso", false);
         } else if monitor.connectivity() < gio::NetworkConnectivity::Full {
             priv_
                 .offline_info_bar_label
                 .set_label(&gettext("No Internet connection"));
             priv_.offline_info_bar.set_revealed(true);
-            self.update_next_action();
-            priv_.sso_box.set_sensitive(true);
-            priv_.more_sso_option.set_sensitive(true);
+            self.action_set_enabled("login.sso", true);
         } else {
             priv_.offline_info_bar.set_revealed(false);
-            self.update_next_action();
-            priv_.sso_box.set_sensitive(true);
-            priv_.more_sso_option.set_sensitive(true);
+            self.action_set_enabled("login.sso", true);
         }
+
+        self.update_next_state();
     }
 }
 
@@ -685,19 +592,3 @@ impl Default for Login {
         Self::new()
     }
 }
-
-fn build_server_name(server: &str) -> Result<OwnedServerName, IdParseError> {
-    let server = server
-        .strip_prefix("http://";)
-        .or_else(|| server.strip_prefix("https://";))
-        .unwrap_or(server);
-    ServerName::parse(server)
-}
-
-fn build_homeserver_url(server: &str) -> Result<Url, ParseError> {
-    if server.starts_with("http://";) || server.starts_with("https://";) {
-        Url::parse(server)
-    } else {
-        Url::parse(&format!("https://{}";, server))
-    }
-}
diff --git a/src/login/sso_page.rs b/src/login/sso_page.rs
new file mode 100644
index 000000000..ba90bfccf
--- /dev/null
+++ b/src/login/sso_page.rs
@@ -0,0 +1,51 @@
+use adw::{prelude::*, subclass::prelude::BinImpl};
+use gtk::{self, glib, subclass::prelude::*, CompositeTemplate};
+
+mod imp {
+    use glib::subclass::InitializingObject;
+
+    use super::*;
+
+    #[derive(Debug, Default, CompositeTemplate)]
+    #[template(resource = "/org/gnome/Fractal/login-sso-page.ui")]
+    pub struct LoginSsoPage {}
+
+    #[glib::object_subclass]
+    impl ObjectSubclass for LoginSsoPage {
+        const NAME: &'static str = "LoginSsoPage";
+        type Type = super::LoginSsoPage;
+        type ParentType = adw::Bin;
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        fn instance_init(obj: &InitializingObject<Self>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for LoginSsoPage {}
+
+    impl WidgetImpl for LoginSsoPage {}
+
+    impl BinImpl for LoginSsoPage {}
+}
+
+glib::wrapper! {
+    /// A widget handling the login flows.
+    pub struct LoginSsoPage(ObjectSubclass<imp::LoginSsoPage>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl LoginSsoPage {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create LoginSsoPage")
+    }
+}
+
+impl Default for LoginSsoPage {
+    fn default() -> Self {
+        Self::new()
+    }
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]