[geary/gnumdk/autoconfig: 1/2] client: accounts: Add support for Thunderbird autoconfig
- From: Cédric Bellegarde <cbellegarde src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/gnumdk/autoconfig: 1/2] client: accounts: Add support for Thunderbird autoconfig
- Date: Thu, 25 Aug 2022 21:36:46 +0000 (UTC)
commit 6b8ed231ae4ebd15e665e8f959632febb69e9ce5
Author: Cédric Bellegarde <cedric.bellegarde@ædishatz.org>
Date: Thu Aug 25 23:12:45 2022 +0200
client: accounts: Add support for Thunderbird autoconfig
- Auto detect server settings
- Rework accounts editor add panel
Fix #1390
Fix #1350
meson.build | 1 +
src/client/accounts/accounts-autoconfig.vala | 170 +++++++++++
src/client/accounts/accounts-editor-add-pane.vala | 336 ++++++++++++++-------
src/client/accounts/accounts-editor-list-pane.vala | 103 +------
src/client/meson.build | 1 +
ui/accounts_editor_add_pane.ui | 179 ++++++-----
ui/accounts_editor_list_pane.ui | 58 +---
7 files changed, 496 insertions(+), 352 deletions(-)
---
diff --git a/meson.build b/meson.build
index 1be205e31..df2ee5a86 100644
--- a/meson.build
+++ b/meson.build
@@ -93,6 +93,7 @@ libhandy = dependency('libhandy-1', version: '>= 1.2.1', required: false)
libmath = cc.find_library('m')
libpeas = dependency('libpeas-1.0', version: '>= 1.24.0')
libsecret = dependency('libsecret-1', version: '>= 0.11')
+libsoup = dependency('libsoup-3.0')
libstemmer_dep = cc.find_library('stemmer')
libunwind_dep = dependency(
'libunwind', version: '>= 1.1', required: get_option('libunwind')
diff --git a/src/client/accounts/accounts-autoconfig.vala b/src/client/accounts/accounts-autoconfig.vala
new file mode 100644
index 000000000..89684d7b7
--- /dev/null
+++ b/src/client/accounts/accounts-autoconfig.vala
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2022 Cédric Bellegarde <cedric bellegarde adishatz org>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * Thunderbird autoconfig XML values
+ */
+internal class Accounts.AutoConfigValues {
+ // emailProvider.id
+ public string id { get; set; default = ""; }
+
+ // incomingServer[type="imap"].hostname
+ public string imap_server { get; set; default = ""; }
+ // incomingServer[type="imap"].port
+ public string imap_port { get; set; default = ""; }
+ // incomingServer[type="imap"].socketType
+ public Geary.TlsNegotiationMethod imap_tls_method {
+ get; set; default = Geary.TlsNegotiationMethod.TRANSPORT;
+ }
+
+ // outgoingServer[type="smtp"].hostname
+ public string smtp_server{ get; set; default = ""; }
+ // outgoingServer[type="smtp"].port
+ public string smtp_port { get; set; default = ""; }
+ // outgoingServer[type="smtp"].socketType
+ public Geary.TlsNegotiationMethod smtp_tls_method {
+ get; set; default = Geary.TlsNegotiationMethod.TRANSPORT;
+ }
+
+}
+
+internal errordomain Accounts.AutoConfigError {
+ ERROR
+}
+
+/**
+ * An account autoconfiguration helper
+ */
+internal class Accounts.AutoConfig {
+
+ private static string AUTOCONFIG_BASE_URI = "https://autoconfig.thunderbird.net/v1.1/";
+ private static string AUTOCONFIG_PATH = "/mail/config-v1.1.xml";
+
+ private unowned GLib.Cancellable cancellable;
+
+ internal AutoConfig(GLib.Cancellable auto_config_cancellable) {
+ cancellable = auto_config_cancellable;
+ }
+
+ public async AutoConfigValues get_config(string hostname)
+ throws AutoConfigError {
+ AutoConfigValues auto_config_values;
+
+ // First try to get config from mail domain, then from thunderbird
+ try {
+ auto_config_values = yield get_config_for_uri(
+ "http://autoconfig." + hostname + AUTOCONFIG_PATH
+ );
+ } catch (AutoConfigError err) {
+ auto_config_values = yield get_config_for_uri(
+ AUTOCONFIG_BASE_URI + hostname
+ );
+ }
+ return auto_config_values;
+ }
+
+ private async AutoConfigValues get_config_for_uri(string uri)
+ throws AutoConfigError {
+ GLib.InputStream stream;
+ var session = new Soup.Session();
+ var msg = new Soup.Message("GET", uri);
+
+ try {
+ stream = yield session.send_async(
+ msg, Priority.DEFAULT, this.cancellable
+ );
+ } catch (GLib.Error err) {
+ throw new AutoConfigError.ERROR(err.message);
+ }
+
+ try {
+ var stdout_stream = new MemoryOutputStream.resizable();
+ yield stdout_stream.splice_async(
+ stream, 0, Priority.DEFAULT, null
+ );
+ stdout_stream.write("\0".data);
+ stdout_stream.close();
+ unowned var xml_data = (string) stdout_stream.get_data();
+ return get_config_for_xml(xml_data);
+ } catch (GLib.Error err) {
+ throw new AutoConfigError.ERROR(err.message);
+ } finally {
+ try {
+ yield stream.close_async();
+ } catch (GLib.Error err) {
+ // Oh well
+ }
+ }
+ }
+
+ private AutoConfigValues get_config_for_xml(string xml_data)
+ throws AutoConfigError {
+ unowned Xml.Doc doc = Xml.Parser.parse_memory(xml_data, xml_data.length);
+ if (doc == null) {
+ throw new AutoConfigError.ERROR("Invalid XML");
+ }
+
+ unowned Xml.Node root = doc.get_root_element();
+ unowned Xml.Node email_provider = get_node(root, "emailProvider");
+ unowned Xml.Node incoming_server = get_node(email_provider, "incomingServer");
+ unowned Xml.Node outgoing_server = get_node(email_provider, "outgoingServer");
+
+ if (incoming_server == null || outgoing_server == null) {
+ throw new AutoConfigError.ERROR("Invalid XML");
+ }
+
+ if (incoming_server.get_prop("type") != "imap" ||
+ outgoing_server.get_prop("type") != "smtp") {
+ throw new AutoConfigError.ERROR("Unsupported protocol");
+ }
+
+ var auto_config_values = new AutoConfigValues();
+
+ auto_config_values.id = email_provider.get_prop("id");
+
+ auto_config_values.imap_server = get_node_value(incoming_server, "hostname");
+ auto_config_values.imap_port = get_node_value(incoming_server, "port");
+ auto_config_values.imap_tls_method = get_tls_method(
+ get_node_value(incoming_server, "socketType")
+ );
+
+ auto_config_values.smtp_server = get_node_value(outgoing_server, "hostname");
+ auto_config_values.smtp_port = get_node_value(outgoing_server, "port");
+ auto_config_values.smtp_tls_method = get_tls_method(
+ get_node_value(outgoing_server, "socketType")
+ );
+
+ return auto_config_values;
+ }
+
+ private unowned Xml.Node? get_node(Xml.Node root, string name) {
+ for (unowned Xml.Node entry = root.children; entry != null; entry = entry.next) {
+ if (entry.type == Xml.ElementType.ELEMENT_NODE && entry.name == name) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ private string get_node_value(Xml.Node root, string name) {
+ unowned Xml.Node? node = get_node(root, name);
+ if (node == null)
+ return "";
+ return node.get_content();
+ }
+
+ private Geary.TlsNegotiationMethod get_tls_method(string method) {
+ switch (method) {
+ case "SSL":
+ return Geary.TlsNegotiationMethod.TRANSPORT;
+ case "STARTTLS":
+ return Geary.TlsNegotiationMethod.START_TLS;
+ default:
+ return Geary.TlsNegotiationMethod.NONE;
+ }
+ }
+}
diff --git a/src/client/accounts/accounts-editor-add-pane.vala
b/src/client/accounts/accounts-editor-add-pane.vala
index 647711f47..1eb4eda3a 100644
--- a/src/client/accounts/accounts-editor-add-pane.vala
+++ b/src/client/accounts/accounts-editor-add-pane.vala
@@ -37,31 +37,29 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
[GtkChild] private unowned Gtk.HeaderBar header;
- [GtkChild] private unowned Gtk.Grid pane_content;
+ [GtkChild] private unowned Gtk.Stack stack;
[GtkChild] private unowned Gtk.Adjustment pane_adjustment;
[GtkChild] private unowned Gtk.ListBox details_list;
- [GtkChild] private unowned Gtk.Grid receiving_panel;
-
[GtkChild] private unowned Gtk.ListBox receiving_list;
- [GtkChild] private unowned Gtk.Grid sending_panel;
-
[GtkChild] private unowned Gtk.ListBox sending_list;
- [GtkChild] private unowned Gtk.Button create_button;
+ [GtkChild] private unowned Gtk.Button action_button;
[GtkChild] private unowned Gtk.Button back_button;
- [GtkChild] private unowned Gtk.Spinner create_spinner;
+ [GtkChild] private unowned Gtk.Spinner action_spinner;
private NameRow real_name;
private EmailRow email = new EmailRow();
private string last_valid_email = "";
private string last_valid_hostname = "";
+ private GLib.Cancellable auto_config_cancellable = new GLib.Cancellable();
+
private HostnameRow imap_hostname = new HostnameRow(Geary.Protocol.IMAP);
private TransportSecurityRow imap_tls = new TransportSecurityRow();
private LoginRow imap_login = new LoginRow();
@@ -76,32 +74,19 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
private bool controls_valid = false;
- internal EditorAddPane(Editor editor, Geary.ServiceProvider provider) {
+ internal EditorAddPane(Editor editor) {
this.editor = editor;
- this.provider = provider;
+ this.provider = Geary.ServiceProvider.OTHER;
this.accounts = editor.application.controller.account_manager;
this.engine = editor.application.engine;
- this.pane_content.set_focus_vadjustment(this.pane_adjustment);
+ this.stack.set_focus_vadjustment(this.pane_adjustment);
this.details_list.set_header_func(Editor.seperator_headers);
this.receiving_list.set_header_func(Editor.seperator_headers);
this.sending_list.set_header_func(Editor.seperator_headers);
- if (provider != Geary.ServiceProvider.OTHER) {
- this.details_list.add(
- new ServiceProviderRow<EditorAddPane>(
- provider,
- // Translators: Label for adding an email account
- // account for a generic IMAP service provider.
- _("All others")
- )
- );
- this.receiving_panel.hide();
- this.sending_panel.hide();
- }
-
this.real_name = new NameRow(this.accounts.get_account_name());
this.details_list.add(this.real_name);
@@ -130,18 +115,14 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
this.smtp_password.validator.state_changed.connect(on_validated);
this.smtp_password.value.activate.connect(on_activated);
- if (provider == Geary.ServiceProvider.OTHER) {
- this.receiving_list.add(this.imap_hostname);
- this.receiving_list.add(this.imap_tls);
- this.receiving_list.add(this.imap_login);
- this.receiving_list.add(this.imap_password);
+ this.receiving_list.add(this.imap_hostname);
+ this.receiving_list.add(this.imap_tls);
+ this.receiving_list.add(this.imap_login);
+ this.receiving_list.add(this.imap_password);
- this.sending_list.add(this.smtp_hostname);
- this.sending_list.add(this.smtp_tls);
- this.sending_list.add(this.smtp_auth);
- } else {
- this.details_list.add(this.imap_password);
- }
+ this.sending_list.add(this.smtp_hostname);
+ this.sending_list.add(this.smtp_tls);
+ this.sending_list.add(this.smtp_auth);
}
internal Gtk.HeaderBar get_header() {
@@ -169,7 +150,8 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
account.outgoing = new_smtp_service();
account.untrusted_host.connect(on_untrusted_host);
- if (this.provider == Geary.ServiceProvider.OTHER) {
+ if (this.provider == Geary.ServiceProvider.OTHER &&
+ this.imap_hostname.get_visible()) {
bool imap_valid = false;
bool smtp_valid = false;
@@ -302,30 +284,22 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
Geary.Protocol.IMAP, this.provider
);
- if (this.provider == Geary.ServiceProvider.OTHER) {
- service.credentials = new Geary.Credentials(
- Geary.Credentials.Method.PASSWORD,
- this.imap_login.value.get_text().strip(),
- this.imap_password.value.get_text().strip()
- );
+ service.credentials = new Geary.Credentials(
+ Geary.Credentials.Method.PASSWORD,
+ this.imap_login.value.get_text().strip(),
+ this.imap_password.value.get_text().strip()
+ );
- Components.NetworkAddressValidator host =
- (Components.NetworkAddressValidator)
- this.imap_hostname.validator;
- GLib.NetworkAddress address = host.validated_address;
- service.host = address.hostname;
- service.port = (uint16) address.port;
- service.transport_security = this.imap_tls.value.method;
+ Components.NetworkAddressValidator host =
+ (Components.NetworkAddressValidator)
+ this.imap_hostname.validator;
+ GLib.NetworkAddress address = host.validated_address;
+ service.host = address.hostname;
+ service.port = (uint16) address.port;
+ service.transport_security = this.imap_tls.value.method;
- if (service.port == 0) {
- service.port = service.get_default_port();
- }
- } else {
- service.credentials = new Geary.Credentials(
- Geary.Credentials.Method.PASSWORD,
- this.email.value.get_text().strip(),
- this.imap_password.value.get_text().strip()
- );
+ if (service.port == 0) {
+ service.port = service.get_default_port();
}
return service;
@@ -336,90 +310,204 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
Geary.Protocol.SMTP, this.provider
);
- if (this.provider == Geary.ServiceProvider.OTHER) {
- service.credentials_requirement = this.smtp_auth.value.source;
- if (service.credentials_requirement ==
- Geary.Credentials.Requirement.CUSTOM) {
- service.credentials = new Geary.Credentials(
- Geary.Credentials.Method.PASSWORD,
- this.smtp_login.value.get_text().strip(),
- this.smtp_password.value.get_text().strip()
- );
- }
+ service.credentials_requirement = this.smtp_auth.value.source;
+ if (service.credentials_requirement ==
+ Geary.Credentials.Requirement.CUSTOM) {
+ service.credentials = new Geary.Credentials(
+ Geary.Credentials.Method.PASSWORD,
+ this.smtp_login.value.get_text().strip(),
+ this.smtp_password.value.get_text().strip()
+ );
+ }
- Components.NetworkAddressValidator host =
- (Components.NetworkAddressValidator)
- this.smtp_hostname.validator;
- GLib.NetworkAddress address = host.validated_address;
+ Components.NetworkAddressValidator host =
+ (Components.NetworkAddressValidator)
+ this.smtp_hostname.validator;
+ GLib.NetworkAddress address = host.validated_address;
- service.host = address.hostname;
- service.port = (uint16) address.port;
- service.transport_security = this.smtp_tls.value.method;
+ service.host = address.hostname;
+ service.port = (uint16) address.port;
+ service.transport_security = this.smtp_tls.value.method;
- if (service.port == 0) {
- service.port = service.get_default_port();
- }
+ if (service.port == 0) {
+ service.port = service.get_default_port();
}
return service;
}
private void check_validation() {
+ bool server_settings_visible = this.stack.get_visible_child_name() == "server_settings";
bool controls_valid = true;
- foreach (Gtk.ListBox list in new Gtk.ListBox[] {
+ Gtk.ListBox[] list_boxes;
+ if (server_settings_visible) {
+ list_boxes = new Gtk.ListBox[] {
this.details_list, this.receiving_list, this.sending_list
- }) {
- list.foreach((child) => {
+ };
+ } else {
+ list_boxes = new Gtk.ListBox[] { this.details_list };
+ }
+ foreach (Gtk.ListBox list_box in list_boxes) {
+ list_box.foreach((child) => {
AddPaneRow? validatable = child as AddPaneRow;
if (validatable != null && !validatable.validator.is_valid) {
controls_valid = false;
}
});
}
- this.create_button.set_sensitive(controls_valid);
+ this.action_button.set_sensitive(controls_valid);
this.controls_valid = controls_valid;
}
private void update_operation_ui(bool is_running) {
- this.create_spinner.visible = is_running;
- this.create_spinner.active = is_running;
- this.create_button.sensitive = !is_running;
+ this.action_spinner.visible = is_running;
+ this.action_spinner.active = is_running;
+ this.action_button.sensitive = !is_running;
this.back_button.sensitive = !is_running;
this.sensitive = !is_running;
}
+ private void switch_to_user_settings() {
+ this.stack.set_visible_child_name("user_settings");
+ this.action_button.set_label(_("_Next"));
+ this.action_button.set_sensitive(true);
+ this.action_button.get_style_context().remove_class("suggested-action");
+ }
+
+ private void switch_to_server_settings() {
+ this.stack.set_visible_child_name("server_settings");
+ this.action_button.set_label(_("_Create"));
+ this.action_button.set_sensitive(false);
+ this.action_button.get_style_context().add_class("suggested-action");
+ }
+
+ private void set_server_settings_from_autoconfig(AutoConfig auto_config,
+ GLib.AsyncResult res)
+ throws Accounts.AutoConfigError {
+ AutoConfigValues auto_config_values = auto_config.get_config.end(res);
+ Gtk.Entry imap_hostname_entry = this.imap_hostname.value;
+ Gtk.Entry smtp_hostname_entry = this.smtp_hostname.value;
+ TlsComboBox imap_tls_combo_box = this.imap_tls.value;
+ TlsComboBox smtp_tls_combo_box = this.smtp_tls.value;
+
+ imap_hostname_entry.text = auto_config_values.imap_server +
+ ":" + auto_config_values.imap_port;
+ smtp_hostname_entry.text = auto_config_values.smtp_server +
+ ":" + auto_config_values.smtp_port;
+ imap_tls_combo_box.method = auto_config_values.imap_tls_method;
+ smtp_tls_combo_box.method = auto_config_values.smtp_tls_method;
+
+ this.imap_hostname.hide();
+ this.smtp_hostname.hide();
+ this.imap_tls.hide();
+ this.smtp_tls.hide();
+
+ switch (auto_config_values.id) {
+ case "googlemail.com":
+ this.provider = Geary.ServiceProvider.GMAIL;
+ break;
+ case "hotmail.com":
+ this.provider = Geary.ServiceProvider.OUTLOOK;
+ break;
+ default:
+ this.provider = Geary.ServiceProvider.OTHER;
+ break;
+ }
+ }
+
+ private void set_server_settings_from_hostname(string hostname) {
+ Gtk.Entry imap_hostname_entry = this.imap_hostname.value;
+ Gtk.Entry smtp_hostname_entry = this.smtp_hostname.value;
+ string smtp_hostname = "smtp." + hostname;
+ string imap_hostname = "imap." + hostname;
+ string last_imap_hostname = "";
+ string last_smtp_hostname = "";
+
+ this.imap_hostname.show();
+ this.smtp_hostname.show();
+
+ if (this.last_valid_hostname != "") {
+ last_imap_hostname = "imap." + this.last_valid_hostname;
+ last_smtp_hostname = "smtp." + this.last_valid_hostname;
+ }
+ if (imap_hostname_entry.text == last_imap_hostname) {
+ imap_hostname_entry.text = imap_hostname;
+ }
+ if (smtp_hostname_entry.text == last_smtp_hostname) {
+ smtp_hostname_entry.text = smtp_hostname;
+ }
+ this.last_valid_hostname = hostname;
+ }
+
+ private void add_goa_account() {
+ this.accounts.add_goa_account.begin(
+ this.provider, this.op_cancellable,
+ (obj, res) => {
+ bool add_local = false;
+ try {
+ this.accounts.add_goa_account.end(res);
+ } catch (GLib.IOError.NOT_SUPPORTED err) {
+ // Not a supported type, so don't bother logging the error
+ add_local = true;
+ } catch (GLib.Error err) {
+ debug("Failed to add %s via GOA: %s",
+ this.provider.to_string(), err.message);
+ add_local = true;
+ }
+ // Google Mail does not support "Less secure apps" anymore
+ if (add_local) {
+ switch (this.provider) {
+ case Geary.ServiceProvider.GMAIL:
+ this.editor.add_notification(
+ new Components.InAppNotification(
+ // Translators: In-app notification label, when
+ // GNOME Online Accounts are missing
+ _("Online accounts are missing")
+ )
+ );
+ add_local = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (add_local) {
+ switch_to_server_settings();
+ } else {
+ this.editor.pop();
+ }
+ }
+ );
+ }
+
private void on_validated(Components.Validator.Trigger reason) {
check_validation();
if (this.controls_valid && reason == Components.Validator.Trigger.ACTIVATED) {
- this.create_button.clicked();
+ this.action_button.clicked();
}
}
private void on_activated() {
if (this.controls_valid) {
- this.create_button.clicked();
+ this.action_button.clicked();
}
}
private void on_email_changed() {
Gtk.Entry imap_login_entry = this.imap_login.value;
Gtk.Entry smtp_login_entry = this.smtp_login.value;
- Gtk.Entry imap_hostname_entry = this.imap_hostname.value;
- Gtk.Entry smtp_hostname_entry = this.smtp_hostname.value;
- string email = "";
- string hostname = "";
- string imap_hostname = "";
- string smtp_hostname = "";
- string last_imap_hostname = "";
- string last_smtp_hostname = "";
- if (this.email.validator.state == Components.Validator.Validity.VALID) {
- email = this.email.value.text;
- hostname = email.split("@")[1];
- smtp_hostname = "smtp." + hostname;
- imap_hostname = "imap." + hostname;
+ this.auto_config_cancellable.cancel();
+
+ if (this.email.validator.state != Components.Validator.Validity.VALID) {
+ return;
}
+ string email = this.email.value.text;
+ string hostname = email.split("@")[1];
+
+ // Do not update entries if changed by user
if (imap_login_entry.text == this.last_valid_email) {
imap_login_entry.text = email;
}
@@ -427,19 +515,23 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
smtp_login_entry.text = email;
}
- if (this.last_valid_hostname != "") {
- last_imap_hostname = "imap." + this.last_valid_hostname;
- last_smtp_hostname = "smtp." + this.last_valid_hostname;
- }
- if (imap_hostname_entry.text == last_imap_hostname) {
- imap_hostname_entry.text = imap_hostname;
- }
- if (smtp_hostname_entry.text == last_smtp_hostname) {
- smtp_hostname_entry.text = smtp_hostname;
- }
-
this.last_valid_email = email;
- this.last_valid_hostname = hostname;
+
+ // Try to get configuration from Thunderbird autoconfig service
+ this.action_spinner.visible = true;
+ this.action_spinner.active = true;
+ this.auto_config_cancellable = new GLib.Cancellable();
+ var auto_config = new AutoConfig(this.auto_config_cancellable);
+ auto_config.get_config.begin(hostname, (obj, res) => {
+ try {
+ set_server_settings_from_autoconfig(auto_config, res);
+ } catch (Accounts.AutoConfigError err) {
+ debug("Error getting auto configuration: %s", err.message);
+ set_server_settings_from_hostname(hostname);
+ }
+ this.action_spinner.visible = false;
+ this.action_spinner.active = false;
+ });
}
private void on_smtp_auth_changed() {
@@ -474,13 +566,29 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
}
[GtkCallback]
- private void on_create_button_clicked() {
- this.validate_account.begin(this.op_cancellable);
+ private void on_action_button_clicked() {
+ if (this.stack.get_visible_child_name() == "user_settings") {
+ switch (this.provider) {
+ case Geary.ServiceProvider.GMAIL:
+ case Geary.ServiceProvider.OUTLOOK:
+ add_goa_account();
+ break;
+ case Geary.ServiceProvider.OTHER:
+ switch_to_server_settings();
+ break;
+ }
+ } else {
+ this.validate_account.begin(this.op_cancellable);
+ }
}
[GtkCallback]
private void on_back_button_clicked() {
- this.editor.pop();
+ if (this.stack.get_visible_child_name() == "user_settings") {
+ this.editor.pop();
+ } else {
+ switch_to_user_settings();
+ }
}
[GtkCallback]
diff --git a/src/client/accounts/accounts-editor-list-pane.vala
b/src/client/accounts/accounts-editor-list-pane.vala
index c156eeaff..2f0918d83 100644
--- a/src/client/accounts/accounts-editor-list-pane.vala
+++ b/src/client/accounts/accounts-editor-list-pane.vala
@@ -31,7 +31,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
/** {@inheritDoc} */
internal Gtk.Widget initial_widget {
get {
- return this.show_welcome ? this.service_list : this.accounts_list;
+ return this.accounts_list;
}
}
@@ -73,10 +73,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
[GtkChild] private unowned Gtk.Frame accounts_list_frame;
- [GtkChild] private unowned Gtk.Label add_service_label;
-
- [GtkChild] private unowned Gtk.ListBox service_list;
-
private Gee.Map<Geary.AccountInformation,EditorEditPane> edit_pane_cache =
new Gee.HashMap<Geary.AccountInformation,EditorEditPane>();
@@ -97,12 +93,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
add_account(account, this.accounts.get_status(account));
}
- this.service_list.set_header_func(Editor.seperator_headers);
- this.service_list.add(new AddServiceProviderRow(Geary.ServiceProvider.GMAIL));
- this.service_list.add(new AddServiceProviderRow(Geary.ServiceProvider.OUTLOOK));
- this.service_list.add(new AddServiceProviderRow(Geary.ServiceProvider.YAHOO));
- this.service_list.add(new AddServiceProviderRow(Geary.ServiceProvider.OTHER));
-
this.accounts.account_added.connect(on_account_added);
this.accounts.account_status_changed.connect(on_account_status_changed);
this.accounts.account_removed.connect(on_account_removed);
@@ -128,8 +118,8 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
base.destroy();
}
- internal void show_new_account(Geary.ServiceProvider provider) {
- this.editor.push(new EditorAddPane(this.editor, provider));
+ internal void show_new_account() {
+ this.editor.push(new EditorAddPane(this.editor));
}
internal void show_existing_account(Geary.AccountInformation account) {
@@ -171,13 +161,11 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
// pane and service list.
this.welcome_panel.show();
this.accounts_list_frame.hide();
- this.add_service_label.hide();
} else {
// There are some accounts available, so show them and
// the full add service UI.
this.welcome_panel.hide();
this.accounts_list_frame.show();
- this.add_service_label.show();
}
}
@@ -266,21 +254,9 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
}
[GtkCallback]
- private bool on_list_keynav_failed(Gtk.Widget widget,
- Gtk.DirectionType direction) {
- bool ret = Gdk.EVENT_PROPAGATE;
- if (direction == Gtk.DirectionType.DOWN &&
- widget == this.accounts_list) {
- this.service_list.child_focus(direction);
- ret = Gdk.EVENT_STOP;
- } else if (direction == Gtk.DirectionType.UP &&
- widget == this.service_list) {
- this.accounts_list.child_focus(direction);
- ret = Gdk.EVENT_STOP;
- }
- return ret;
+ private void on_add_button_clicked() {
+ show_new_account();
}
-
}
@@ -424,75 +400,6 @@ private class Accounts.AccountListRow : AccountRow<EditorListPane,Gtk.Grid> {
}
-private class Accounts.AddServiceProviderRow : EditorRow<EditorListPane> {
-
-
- internal Geary.ServiceProvider provider;
-
- private Gtk.Label service_name = new Gtk.Label("");
- private Gtk.Image next_icon = new Gtk.Image.from_icon_name(
- "go-next-symbolic", Gtk.IconSize.SMALL_TOOLBAR
- );
-
-
- public AddServiceProviderRow(Geary.ServiceProvider provider) {
- this.provider = provider;
-
- // Translators: Label for adding a generic email account
- string? name = null;
- switch (provider) {
- case Geary.ServiceProvider.GMAIL:
- name = _("Gmail");
- break;
-
- case Geary.ServiceProvider.OUTLOOK:
- name = _("Outlook.com");
- break;
-
- case Geary.ServiceProvider.YAHOO:
- name = _("Yahoo");
- break;
-
- case Geary.ServiceProvider.OTHER:
- name = _("Other email providers");
- break;
- }
- this.service_name.set_text(name);
- this.service_name.set_hexpand(true);
- this.service_name.halign = Gtk.Align.START;
- this.service_name.show();
-
- this.next_icon.show();
-
- this.layout.add(this.service_name);
- this.layout.add(this.next_icon);
- }
-
- public override void activated(EditorListPane pane) {
- pane.accounts.add_goa_account.begin(
- this.provider, pane.op_cancellable,
- (obj, res) => {
- bool add_local = false;
- try {
- pane.accounts.add_goa_account.end(res);
- } catch (GLib.IOError.NOT_SUPPORTED err) {
- // Not a supported type, so don't bother logging the error
- add_local = true;
- } catch (GLib.Error err) {
- debug("Failed to add %s via GOA: %s",
- this.provider.to_string(), err.message);
- add_local = true;
- }
-
- if (add_local) {
- pane.show_new_account(this.provider);
- }
- });
- }
-
-}
-
-
internal class Accounts.ReorderAccountCommand : Application.Command {
diff --git a/src/client/meson.build b/src/client/meson.build
index 715358320..18ad1ecb1 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -34,6 +34,7 @@ client_vala_sources = files(
'application/goa-mediator.vala',
'application/secret-mediator.vala',
+ 'accounts/accounts-autoconfig.vala',
'accounts/accounts-editor.vala',
'accounts/accounts-editor-add-pane.vala',
'accounts/accounts-editor-edit-pane.vala',
diff --git a/ui/accounts_editor_add_pane.ui b/ui/accounts_editor_add_pane.ui
index cfe259021..8a38f67a9 100644
--- a/ui/accounts_editor_add_pane.ui
+++ b/ui/accounts_editor_add_pane.ui
@@ -7,34 +7,29 @@
<property name="title" translatable="yes">Add an account</property>
<property name="has_subtitle">False</property>
<child>
- <object class="GtkGrid">
+ <object class="GtkButton" id="back_button">
<property name="visible">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
<child>
- <object class="GtkButton" id="back_button">
+ <object class="GtkImage">
<property name="visible">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="no_show_all">True</property>
- <property name="icon_name">go-previous-symbolic</property>
- </object>
- </child>
+ <property name="no_show_all">True</property>
+ <property name="icon_name">go-previous-symbolic</property>
</object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
</child>
</object>
+ <packing>
+ <property name="pack_type">start</property>
+ <property name="position">1</property>
+ </packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="column_spacing">12</property>
<child>
- <object class="GtkSpinner" id="create_spinner">
+ <object class="GtkSpinner" id="action_spinner">
<property name="visible">True</property>
</object>
<packing>
@@ -43,16 +38,13 @@
</packing>
</child>
<child>
- <object class="GtkButton" id="create_button">
- <property name="label" translatable="yes">_Create</property>
+ <object class="GtkButton" id="action_button">
+ <property name="label" translatable="yes">_Next</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
- <signal name="clicked" handler="on_create_button_clicked" swapped="no"/>
- <style>
- <class name="suggested-action"/>
- </style>
+ <signal name="clicked" handler="on_action_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -89,13 +81,14 @@
<property name="visible">True</property>
<property name="margin">24</property>
<child>
- <object class="GtkGrid" id="pane_content">
+ <object class="GtkStack" id="stack">
<property name="visible">True</property>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
+ <property name="valign">start</property>
<child>
<object class="GtkListBox" id="details_list">
<property name="visible">True</property>
@@ -105,102 +98,98 @@
</child>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
+ <property name="name">user_settings</property>
</packing>
</child>
<child>
- <object class="GtkGrid" id="receiving_panel">
+ <object class="GtkBox">
<property name="visible">True</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkLabel">
+ <object class="GtkGrid" id="receiving_panel">
<property name="visible">True</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Receiving</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- <style>
- <class name="geary-settings-heading"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame">
- <property name="visible">True</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
<child>
- <object class="GtkListBox" id="receiving_list">
+ <object class="GtkLabel">
<property name="visible">True</property>
- <property name="selection_mode">none</property>
- <signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Receiving</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="geary-settings-heading"/>
+ </style>
</object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="receiving_list">
+ <property name="visible">True</property>
+ <property name="selection_mode">none</property>
+ <signal name="keynav-failed" handler="on_list_keynav_failed"
swapped="no"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid" id="sending_panel">
- <property name="visible">True</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Sending</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- <style>
- <class name="geary-settings-heading"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
</child>
<child>
- <object class="GtkFrame">
+ <object class="GtkGrid" id="sending_panel">
<property name="visible">True</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
<child>
- <object class="GtkListBox" id="sending_list">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Sending</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="geary-settings-heading"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame">
<property name="visible">True</property>
- <property name="selection_mode">none</property>
- <signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="sending_list">
+ <property name="visible">True</property>
+ <property name="selection_mode">none</property>
+ <signal name="keynav-failed" handler="on_list_keynav_failed"
swapped="no"/>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
</child>
</object>
<packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
+ <property name="name">server_settings</property>
</packing>
</child>
- <style>
- <class name="geary-accounts-editor-pane-content"/>
- </style>
-
</object>
</child>
</object>
diff --git a/ui/accounts_editor_list_pane.ui b/ui/accounts_editor_list_pane.ui
index 2f5e6af27..975853a4b 100644
--- a/ui/accounts_editor_list_pane.ui
+++ b/ui/accounts_editor_list_pane.ui
@@ -7,6 +7,19 @@
<property name="title" translatable="yes">Accounts</property>
<property name="has_subtitle">False</property>
<property name="show_close_button">True</property>
+ <child>
+ <object class="GtkButton" id="add_button">
+ <property name="label" translatable="yes">_Add</property>
+ <property name="tooltip_text" translatable="yes">Add an account</property>
+ <property name="visible">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="on_add_button_clicked"/>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
</object>
<object class="GtkAdjustment" id="pane_adjustment">
<property name="upper">100</property>
@@ -103,7 +116,6 @@
<object class="GtkListBox" id="accounts_list">
<property name="visible">True</property>
<property name="selection_mode">none</property>
- <signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_row_activated" swapped="no"/>
</object>
</child>
@@ -115,53 +127,9 @@
<property name="top_attach">1</property>
</packing>
</child>
- <child>
- <object class="GtkLabel" id="add_service_label">
- <property name="visible">True</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Add an account</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- <style>
- <class name="geary-settings-heading"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame">
- <property name="visible">True</property>
- <property name="valign">start</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkListBox" id="service_list">
- <property name="width_request">0</property>
- <property name="visible">True</property>
- <property name="valign">start</property>
- <property name="selection_mode">none</property>
- <signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
- <signal name="row-activated" handler="on_row_activated" swapped="no"/>
- </object>
- </child>
- <child type="label_item">
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
<style>
<class name="geary-accounts-editor-pane-content"/>
</style>
-
</object>
</child>
</object>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]