Improve handling of GOA accounts in the account editor

Show GNOME settings when adding a supported GOA service type and
it is installed, otherwise show Geary's custom password-based impl.

For GOA accounts, hide the Remove button, and enable opening the
account in GNOME Settings from the server pane.
This commit is contained in:
Michael Gratton 2018-09-09 18:39:44 +10:00 committed by Michael James Gratton
parent b37d146a2d
commit 3c06c0969e
8 changed files with 192 additions and 14 deletions

View file

@ -48,6 +48,9 @@ internal class Accounts.EditorEditPane : Gtk.Grid, EditorPane, AccountPane {
[GtkChild]
private Gtk.Button undo_button;
[GtkChild]
private Gtk.Button remove_button;
public EditorEditPane(Editor editor, Geary.AccountInformation account) {
this.editor = editor;
@ -106,6 +109,10 @@ internal class Accounts.EditorEditPane : Gtk.Grid, EditorPane, AccountPane {
this.settings_list.set_header_func(Editor.seperator_headers);
this.settings_list.add(new EmailPrefetchRow(this));
this.remove_button.set_visible(
!this.editor.accounts.is_goa_account(account)
);
this.account.information_changed.connect(on_account_changed);
update_header();
@ -189,7 +196,9 @@ internal class Accounts.EditorEditPane : Gtk.Grid, EditorPane, AccountPane {
[GtkCallback]
private void on_remove_account_clicked() {
this.editor.push(new EditorRemovePane(this.editor, this.account));
if (!this.editor.accounts.is_goa_account(account)) {
this.editor.push(new EditorRemovePane(this.editor, this.account));
}
}
[GtkCallback]

View file

@ -28,9 +28,9 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
}
protected weak Accounts.Editor editor { get; set; }
internal Manager accounts { get; private set; }
private Manager accounts;
protected weak Accounts.Editor editor { get; set; }
private Application.CommandStack commands = new Application.CommandStack();
@ -67,8 +67,10 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
public EditorListPane(Editor editor) {
this.editor = editor;
this.accounts =
((GearyApplication) editor.application).controller.account_manager;
// keep our own copy of this so we can disconnect from its signals
// without worrying about the editor's lifecycle
this.accounts = editor.accounts;
this.pane_content.set_focus_vadjustment(this.pane_adjustment);
@ -404,7 +406,25 @@ private class Accounts.AddServiceProviderRow : EditorRow<EditorListPane> {
}
public override void activated(EditorListPane pane) {
pane.show_add_account(this.provider);
pane.accounts.add_goa_account.begin(
this.provider, null,
(obj, res) => {
bool add_local = false;
try {
pane.accounts.add_goa_account.end(res);
} catch (Error.INVALID 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_add_account(this.provider);
}
});
}
}

View file

@ -51,7 +51,9 @@ internal class Accounts.EditorServersPane : Gtk.Grid, EditorPane, AccountPane {
);
// Only add an account provider if it is esoteric enough.
if (this.account.imap.mediator is GoaMediator) {
this.details_list.add(new AccountProviderRow(this.account));
this.details_list.add(
new AccountProviderRow(editor.accounts, this.account)
);
}
this.details_list.add(new SaveDraftsRow(this.account));
@ -119,14 +121,25 @@ internal class Accounts.EditorServersPane : Gtk.Grid, EditorPane, AccountPane {
update_header();
}
[GtkCallback]
private void on_activate(Gtk.ListBoxRow row) {
Accounts.EditorRow<EditorServersPane> server_row =
row as Accounts.EditorRow<EditorServersPane>;
if (server_row != null) {
server_row.activated(this);
}
}
}
private class Accounts.AccountProviderRow :
AccountRow<EditorServersPane,Gtk.Label> {
private Manager accounts;
public AccountProviderRow(Geary.AccountInformation account) {
public AccountProviderRow(Manager accounts,
Geary.AccountInformation account) {
base(
account,
// Translators: This label describes the program that
@ -136,21 +149,48 @@ private class Accounts.AccountProviderRow :
new Gtk.Label("")
);
// Can't change this, so dim it out
this.value.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
this.set_activatable(false);
this.accounts = accounts;
update();
}
public override void update() {
string? source = null;
bool enabled = false;
if (this.account.imap.mediator is GoaMediator) {
source = _("GNOME Online Accounts");
enabled = true;
} else {
source = _("Geary");
}
this.value.set_text(source);
this.set_activatable(enabled);
Gtk.StyleContext style = this.value.get_style_context();
if (enabled) {
style.remove_class(Gtk.STYLE_CLASS_DIM_LABEL);
} else {
style.add_class(Gtk.STYLE_CLASS_DIM_LABEL);
}
}
public override void activated(EditorServersPane pane) {
if (this.accounts.is_goa_account(this.account)) {
this.accounts.show_goa_account.begin(
account, null,
(obj, res) => {
try {
this.accounts.show_goa_account.end(res);
} catch (GLib.Error err) {
// XXX display an error to the user
debug(
"Failed to show GOA account \"%s\": %s",
account.id,
err.message
);
}
});
}
}
}

View file

@ -27,9 +27,11 @@ public class Accounts.Editor : Gtk.Dialog {
}
private SimpleActionGroup actions = new SimpleActionGroup();
internal Manager accounts { get; private set; }
private SimpleActionGroup actions = new SimpleActionGroup();
private Gtk.Stack editor_panes = new Gtk.Stack();
private EditorListPane editor_list_pane;
@ -39,6 +41,7 @@ public class Accounts.Editor : Gtk.Dialog {
public Editor(GearyApplication application, Gtk.Window parent) {
this.application = application;
this.accounts = application.controller.account_manager;
set_default_size(700, 450);
set_icon_name(GearyApplication.APP_ID);

View file

@ -417,6 +417,56 @@ public class Accounts.Manager : GLib.Object {
}
}
/**
* Determines if an account is a GOA account or not.
*/
public bool is_goa_account(Geary.AccountInformation account) {
return (account.imap is GoaServiceInformation);
}
/**
* Opens GNOME Settings to add an account of a particular type.
*
* Throws an error if it was not possible to open GNOME Settings,
* or if the given type is not supported for by GOA.
*/
public async void add_goa_account(Geary.ServiceProvider type,
GLib.Cancellable? cancellable)
throws GLib.Error {
switch (type) {
case Geary.ServiceProvider.GMAIL:
yield open_goa_settings("add", "google", cancellable);
break;
case Geary.ServiceProvider.OUTLOOK:
yield open_goa_settings("add", "windows_live", cancellable);
break;
default:
throw new Error.INVALID("Not supported for GOA");
}
}
/**
* Opens GOA settings for the given account in GNOME Settings.
*
* Throws an error if it was not possible to open GNOME Settings,
* or if the given account is not backed by GOA.
*/
public async void show_goa_account(Geary.AccountInformation account,
GLib.Cancellable? cancellable)
throws GLib.Error {
GoaServiceInformation? goa_service =
account.imap as GoaServiceInformation;
if (goa_service == null) {
throw new Error.INVALID("Not a GOA Account");
}
yield open_goa_settings(
goa_service.account.account.id, null, cancellable
);
}
/**
* Loads an account info from a config directory.
*
@ -864,6 +914,59 @@ public class Accounts.Manager : GLib.Object {
}
}
private async void open_goa_settings(string action,
string? param,
GLib.Cancellable? cancellable)
throws GLib.Error {
// This method was based on the implementation from:
// https://gitlab.gnome.org/GNOME/gnome-calendar/blob/master/src/gcal-source-dialog.c,
// Courtesy Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
GLib.DBusProxy settings = yield new GLib.DBusProxy.for_bus(
GLib.BusType.SESSION,
GLib.DBusProxyFlags.NONE,
null,
"org.gnome.ControlCenter",
"/org/gnome/ControlCenter",
"org.gtk.Actions",
cancellable
);
// @s "launch-panel"
// @av [<@(sav) ("online-accounts", [<@s "add">, <@s "google">])>]
// @a{sv} {}
GLib.Variant[] args = new GLib.Variant[] {
new GLib.Variant.variant(new GLib.Variant.string(action))
};
if (param != null) {
args += new GLib.Variant.variant(new GLib.Variant.string(param));
}
GLib.Variant command = new GLib.Variant.tuple(
new GLib.Variant[] {
new GLib.Variant.string("online-accounts"),
new GLib.Variant.array(GLib.VariantType.VARIANT, args)
}
);
GLib.Variant params = new GLib.Variant.tuple(
new GLib.Variant[] {
new GLib.Variant.string("launch-panel"),
new GLib.Variant.array(
GLib.VariantType.VARIANT,
new GLib.Variant[] {
new GLib.Variant.variant(command)
}
),
new GLib.Variant("a{sv}")
}
);
yield settings.call(
"Activate", params, GLib.DBusCallFlags.NONE, -1, cancellable
);
}
private void on_goa_account_added(Goa.Object account) {
// XXX get a cancellable for this.
this.create_goa_account.begin(account, null);

View file

@ -10,7 +10,7 @@
public class GoaServiceInformation : Geary.ServiceInformation {
private Goa.Object account;
internal Goa.Object account { get; private set; }
public GoaServiceInformation(Geary.Protocol protocol,
GoaMediator mediator,

View file

@ -254,7 +254,7 @@
</packing>
</child>
<child>
<object class="GtkButton">
<object class="GtkButton" id="remove_button">
<property name="label" translatable="yes" comments="This is the remove account button in the account settings.">Remove Account</property>
<property name="visible">True</property>
<property name="can_focus">True</property>

View file

@ -93,6 +93,7 @@
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" swapped="no"/>
</object>
</child>
<child type="label_item">
@ -132,6 +133,7 @@
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" swapped="no"/>
</object>
</child>
<child type="label_item">
@ -171,6 +173,7 @@
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" swapped="no"/>
</object>
</child>
<child type="label_item">