[geary/mjog/mutiple-main-windows: 6/14] Add Application.Controller::account_available and unavailable
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/mjog/mutiple-main-windows: 6/14] Add Application.Controller::account_available and unavailable
- Date: Mon, 18 Nov 2019 04:32:08 +0000 (UTC)
commit ab8755aa7820a7ce07635171061a421e664dc943
Author: Michael Gratton <mike vee net>
Date: Wed Nov 13 15:19:38 2019 +1100
Add Application.Controller::account_available and unavailable
Clean up methods for opening an account, emit the signals when accounts
have been opened/before being closed. Use these signals in MainWindow to
manage its own internal state rather than relying on the controller.
src/client/application/application-controller.vala | 197 ++++++++++----------
.../application/application-main-window.vala | 206 +++++++++++++--------
src/client/dialogs/upgrade-dialog.vala | 20 +-
3 files changed, 241 insertions(+), 182 deletions(-)
---
diff --git a/src/client/application/application-controller.vala
b/src/client/application/application-controller.vala
index 8bdef3ff..0c5889c7 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -34,7 +34,7 @@ internal class Application.Controller : Geary.BaseObject {
/** Determines if the controller is open. */
public bool is_open {
get {
- return !this.open_cancellable.is_cancelled();
+ return !this.controller_open.is_cancelled();
}
}
@@ -62,15 +62,13 @@ internal class Application.Controller : Geary.BaseObject {
new Gee.HashMap<Geary.AccountInformation,AccountContext>();
// Cancelled if the controller is closed
- private GLib.Cancellable open_cancellable;
+ private GLib.Cancellable controller_open;
private UpgradeDialog upgrade_dialog;
private Folks.IndividualAggregator folks;
private PluginManager plugin_manager;
- private Cancellable cancellable_open_account = new Cancellable();
-
// List composers that have not yet been closed
private Gee.Collection<Composer.Widget> composer_widgets =
new Gee.LinkedList<Composer.Widget>();
@@ -79,13 +77,38 @@ internal class Application.Controller : Geary.BaseObject {
private Gee.List<string?> pending_mailtos = new Gee.ArrayList<string>();
+ /**
+ * Emitted when an account is added or is enabled.
+ *
+ * This will be emitted after an account is opened and added to
+ * the controller.
+ */
+ public signal void account_available(AccountContext context);
+
+ /**
+ * Emitted when an account is removed or is disabled.
+ *
+ * This will be emitted after the account is removed from the
+ * controller's collection of accounts, but before the {@link
+ * AccountContext.cancellable} is cancelled and before the account
+ * itself is closed.
+ *
+ * The `is_shutdown` argument will be true if the application is
+ * in the middle of quitting, otherwise if the account was simply
+ * removed but the application will keep running, then it will be
+ * false.
+ */
+ public signal void account_unavailable(AccountContext context,
+ bool is_shutdown);
+
+
/**
* Constructs a new instance of the controller.
*/
public async Controller(Client application,
GLib.Cancellable cancellable) {
this.application = application;
- this.open_cancellable = cancellable;
+ this.controller_open = cancellable;
Geary.Engine engine = this.application.engine;
@@ -139,9 +162,8 @@ internal class Application.Controller : Geary.BaseObject {
);
this.plugin_manager.load();
- // Create the main window (must be done after creating actions.)
- main_window = new MainWindow(this.application);
- main_window.retry_service_problem.connect(on_retry_service_problem);
+ this.main_window = new MainWindow(application, this);
+ this.main_window.retry_service_problem.connect(on_retry_service_problem);
engine.account_available.connect(on_account_available);
@@ -265,7 +287,7 @@ internal class Application.Controller : Geary.BaseObject {
// Now that all composers are closed, we can shut down the
// rest of the client and engine. Cancel internal processes
// first so they don't block shutdown.
- this.open_cancellable.cancel();
+ this.controller_open.cancel();
// Release folder and conversations in the main window
yield this.main_window.select_folder(null, false);
@@ -278,8 +300,6 @@ internal class Application.Controller : Geary.BaseObject {
// be freed up
this.plugin_manager.notifications.clear_folders();
- this.cancellable_open_account.cancel();
-
// Create a copy of known accounts so the loop below does not
// explode if accounts are removed while iterating.
var closing_accounts = new Gee.LinkedList<AccountContext>();
@@ -859,16 +879,35 @@ internal class Application.Controller : Geary.BaseObject {
}
}
+ /** Returns the first open account, sorted by ordinal. */
+ internal Geary.AccountInformation? get_first_account() {
+ return this.accounts.keys.iterator().fold<Geary.AccountInformation?>(
+ (next, prev) => {
+ return prev == null || next.ordinal < prev.ordinal ? next : prev;
+ },
+ null
+ );
+ }
+
/** Expunges removed accounts while the controller remains open. */
internal async void expunge_accounts() {
try {
- yield this.account_manager.expunge_accounts(this.open_cancellable);
+ yield this.account_manager.expunge_accounts(this.controller_open);
} catch (GLib.Error err) {
report_problem(new Geary.ProblemReport(err));
}
}
- private void open_account(Geary.Account account) {
+ private async void open_account(Geary.Account account) {
+ AccountContext context = new AccountContext(
+ account,
+ new Geary.App.EmailStore(account),
+ new Application.ContactStore(account, this.folks)
+ );
+ this.accounts.set(account.information, context);
+
+ this.upgrade_dialog.add_account(account, this.controller_open);
+
account.information.authentication_failure.connect(
on_authentication_failure
);
@@ -876,8 +915,49 @@ internal class Application.Controller : Geary.BaseObject {
account.notify["current-status"].connect(
on_account_status_notify
);
+ account.email_removed.connect(on_account_email_removed);
+ account.folders_available_unavailable.connect(on_folders_available_unavailable);
account.report_problem.connect(on_report_problem);
- connect_account_async.begin(account, cancellable_open_account);
+
+ Geary.Smtp.ClientService? smtp = (
+ account.outgoing as Geary.Smtp.ClientService
+ );
+ if (smtp != null) {
+ smtp.email_sent.connect(on_sent);
+ smtp.sending_monitor.start.connect(on_sending_started);
+ smtp.sending_monitor.finish.connect(on_sending_finished);
+ }
+
+ bool retry = false;
+ do {
+ try {
+ account.set_data(PROP_ATTEMPT_OPEN_ACCOUNT, true);
+ yield account.open_async(this.controller_open);
+ retry = false;
+ } catch (Error open_err) {
+ debug("Unable to open account %s: %s", account.to_string(), open_err.message);
+
+ if (open_err is Geary.EngineError.CORRUPT) {
+ retry = yield account_database_error_async(account);
+ }
+
+ if (!retry) {
+ report_problem(
+ new Geary.AccountProblemReport(
+ account.information,
+ open_err
+ )
+ );
+
+ this.account_manager.disable_account(account.information);
+ this.accounts.unset(account.information);
+ }
+ }
+ } while (retry);
+
+ account_available(context);
+ display_main_window_if_ready();
+ update_account_status();
}
private async void close_account(Geary.AccountInformation config,
@@ -890,6 +970,8 @@ internal class Application.Controller : Geary.BaseObject {
// Guard against trying to close the account twice
this.accounts.unset(account.information);
+ this.upgrade_dialog.remove_account(account);
+
// Stop updating status and showing errors when closing
// the account - the user doesn't care any more
account.report_problem.disconnect(on_report_problem);
@@ -917,21 +999,7 @@ internal class Application.Controller : Geary.BaseObject {
// status notifications for it
update_account_status();
- // If we're not shutting down, select the inbox of the
- // first account so that we show something other than
- // empty conversation list/viewer.
- Geary.Folder? to_select = null;
- if (!is_shutdown) {
- Geary.AccountInformation? first_account = get_first_account();
- if (first_account != null) {
- AccountContext? first_context = this.accounts[first_account];
- if (first_context != null) {
- to_select = first_context.inbox;
- }
- }
- }
-
- yield this.main_window.remove_account(account, to_select);
+ account_unavailable(context, is_shutdown);
context.cancellable.cancel();
context.contacts.close();
@@ -1159,61 +1227,6 @@ internal class Application.Controller : Geary.BaseObject {
main_window.status_bar.deactivate_message(StatusBar.Message.OUTBOX_SENDING);
}
- private async void connect_account_async(Geary.Account account, Cancellable? cancellable = null) {
- AccountContext context = new AccountContext(
- account,
- new Geary.App.EmailStore(account),
- new Application.ContactStore(account, this.folks)
- );
-
- // XXX Need to set this early since
- // on_folders_available_unavailable expects it to be there
- this.accounts.set(account.information, context);
-
- account.email_removed.connect(on_account_email_removed);
- account.folders_available_unavailable.connect(on_folders_available_unavailable);
-
- Geary.Smtp.ClientService? smtp = (
- account.outgoing as Geary.Smtp.ClientService
- );
- if (smtp != null) {
- smtp.email_sent.connect(on_sent);
- smtp.sending_monitor.start.connect(on_sending_started);
- smtp.sending_monitor.finish.connect(on_sending_finished);
- }
-
- bool retry = false;
- do {
- try {
- account.set_data(PROP_ATTEMPT_OPEN_ACCOUNT, true);
- yield account.open_async(cancellable);
- retry = false;
- } catch (Error open_err) {
- debug("Unable to open account %s: %s", account.to_string(), open_err.message);
-
- if (open_err is Geary.EngineError.CORRUPT) {
- retry = yield account_database_error_async(account);
- }
-
- if (!retry) {
- report_problem(
- new Geary.AccountProblemReport(
- account.information,
- open_err
- )
- );
-
- this.account_manager.disable_account(account.information);
- this.accounts.unset(account.information);
- }
- }
- } while (retry);
-
- main_window.folder_list.set_user_folders_root_name(account, _("Labels"));
- display_main_window_if_ready();
- update_account_status();
- }
-
// Returns true if the caller should try opening the account again
private async bool account_database_error_async(Geary.Account account) {
bool retry = true;
@@ -1272,8 +1285,8 @@ internal class Application.Controller : Geary.BaseObject {
*/
private void display_main_window_if_ready() {
if (did_attempt_open_all_accounts() &&
- !upgrade_dialog.visible &&
- !cancellable_open_account.is_cancelled() &&
+ !this.upgrade_dialog.visible &&
+ !this.controller_open.is_cancelled() &&
!this.application.is_background_service)
main_window.show();
}
@@ -1624,15 +1637,6 @@ internal class Application.Controller : Geary.BaseObject {
}
}
- private Geary.AccountInformation? get_first_account() {
- return this.accounts.keys.iterator().fold<Geary.AccountInformation?>(
- (next, prev) => {
- return prev == null || next.ordinal < prev.ordinal ? next : prev;
- },
- null
- );
- }
-
private bool should_add_folder(Gee.Collection<Geary.Folder>? all,
Geary.Folder folder) {
// if folder is openable, add it
@@ -1673,8 +1677,7 @@ internal class Application.Controller : Geary.BaseObject {
}
if (account != null) {
- upgrade_dialog.add_account(account, cancellable_open_account);
- open_account(account);
+ this.open_account.begin(account);
}
}
diff --git a/src/client/application/application-main-window.vala
b/src/client/application/application-main-window.vala
index 255c105d..b262a3b0 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -208,9 +208,10 @@ public class Application.MainWindow :
public ConversationListView conversation_list_view { get; private set; }
public ConversationViewer conversation_viewer { get; private set; }
public StatusBar status_bar { get; private set; default = new StatusBar(); }
+
private MonitoredSpinner spinner = new MonitoredSpinner();
- private AccountContext? context = null;
+ private Gee.Set<AccountContext> accounts = new Gee.HashSet<AccountContext>();
private GLib.SimpleActionGroup edit_actions = new GLib.SimpleActionGroup();
@@ -273,7 +274,7 @@ public class Application.MainWindow :
public signal void retry_service_problem(Geary.ClientService.Status problem);
- public MainWindow(Client application) {
+ internal MainWindow(Client application, Controller controller) {
Object(
application: application,
show_menubar: false
@@ -299,22 +300,43 @@ public class Application.MainWindow :
this.attachments = new AttachmentManager(this);
- this.application.engine.account_available.connect(on_account_available);
- this.application.engine.account_unavailable.connect(on_account_unavailable);
-
this.update_ui_timeout = new Geary.TimeoutManager.seconds(
UPDATE_UI_INTERVAL, on_update_ui_timeout
);
this.update_ui_timeout.repetition = FOREVER;
+ // Add future and existing accounts to the main window
+ controller.account_available.connect(
+ on_account_available
+ );
+ controller.account_unavailable.connect(
+ on_account_unavailable
+ );
+ foreach (AccountContext context in controller.get_account_contexts()) {
+ add_account(context);
+ }
+
this.main_layout.show_all();
}
~MainWindow() {
- this.update_ui_timeout.reset();
base_unref();
}
+ /** {@inheritDoc} */
+ public override void destroy() {
+ if (this.application != null) {
+ this.application.controller.account_available.disconnect(
+ on_account_available
+ );
+ this.application.controller.account_unavailable.disconnect(
+ on_account_unavailable
+ );
+ }
+ this.update_ui_timeout.reset();
+ base.destroy();
+ }
+
/** Updates the window's account status info bars. */
public void update_account_status(Geary.Account.Status status,
bool has_auth_error,
@@ -666,7 +688,7 @@ public class Application.MainWindow :
}
/** Adds a folder to the window. */
- public void add_folder(Geary.Folder to_add) {
+ internal void add_folder(Geary.Folder to_add) {
this.folder_list.add_folder(to_add);
if (to_add.account == this.selected_account) {
this.main_toolbar.copy_folder_menu.add_folder(to_add);
@@ -675,7 +697,7 @@ public class Application.MainWindow :
}
/** Removes a folder from the window. */
- public void remove_folder(Geary.Folder to_remove) {
+ internal void remove_folder(Geary.Folder to_remove) {
if (to_remove.account == this.selected_account) {
this.main_toolbar.copy_folder_menu.remove_folder(to_remove);
this.main_toolbar.move_folder_menu.remove_folder(to_remove);
@@ -683,35 +705,82 @@ public class Application.MainWindow :
this.folder_list.remove_folder(to_remove);
}
+ private void add_account(AccountContext to_add) {
+ if (!this.accounts.contains(to_add)) {
+ this.folder_list.set_user_folders_root_name(
+ to_add.account, _("Labels")
+ );
+
+ this.progress_monitor.add(to_add.account.opening_monitor);
+ Geary.Smtp.ClientService? smtp = (
+ to_add.account.outgoing as Geary.Smtp.ClientService
+ );
+ if (smtp != null) {
+ this.progress_monitor.add(smtp.sending_monitor);
+ }
+
+ to_add.commands.executed.connect(on_command_execute);
+ to_add.commands.undone.connect(on_command_undo);
+ to_add.commands.redone.connect(on_command_redo);
+
+ this.accounts.add(to_add);
+ }
+ }
+
/**
* Removes the given account from the main window.
*
* If `to_select` is not null, the given folder will be selected,
* otherwise no folder will be.
*/
- public async void remove_account(Geary.Account to_remove,
- Geary.Folder? to_select) {
- // Explicitly unset the selected folder if it belongs to the
- // account so we block until it's gone. This also clears the
- // previous search folder, so it won't try to re-load that
- // that when the account is gone.
- if (this.selected_folder != null &&
- this.selected_folder.account == to_remove) {
- Geary.SearchFolder? current_search = (
- this.selected_folder as Geary.SearchFolder
- );
+ private async void remove_account(AccountContext to_remove,
+ Geary.Folder? to_select) {
+ if (this.accounts.contains(to_remove)) {
+ // Explicitly unset the selected folder if it belongs to the
+ // account so we block until it's gone. This also clears the
+ // previous search folder, so it won't try to re-load that
+ // that when the account is gone.
+ if (this.selected_folder != null &&
+ this.selected_folder.account == to_remove.account) {
+ Geary.SearchFolder? current_search = (
+ this.selected_folder as Geary.SearchFolder
+ );
+
+ yield select_folder(to_select, false);
+
+ // Clear the account's search folder if it existed
+ if (current_search != null) {
+ this.search_bar.set_search_text("");
+ this.search_bar.search_mode_enabled = false;
+ }
+ }
- yield select_folder(to_select, false);
+ to_remove.commands.executed.disconnect(on_command_execute);
+ to_remove.commands.undone.disconnect(on_command_undo);
+ to_remove.commands.redone.disconnect(on_command_redo);
- // Clear the account's search folder if it existed
- if (current_search != null) {
- this.search_bar.set_search_text("");
- this.search_bar.search_mode_enabled = false;
+ this.progress_monitor.remove(to_remove.account.opening_monitor);
+ Geary.Smtp.ClientService? smtp = (
+ to_remove.account.outgoing as Geary.Smtp.ClientService
+ );
+ if (smtp != null) {
+ this.progress_monitor.remove(smtp.sending_monitor);
}
+
+ // Finally, remove the account and its folders
+ this.folder_list.remove_account(to_remove.account);
+ this.accounts.remove(to_remove);
}
+ }
- // Finally, remove the account and its folders
- this.folder_list.remove_account(to_remove);
+ private AccountContext? get_selected_account_context() {
+ AccountContext? context = null;
+ if (this.selected_account != null) {
+ context = this.application.controller.get_context_for_account(
+ this.selected_account.information
+ );
+ }
+ return context;
}
private void load_config(Configuration config) {
@@ -975,7 +1044,7 @@ public class Application.MainWindow :
/** Un-does the last executed application command, if any. */
private async void undo() {
- AccountContext? selected = this.context;
+ AccountContext? selected = get_selected_account_context();
if (selected != null) {
selected.commands.undo.begin(
selected.cancellable,
@@ -992,7 +1061,7 @@ public class Application.MainWindow :
/** Re-does the last undone application command, if any. */
private async void redo() {
- AccountContext? selected = this.context;
+ AccountContext? selected = get_selected_account_context();
if (selected != null) {
selected.commands.redo.begin(
selected.cancellable,
@@ -1008,7 +1077,7 @@ public class Application.MainWindow :
}
private void update_command_actions() {
- AccountContext? selected = this.context;
+ AccountContext? selected = get_selected_account_context();
get_edit_action(Action.Edit.UNDO).set_enabled(
selected != null && selected.commands.can_undo
);
@@ -1130,29 +1199,12 @@ public class Application.MainWindow :
if (this.selected_account != null) {
this.main_toolbar.copy_folder_menu.clear();
this.main_toolbar.move_folder_menu.clear();
-
- AccountContext? context = this.context;
- if (context != null) {
- context.commands.executed.disconnect(on_command_execute);
- context.commands.undone.disconnect(on_command_undo);
- context.commands.redone.disconnect(on_command_redo);
- }
- this.context = null;
}
this.selected_account = account;
this.search_bar.set_account(account);
if (account != null) {
- this.context = this.application.controller.get_context_for_account(
- account.information
- );
- if (this.context != null) {
- this.context.commands.executed.connect(on_command_execute);
- this.context.commands.undone.connect(on_command_undo);
- this.context.commands.redone.connect(on_command_redo);
- }
-
foreach (Geary.Folder folder in account.list_folders()) {
this.main_toolbar.copy_folder_menu.add_folder(folder);
this.main_toolbar.move_folder_menu.add_folder(folder);
@@ -1194,7 +1246,7 @@ public class Application.MainWindow :
// last email was removed but the conversation monitor
// hasn't signalled its removal yet. In this case,
// just don't load it since it will soon disappear.
- AccountContext? context = this.context;
+ AccountContext? context = get_selected_account_context();
if (context != null && convo.get_count() > 0) {
try {
yield this.conversation_viewer.load_conversation(
@@ -1324,40 +1376,6 @@ public class Application.MainWindow :
}
}
- private void on_account_available(Geary.AccountInformation account) {
- try {
- Geary.Account? engine = this.application.engine.get_account_instance(account);
- if (engine != null) {
- this.progress_monitor.add(engine.opening_monitor);
- Geary.Smtp.ClientService? smtp = (
- engine.outgoing as Geary.Smtp.ClientService
- );
- if (smtp != null) {
- this.progress_monitor.add(smtp.sending_monitor);
- }
- }
- } catch (GLib.Error e) {
- debug("Could not access account progress monitors: %s", e.message);
- }
- }
-
- private void on_account_unavailable(Geary.AccountInformation account) {
- try {
- Geary.Account? engine = this.application.engine.get_account_instance(account);
- if (engine != null) {
- this.progress_monitor.remove(engine.opening_monitor);
- Geary.Smtp.ClientService? smtp = (
- engine.outgoing as Geary.Smtp.ClientService
- );
- if (smtp != null) {
- this.progress_monitor.remove(smtp.sending_monitor);
- }
- }
- } catch (GLib.Error e) {
- debug("Could not access account progress monitors: %s", e.message);
- }
- }
-
private void on_change_orientation() {
bool horizontal = this.application.config.folder_list_pane_horizontal;
bool initial = true;
@@ -1646,6 +1664,32 @@ public class Application.MainWindow :
update_ui();
}
+ private void on_account_available(AccountContext account) {
+ add_account(account);
+ }
+
+ private void on_account_unavailable(AccountContext account,
+ bool is_shutdown) {
+ // If we're not shutting down, select the inbox of the first
+ // account so that we show something other than empty
+ // conversation list/viewer.
+ Geary.Folder? to_select = null;
+ if (!is_shutdown) {
+ Geary.AccountInformation? first_account =
+ this.application.controller.get_first_account();
+ if (first_account != null) {
+ AccountContext? first_context =
+ this.application.controller.get_context_for_account(
+ first_account
+ );
+ if (first_context != null) {
+ to_select = first_context.inbox;
+ }
+ }
+ }
+
+ this.remove_account.begin(account, to_select);
+ }
private void on_command_execute(Command command) {
if (!(command is TrivialCommand)) {
// Only show an execute notification for non-trivial
diff --git a/src/client/dialogs/upgrade-dialog.vala b/src/client/dialogs/upgrade-dialog.vala
index 55cf81ca..7a238b17 100644
--- a/src/client/dialogs/upgrade-dialog.vala
+++ b/src/client/dialogs/upgrade-dialog.vala
@@ -56,13 +56,25 @@ public class UpgradeDialog : Object {
}
/**
- * Add accounts before opening them.
+ * Adds an account to be monitored for upgrades by the dialog.
+ *
+ * Accounts should be added before being opened.
*/
- public void add_account(Geary.Account account, Cancellable? cancellable = null) {
+ public void add_account(Geary.Account account,
+ GLib.Cancellable? cancellable = null) {
monitor.add(account.db_upgrade_monitor);
monitor.add(account.db_vacuum_monitor);
- if (cancellable != null)
+ if (cancellable != null) {
cancellables.add(cancellable);
+ }
}
-}
+ /**
+ * Stops an account from being monitored.
+ */
+ public void remove_account(Geary.Account account) {
+ monitor.remove(account.db_upgrade_monitor);
+ monitor.remove(account.db_vacuum_monitor);
+ }
+
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]