[fractal] fractal-gtk: Redesign Login Flow
- From: Daniel Garcia Moreno <danigm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] fractal-gtk: Redesign Login Flow
- Date: Mon, 15 Apr 2019 15:50:43 +0000 (UTC)
commit 66e95aa15157b05e971740341b7cf14c3cff77c4
Author: Christopher Davis <brainblasted disroot org>
Date: Sat Mar 30 01:49:26 2019 -0400
fractal-gtk: Redesign Login Flow
This is a complete overhaul over the interface for login.
* widgetifies the login view (removes app/connect/login.rs)
* Instead of asking for the identity server, uses .well-known
* No longer assumes a homeserver
Closes #448
fractal-gtk/res/app.css | 11 ++-
fractal-gtk/res/resources.xml | 1 +
fractal-gtk/res/ui/login_flow.ui | 177 +++++++++++++++++++++++++++++++++--
fractal-gtk/res/ui/main_window.ui | 6 +-
fractal-gtk/src/actions/login.rs | 129 +++++++++++++++++++++++++
fractal-gtk/src/actions/mod.rs | 3 +
fractal-gtk/src/app/backend_loop.rs | 1 +
fractal-gtk/src/app/connect/login.rs | 80 ----------------
fractal-gtk/src/app/connect/mod.rs | 2 -
fractal-gtk/src/app/mod.rs | 6 ++
fractal-gtk/src/appop/login.rs | 107 ---------------------
fractal-gtk/src/appop/mod.rs | 3 +-
fractal-gtk/src/appop/state.rs | 8 +-
fractal-gtk/src/globals.rs | 1 +
fractal-gtk/src/widgets/login.rs | 164 ++++++++++++++++++++++++++++++++
fractal-gtk/src/widgets/mod.rs | 2 +
16 files changed, 492 insertions(+), 209 deletions(-)
---
diff --git a/fractal-gtk/res/app.css b/fractal-gtk/res/app.css
index 4680520d..b968a482 100644
--- a/fractal-gtk/res/app.css
+++ b/fractal-gtk/res/app.css
@@ -317,4 +317,13 @@ stack.titlebar:not(headerbar) > box > separator {
}
.badge-grey {
background-color: #D9D9D9;
-}
\ No newline at end of file
+}
+
+button.forgot-password {
+ padding: 0px;
+ outline-offset: 0px;
+}
+
+.error-label {
+ color: @error_color;
+}
diff --git a/fractal-gtk/res/resources.xml b/fractal-gtk/res/resources.xml
index e5cb1da5..ae63d0d0 100644
--- a/fractal-gtk/res/resources.xml
+++ b/fractal-gtk/res/resources.xml
@@ -14,6 +14,7 @@
<file preprocess="xml-stripblanks">ui/invite_user.ui</file>
<file preprocess="xml-stripblanks">ui/join_room.ui</file>
<file preprocess="xml-stripblanks">ui/leave_room.ui</file>
+ <file preprocess="xml-stripblanks">ui/login_flow.ui</file>
<file preprocess="xml-stripblanks">ui/main_window.ui</file>
<file preprocess="xml-stripblanks">ui/scroll_widget.ui</file>
<file preprocess="xml-stripblanks">ui/members.ui</file>
diff --git a/fractal-gtk/res/ui/login_flow.ui b/fractal-gtk/res/ui/login_flow.ui
index a7a0e922..548870d4 100644
--- a/fractal-gtk/res/ui/login_flow.ui
+++ b/fractal-gtk/res/ui/login_flow.ui
@@ -5,6 +5,7 @@
<object class="GtkStack" id="login_flow_stack">
<property name="can_focus">False</property>
<property name="hhomogeneous">True</property>
+ <property name="transition_type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
<child>
<object class="GtkBox" id="login_greeter">
<property name="visible">True</property>
@@ -22,10 +23,7 @@
<property name="margin_end">18</property>
<property name="margin_top">18</property>
<property name="pixel_size">128</property>
- <property name="icon_name">user-available-symbolic</property>
- <style>
- <class name="dim-label"/>
- </style>
+ <property name="icon_name">chat-icon</property>
</object>
</child>
<child>
@@ -34,6 +32,8 @@
<property name="can_focus">False</property>
<property name="label" translatable="yes">Welcome to Fractal</property>
<property name="margin_bottom">48</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
<attributes>
<attribute name="weight" value="ultrabold"/>
<attribute name="scale" value="1.7"/>
@@ -44,6 +44,7 @@
<object class="GtkButton" id="login_button">
<property name="visible">True</property>
<property name="label" translatable="true">Log In</property>
+ <property name="action_name">login.server_chooser</property>
<property name="height-request">48</property>
<style>
<class name="suggested-action"/>
@@ -55,6 +56,7 @@
<property name="visible">True</property>
<property name="label" translatable="yes">Create Account</property>
<property name="height-request">48</property>
+ <property name="action_name">login.create-account</property>
</object>
</child>
</object>
@@ -77,7 +79,6 @@
<property name="halign">center</property>
<property name="margin_start">18</property>
<property name="margin_end">18</property>
- <property name="margin_top">18</property>
<property name="pixel_size">128</property>
<property name="icon_name">network-server-symbolic</property>
<style>
@@ -89,8 +90,10 @@
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">What is your Server?</property>
+ <property name="label" translatable="yes">What is your Provider?</property>
<property name="margin_bottom">30</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
<attributes>
<attribute name="weight" value="ultrabold"/>
<attribute name="scale" value="1.7"/>
@@ -106,15 +109,19 @@
<child>
<object class="GtkEntry" id="server_chooser_entry">
<property name="visible">True</property>
+ <property name="halign">center</property>
<property name="max_width_chars">-1</property>
- <property name="width_request">312</property>
+ <property name="width_request">300</property>
+ <property name="input_purpose">GTK_INPUT_PURPOSE_URL</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">The domain of your Matrix server, e.g.
myserver.co</property>
+ <property name="label" translatable="yes">Matrix provider domain, e.g. myserver.co</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
<style>
<class name="dim-label"/>
<class name="small-label"/>
@@ -123,6 +130,19 @@
</child>
</object>
</child>
+ <child>
+ <object class="GtkLabel" id="server_err_label">
+ <property name="visible">False</property>
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="label" translatable="yes">The domain may not be empty.</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+ <style>
+ <class name="error-label"/>
+ </style>
+ </object>
+ </child>
</object>
<packing>
<property name="name">server-chooser</property>
@@ -143,6 +163,8 @@
<property name="label" translatable="yes">User ID</property>
<property name="halign">end</property>
<property name="valign">end</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
<style>
<class name="dim-label"/>
</style>
@@ -154,6 +176,8 @@
<property name="can_focus">False</property>
<property name="label" translatable="yes">Password</property>
<property name="halign">end</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
<style>
<class name="dim-label"/>
</style>
@@ -183,6 +207,8 @@
<property name="can_focus">False</property>
<property name="label" translatable="yes">User name, email, or phone number</property>
<property name="halign">start</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
<style>
<class name="dim-label"/>
<class name="small-font"/>
@@ -201,12 +227,147 @@
<property name="max_width_chars">-1</property>
<property name="width_request">232</property>
<property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="input_purpose">GTK_INPUT_PURPOSE_PASSWORD</property>
</object>
<packing>
<property name="top-attach">3</property>
<property name="left-attach">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkLinkButton" id="forgot_password">
+ <property name="label" translatable="yes">Forgot Password?</property>
+ <property name="uri">https://riot.im/app/#/login</property>
+ <property name="halign">start</property>
+ <style>
+ <class name="forgot-password"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">4</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="credentials_err_label">
+ <property name="visible">False</property>
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Invalid username or password</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+ <style>
+ <class name="error-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">5</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">credentials</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkStack" id="login_flow_headers">
+ <property name="can_focus">False</property>
+ <property name="hhomogeneous">True</property>
+ <property name="visible_child_name" bind-source="login_flow_stack" bind-property="visible-child-name"
bind-flags="sync-create"/>
+ <property name="transition_duration" bind-source="login_flow_stack" bind-property="transition-duration"
bind-flags="sync-create"/>
+ <property name="transition_type" bind-source="login_flow_stack" bind-property="transition-type"
bind-flags="sync-create"/>
+ <child>
+ <object class="GtkHeaderBar" id="login_greeter_header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button">True</property>
+ <property name="title" translatable="yes">Fractal</property>
+ </object>
+ <packing>
+ <property name="name">greeter</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHeaderBar" id="login_server_chooser_header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button">True</property>
+ <property name="width_request">360</property>
+ <property name="title" translatable="yes">Choose Provider</property>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="action_name">login.back</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">start</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="action_name">login.credentials</property>
+ <property name="label" translatable="yes">Next</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">server-chooser</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHeaderBar" id="login_credentials_header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button">True</property>
+ <property name="title" translatable="yes">Log In</property>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="action_name">login.back</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">start</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="action_name">login.login</property>
+ <property name="label" translatable="yes">Log In</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="name">credentials</property>
diff --git a/fractal-gtk/res/ui/main_window.ui b/fractal-gtk/res/ui/main_window.ui
index db4626b7..680e3f52 100644
--- a/fractal-gtk/res/ui/main_window.ui
+++ b/fractal-gtk/res/ui/main_window.ui
@@ -608,7 +608,7 @@
</packing>
</child>
<child>
- <object class="GtkBox"> <!--login titlebar-->
+ <object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
@@ -621,8 +621,8 @@
</child>
</object>
<packing>
- <property name="name">login</property>
- <property name="title">login</property>
+ <property name="name">loading</property>
+ <property name="title">loading</property>
<property name="position">1</property>
</packing>
</child>
diff --git a/fractal-gtk/src/actions/login.rs b/fractal-gtk/src/actions/login.rs
new file mode 100644
index 00000000..0b82498e
--- /dev/null
+++ b/fractal-gtk/src/actions/login.rs
@@ -0,0 +1,129 @@
+use log::{debug, warn};
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use gio::prelude::*;
+use gio::SimpleAction;
+use gio::SimpleActionGroup;
+use gtk::prelude::*;
+
+use crate::globals;
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum LoginState {
+ Greeter,
+ ServerChooser,
+ Credentials,
+}
+
+impl From<String> for LoginState {
+ fn from(v: String) -> LoginState {
+ match v.as_str() {
+ "greeter" => LoginState::Greeter,
+ "server-chooser" => LoginState::ServerChooser,
+ "credentials" => LoginState::Credentials,
+ _ => panic!("Invalid back state type"),
+ }
+ }
+}
+
+impl ToString for LoginState {
+ fn to_string(&self) -> String {
+ let str = match self {
+ LoginState::Greeter => "greeter",
+ LoginState::ServerChooser => "server-chooser",
+ LoginState::Credentials => "credentials",
+ };
+
+ String::from(str)
+ }
+}
+
+pub fn new(
+ stack: >k::Stack,
+ headers: >k::Stack,
+ server_entry: >k::Entry,
+ err_label: >k::Label,
+) -> SimpleActionGroup {
+ let actions = SimpleActionGroup::new();
+
+ let create_account = SimpleAction::new("create-account", None);
+ let server_chooser = SimpleAction::new("server_chooser", None);
+ let credentials = SimpleAction::new("credentials", None);
+ let back = SimpleAction::new("back", None);
+ let login = SimpleAction::new("login", None);
+
+ actions.add_action(&create_account);
+ actions.add_action(&server_chooser);
+ actions.add_action(&credentials);
+ actions.add_action(&back);
+ actions.add_action(&login);
+
+ let stack_weak = stack.downgrade();
+ create_account.connect_activate(move |_, _| {
+ let stack = upgrade_weak!(stack_weak);
+
+ let toplevel = stack
+ .get_toplevel()
+ .expect("Could not grab toplevel widget")
+ .downcast::<gtk::Window>()
+ .expect("Could not cast toplevel to GtkWindow");
+ let uri = globals::RIOT_REGISTER_URL;
+ if let Err(e) = gtk::show_uri_on_window(&toplevel, uri, gtk::get_current_event_time()) {
+ warn!("Could not show {}: {}", uri, e)
+ }
+ });
+
+ let back_history: Rc<RefCell<Vec<LoginState>>> = Rc::new(RefCell::new(vec![]));
+
+ let back_weak = Rc::downgrade(&back_history);
+ let stack_weak = stack.downgrade();
+ server_chooser.connect_activate(move |_, _| {
+ let stack = upgrade_weak!(stack_weak);
+ let back = upgrade_weak!(back_weak);
+
+ let state = LoginState::ServerChooser;
+ stack.set_visible_child_name(&state.to_string());
+ back.borrow_mut().push(state);
+ });
+
+ let back_weak = Rc::downgrade(&back_history);
+ let stack_weak = stack.downgrade();
+ let server_weak = server_entry.downgrade();
+ let err_weak = err_label.downgrade();
+ credentials.connect_activate(move |_, _| {
+ let stack = upgrade_weak!(stack_weak);
+ let back = upgrade_weak!(back_weak);
+ let server_entry = upgrade_weak!(server_weak);
+ let err_label = upgrade_weak!(err_weak);
+
+ server_entry.get_text().map(|txt| {
+ if !txt.is_empty() {
+ err_label.hide();
+ let state = LoginState::Credentials;
+ stack.set_visible_child_name(&state.to_string());
+ back.borrow_mut().push(state);
+ } else {
+ err_label.show();
+ }
+ });
+ });
+
+ let stack_weak = stack.downgrade();
+ back.connect_activate(move |_, _| {
+ let stack = upgrade_weak!(stack_weak);
+ back_history.borrow_mut().pop();
+ if let Some(state) = back_history.borrow().last() {
+ debug!("Go back to state {}", state.to_string());
+ stack.set_visible_child_name(&state.to_string());
+ } else {
+ debug!("There is no state to go back to. Go back to state greeter");
+ stack.set_visible_child_name(&LoginState::Greeter.to_string());
+ }
+ });
+
+ stack.insert_action_group("login", &actions);
+ headers.insert_action_group("login", &actions);
+
+ actions
+}
diff --git a/fractal-gtk/src/actions/mod.rs b/fractal-gtk/src/actions/mod.rs
index 70697a83..72235b56 100644
--- a/fractal-gtk/src/actions/mod.rs
+++ b/fractal-gtk/src/actions/mod.rs
@@ -7,12 +7,15 @@ use gtk::WidgetExt;
pub mod account_settings;
pub mod global;
+pub mod login;
pub mod room_history;
pub mod room_settings;
pub use self::account_settings as AccountSettings;
pub use self::global as Global;
pub use self::global::AppState;
+pub use self::login as Login;
+pub use self::login::LoginState;
pub use self::room_history as RoomHistory;
pub use self::room_settings as RoomSettings;
diff --git a/fractal-gtk/src/app/backend_loop.rs b/fractal-gtk/src/app/backend_loop.rs
index 4b70e155..6e7b5a9f 100644
--- a/fractal-gtk/src/app/backend_loop.rs
+++ b/fractal-gtk/src/app/backend_loop.rs
@@ -239,6 +239,7 @@ pub fn backend_loop(rx: Receiver<BKResponse>) {
let error = i18n("Can’t login, try again");
let st = AppState::Login;
APPOP!(show_error, (error));
+ APPOP!(logout);
APPOP!(set_state, (st));
}
Ok(BKResponse::AttachFileError(err)) => {
diff --git a/fractal-gtk/src/app/connect/mod.rs b/fractal-gtk/src/app/connect/mod.rs
index c37f95e7..037eb15a 100644
--- a/fractal-gtk/src/app/connect/mod.rs
+++ b/fractal-gtk/src/app/connect/mod.rs
@@ -6,7 +6,6 @@ mod headerbar;
mod invite;
mod join_room;
mod leave_room;
-mod login;
mod markdown;
mod new_room;
mod roomlist_search;
@@ -17,7 +16,6 @@ use crate::app::App;
impl App {
pub fn connect_gtk(&self) {
self.connect_headerbars();
- self.connect_login_view();
self.connect_send();
self.connect_markdown();
diff --git a/fractal-gtk/src/app/mod.rs b/fractal-gtk/src/app/mod.rs
index 34ffaff4..e690b26f 100644
--- a/fractal-gtk/src/app/mod.rs
+++ b/fractal-gtk/src/app/mod.rs
@@ -16,6 +16,7 @@ use crate::backend::Backend;
use crate::actions;
use crate::globals;
use crate::uibuilder;
+use crate::widgets;
mod connect;
mod windowstate;
@@ -148,6 +149,11 @@ impl App {
let op = Arc::new(Mutex::new(AppOp::new(ui.clone(), apptx)));
+ // Add login view to the main stack
+ let login = widgets::LoginWidget::new(&op);
+ stack.add_named(&login.container, "login");
+ stack_header.add_named(&login.headers, "login");
+
unsafe {
OP = Some(Arc::downgrade(&op));
}
diff --git a/fractal-gtk/src/appop/login.rs b/fractal-gtk/src/appop/login.rs
index 21a265d9..b338d5b1 100644
--- a/fractal-gtk/src/appop/login.rs
+++ b/fractal-gtk/src/appop/login.rs
@@ -25,7 +25,6 @@ use crate::widgets::ErrorDialog;
impl AppOp {
pub fn bk_login(&mut self, uid: String, token: String, device: Option<String>) {
self.logged_in = true;
- self.clean_login();
if let Err(_) = self.store_token(uid.clone(), token) {
error!("Can't store the token using libsecret");
}
@@ -72,112 +71,6 @@ impl AppOp {
backend_loop(rx);
}
- pub fn clean_login(&self) {
- let user_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_username")
- .expect("Can't find login_username in ui file.");
- let pass_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_password")
- .expect("Can't find login_password in ui file.");
- let server_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_server")
- .expect("Can't find login_server in ui file.");
- let idp_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_idp")
- .expect("Can't find login_idp in ui file.");
-
- user_entry.set_text("");
- pass_entry.set_text("");
- server_entry.set_text(globals::DEFAULT_HOMESERVER);
- idp_entry.set_text(globals::DEFAULT_IDENTITYSERVER);
- }
-
- pub fn login(&mut self) {
- let user_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_username")
- .expect("Can't find login_username in ui file.");
- let pass_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_password")
- .expect("Can't find login_password in ui file.");
- let server_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_server")
- .expect("Can't find login_server in ui file.");
- let idp_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_idp")
- .expect("Can't find login_idp in ui file.");
- let login_error: gtk::Label = self
- .ui
- .builder
- .get_object("login_error_msg")
- .expect("Can't find login_error_msg in ui file.");
-
- let username = user_entry.get_text();
- let password = pass_entry.get_text();
- let server = server_entry.get_text();
- let identity = idp_entry.get_text();
-
- if username.clone().unwrap_or_default().is_empty()
- || password.clone().unwrap_or_default().is_empty()
- {
- login_error.set_text(i18n("Invalid username or password").as_str());
- login_error.show();
- return;
- } else {
- login_error.set_text(i18n("Unknown Error").as_str());
- login_error.hide();
- }
-
- /* FIXME: validate server and identity same as username and passwod */
-
- self.set_state(AppState::Loading);
- self.since = None;
- self.connect(username, password, server, identity);
- }
-
- pub fn set_login_pass(&self, username: &str, password: &str, server: &str, identity: &str) {
- let user_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_username")
- .expect("Can't find login_username in ui file.");
- let pass_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_password")
- .expect("Can't find login_password in ui file.");
- let server_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_server")
- .expect("Can't find login_server in ui file.");
- let idp_entry: gtk::Entry = self
- .ui
- .builder
- .get_object("login_idp")
- .expect("Can't find login_idp in ui file.");
-
- user_entry.set_text(username);
- pass_entry.set_text(password);
- server_entry.set_text(server);
- idp_entry.set_text(identity);
- }
-
#[allow(dead_code)]
pub fn register(&mut self) {
let user_entry: gtk::Entry = self
diff --git a/fractal-gtk/src/appop/mod.rs b/fractal-gtk/src/appop/mod.rs
index 9b3e79b6..af5fd931 100644
--- a/fractal-gtk/src/appop/mod.rs
+++ b/fractal-gtk/src/appop/mod.rs
@@ -33,7 +33,7 @@ mod notify;
mod room;
mod room_settings;
mod start_chat;
-mod state;
+pub mod state;
mod sync;
mod user;
@@ -135,7 +135,6 @@ impl AppOp {
if let Ok((token, uid)) = self.get_token() {
self.set_token(Some(token), Some(uid), Some(pass.2));
} else {
- self.set_login_pass(&pass.0, &pass.1, &pass.2, &pass.3);
self.connect(Some(pass.0), Some(pass.1), Some(pass.2), Some(pass.3));
}
} else {
diff --git a/fractal-gtk/src/appop/state.rs b/fractal-gtk/src/appop/state.rs
index cc33fa1a..7c9699ea 100644
--- a/fractal-gtk/src/appop/state.rs
+++ b/fractal-gtk/src/appop/state.rs
@@ -20,10 +20,7 @@ impl AppOp {
.expect("Can't find room_header_bar in ui file.");
let widget_name = match self.state {
- AppState::Login => {
- self.clean_login();
- "login"
- }
+ AppState::Login => "login",
AppState::NoRoom => {
self.set_state_no_room(&headerbar);
self.leaflet.set_visible_child_name("sidebar");
@@ -53,7 +50,7 @@ impl AppOp {
let bar_name = match self.state {
AppState::Login => "login",
AppState::Directory => "back",
- AppState::Loading => "login",
+ AppState::Loading => "loading",
AppState::AccountSettings => "account-settings",
AppState::RoomSettings => "room-settings",
AppState::MediaViewer => "media-viewer",
@@ -68,7 +65,6 @@ impl AppOp {
//set focus for views
let widget_focus = match self.state {
- AppState::Login => "login_username",
AppState::Directory => "directory_search_entry",
_ => "",
};
diff --git a/fractal-gtk/src/globals.rs b/fractal-gtk/src/globals.rs
index dd627f7a..0657f9d0 100644
--- a/fractal-gtk/src/globals.rs
+++ b/fractal-gtk/src/globals.rs
@@ -6,6 +6,7 @@ pub static MINUTES_TO_SPLIT_MSGS: i64 = 30;
pub static DEFAULT_HOMESERVER: &'static str = "https://matrix.org";
pub static DEFAULT_IDENTITYSERVER: &'static str = "https://vector.im";
pub static PLACEHOLDER_TEXT: &'static str = "Matrix username, email or phone number";
+pub static RIOT_REGISTER_URL: &'static str = "https://riot.im/app/#/register";
pub static MAX_IMAGE_SIZE: (i32, i32) = (600, 400);
pub static MAX_STICKER_SIZE: (i32, i32) = (200, 130);
diff --git a/fractal-gtk/src/widgets/login.rs b/fractal-gtk/src/widgets/login.rs
new file mode 100644
index 00000000..4ed2a0c9
--- /dev/null
+++ b/fractal-gtk/src/widgets/login.rs
@@ -0,0 +1,164 @@
+use gio::prelude::*;
+use gtk::prelude::*;
+use log::info;
+
+use crate::actions;
+use crate::actions::global::AppState;
+use crate::actions::login::LoginState;
+use crate::appop::AppOp;
+
+use fractal_api::backend::register::get_well_known;
+
+use std::sync::{Arc, Mutex};
+
+#[derive(Debug, Clone)]
+pub struct LoginWidget {
+ pub container: gtk::Stack,
+ pub headers: gtk::Stack,
+ pub server_entry: gtk::Entry,
+ pub username_entry: gtk::Entry,
+ pub password_entry: gtk::Entry,
+ server_err_label: gtk::Label,
+ credentials_err_label: gtk::Label,
+ actions: gio::SimpleActionGroup,
+}
+
+impl LoginWidget {
+ pub fn new(op: &Arc<Mutex<AppOp>>) -> Self {
+ let widget = Self::default();
+
+ let weak_server = widget.server_entry.downgrade();
+ let weak_username = widget.username_entry.downgrade();
+ let weak_password = widget.password_entry.downgrade();
+ let weak_err = widget.credentials_err_label.downgrade();
+
+ // Grab the focus for each state
+ let weak_ser = weak_server.clone();
+ let weak_user = weak_username.clone();
+ widget
+ .container
+ .connect_property_visible_child_name_notify(move |container| {
+ let server = upgrade_weak!(weak_ser);
+ let username = upgrade_weak!(weak_user);
+
+ let state: LoginState = container.get_visible_child_name().unwrap().into();
+
+ match state {
+ LoginState::ServerChooser => server.grab_focus(),
+ LoginState::Credentials => username.grab_focus(),
+ _ => (),
+ }
+ });
+
+ let op = op.clone();
+ let weak_server = widget.server_entry.downgrade();
+
+ let login = widget
+ .actions
+ .lookup_action("login")
+ .expect("Could not find 'login' action for LoginWidget")
+ .downcast::<gio::SimpleAction>()
+ .expect("Could not cast action 'login' to SimpleAction");
+
+ let weak_pass = weak_password.clone();
+ login.connect_activate(move |_, _| {
+ let server_entry = upgrade_weak!(weak_server);
+ let username_entry = upgrade_weak!(weak_username);
+ let password_entry = upgrade_weak!(weak_pass);
+ let err_label = upgrade_weak!(weak_err);
+
+ if let Some(txt) = server_entry.get_text() {
+ let username = username_entry.get_text().unwrap_or_default();
+ let password = password_entry.get_text().unwrap_or_default();
+
+ let txt = format!(
+ "{}{}",
+ "https://",
+ String::from(txt)
+ .trim()
+ .trim_start_matches("http://")
+ .trim_start_matches("https://")
+ );
+
+ if !password.is_empty() && !username.is_empty() {
+ // take the user's homeserver value if the
+ // well-known request fails
+ let mut homeserver_url = txt.clone();
+ let mut idserver = None;
+ match get_well_known(&txt) {
+ Ok(response) => {
+ info!("Got well-known response from {}: {:#?}", &txt, response);
+ homeserver_url = response.homeserver.unwrap_or(txt);
+ idserver = response.identity_server;
+ }
+ Err(e) => info!("Failed to .well-known request: {:#?}", e),
+ };
+
+ err_label.hide();
+ op.lock().unwrap().set_state(AppState::Loading);
+ op.lock().unwrap().since = None;
+ op.lock().unwrap().connect(
+ Some(username),
+ Some(password),
+ Some(homeserver_url),
+ idserver,
+ );
+ } else {
+ err_label.show();
+ }
+ }
+ });
+
+ let credentials = widget
+ .actions
+ .lookup_action("credentials")
+ .expect("Could not find 'credentials' action for LoginWidget")
+ .downcast::<gio::SimpleAction>()
+ .expect("Could not cast action 'credentials' to SimpleAction");
+ widget
+ .server_entry
+ .connect_activate(move |_| credentials.activate(None));
+
+ widget.username_entry.connect_activate(move |_| {
+ let password_entry = upgrade_weak!(weak_password);
+ password_entry.grab_focus();
+ });
+
+ widget
+ .password_entry
+ .connect_activate(move |_| login.activate(None));
+
+ widget
+ }
+}
+
+impl Default for LoginWidget {
+ fn default() -> Self {
+ let builder = gtk::Builder::new_from_resource("/org/gnome/Fractal/ui/login_flow.ui");
+
+ let container: gtk::Stack = builder.get_object("login_flow_stack").unwrap();
+ let headers: gtk::Stack = builder.get_object("login_flow_headers").unwrap();
+ let server_entry = builder.get_object("server_chooser_entry").unwrap();
+ let username_entry = builder.get_object("username_entry").unwrap();
+ let password_entry = builder.get_object("password_entry").unwrap();
+
+ let server_err_label = builder.get_object("server_err_label").unwrap();
+ let credentials_err_label = builder.get_object("credentials_err_label").unwrap();
+
+ let actions = actions::Login::new(&container, &headers, &server_entry, &server_err_label);
+
+ container.show_all();
+ headers.show_all();
+
+ LoginWidget {
+ container,
+ headers,
+ server_entry,
+ username_entry,
+ password_entry,
+ server_err_label,
+ credentials_err_label,
+ actions,
+ }
+ }
+}
diff --git a/fractal-gtk/src/widgets/mod.rs b/fractal-gtk/src/widgets/mod.rs
index 81b422f1..7cca372b 100644
--- a/fractal-gtk/src/widgets/mod.rs
+++ b/fractal-gtk/src/widgets/mod.rs
@@ -6,6 +6,7 @@ pub mod error_dialog;
pub mod file_dialog;
pub mod image;
mod inline_player;
+mod login;
pub mod media_viewer;
mod member;
pub mod members_list;
@@ -31,6 +32,7 @@ pub use self::divider::NewMessageDivider;
pub use self::error_dialog as ErrorDialog;
pub use self::file_dialog as FileDialog;
pub use self::inline_player::AudioPlayerWidget;
+pub use self::login::LoginWidget;
pub use self::media_viewer::MediaViewer;
pub use self::member::MemberBox;
pub use self::members_list::MembersList;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]