[fractal] login: Split in pages and cleanup
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] login: Split in pages and cleanup
- Date: Tue, 11 Oct 2022 13:50:04 +0000 (UTC)
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]