You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

207 lines
6.3 KiB

use adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
use gtk::glib;
use ruma::OwnedServerName;
use tracing::warn;
use url::Url;
use super::Login;
use crate::{components::LoadingButton, gettext_f, prelude::*, spawn_tokio, toast};
mod imp {
use glib::subclass::InitializingObject;
use super::*;
#[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/login/method_page.ui")]
#[properties(wrapper_type = super::LoginMethodPage)]
pub struct LoginMethodPage {
#[template_child]
title: TemplateChild<gtk::Label>,
#[template_child]
homeserver_url: TemplateChild<gtk::Label>,
#[template_child]
username_entry: TemplateChild<adw::EntryRow>,
#[template_child]
password_entry: TemplateChild<adw::PasswordEntryRow>,
#[template_child]
sso_button: TemplateChild<gtk::Button>,
#[template_child]
next_button: TemplateChild<LoadingButton>,
/// The parent `Login` object.
#[property(get, set, nullable)]
login: glib::WeakRef<Login>,
}
#[glib::object_subclass]
impl ObjectSubclass for LoginMethodPage {
const NAME: &'static str = "LoginMethodPage";
type Type = super::LoginMethodPage;
type ParentType = adw::NavigationPage;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
Self::bind_template_callbacks(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for LoginMethodPage {}
impl WidgetImpl for LoginMethodPage {
fn grab_focus(&self) -> bool {
self.username_entry.grab_focus()
}
}
impl NavigationPageImpl for LoginMethodPage {
fn shown(&self) {
self.grab_focus();
}
}
#[gtk::template_callbacks]
impl LoginMethodPage {
/// The username entered by the user.
fn username(&self) -> glib::GString {
self.username_entry.text()
}
/// The password entered by the user.
fn password(&self) -> glib::GString {
self.password_entry.text()
}
/// Update the domain name and URL displayed in the title.
pub(super) fn update_title(
&self,
homeserver_url: &Url,
server_name: Option<&OwnedServerName>,
) {
let title = if let Some(server_name) = server_name {
gettext_f(
// Translators: Do NOT translate the content between '{' and '}', this is a
// variable name.
"Log in to {domain_name}",
&[(
"domain_name",
&format!("<span segment=\"word\">{server_name}</span>"),
)],
)
} else {
gettext("Log in")
};
self.title.set_markup(&title);
let homeserver_url = homeserver_url.as_str().trim_end_matches('/');
self.homeserver_url.set_label(homeserver_url);
}
/// Update the SSO group.
pub(super) fn update_sso(&self, supports_sso: bool) {
self.sso_button.set_visible(supports_sso);
}
/// Whether the current state allows to login with a password.
fn can_login_with_password(&self) -> bool {
let username_length = self.username().len();
let password_length = self.password().len();
username_length != 0 && password_length != 0
}
/// Update the state of the "Next" button.
#[template_callback]
pub(super) fn update_next_state(&self) {
self.next_button
.set_sensitive(self.can_login_with_password());
}
/// Login with the password login type.
#[template_callback]
async fn login_with_password(&self) {
if !self.can_login_with_password() {
return;
}
let Some(login) = self.login.upgrade() else {
return;
};
self.next_button.set_is_loading(true);
login.freeze();
let username = self.username();
let password = self.password();
let client = login.client().await.unwrap();
let handle = spawn_tokio!(async move {
client
.matrix_auth()
.login_username(&username, &password)
.initial_device_display_name("Fractal")
.send()
.await
});
match handle.await.expect("task was not aborted") {
Ok(_) => {
login.create_session().await;
}
Err(error) => {
warn!("Could not log in: {error}");
toast!(self.obj(), error.to_user_facing());
}
}
self.next_button.set_is_loading(false);
login.unfreeze();
}
/// Reset this page.
pub(super) fn clean(&self) {
self.username_entry.set_text("");
self.password_entry.set_text("");
self.next_button.set_is_loading(false);
self.update_next_state();
}
}
}
glib::wrapper! {
/// The login page allowing to login via password or to choose a SSO provider.
pub struct LoginMethodPage(ObjectSubclass<imp::LoginMethodPage>)
@extends gtk::Widget, adw::NavigationPage,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}
impl LoginMethodPage {
/// The tag for this page.
pub(super) const TAG: &str = "method";
pub fn new() -> Self {
glib::Object::new()
}
/// Update this page with the given data.
pub(crate) fn update(
&self,
homeserver_url: &Url,
domain_name: Option<&OwnedServerName>,
supports_sso: bool,
) {
let imp = self.imp();
imp.update_title(homeserver_url, domain_name);
imp.update_sso(supports_sso);
imp.update_next_state();
}
/// Reset this page.
pub(crate) fn clean(&self) {
self.imp().clean();
}
}