Application.AccountInterface: New interface for accessing account objects

By defining an interface for account object access, classes that use it
are decoupled from Application.Controller, allowing them to be more
easily unit tested.
This commit is contained in:
Michael Gratton 2020-08-10 18:10:32 +10:00 committed by Michael James Gratton
parent 6e0aa316d8
commit 8608293367
3 changed files with 70 additions and 28 deletions

View file

@ -0,0 +1,63 @@
/*
* Copyright © 2020 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
* Application interface for objects that manage accounts.
*
* This interface allows non-core application components to access the
* application's account context objects. Typically this is
* implemented by {@link Controller}.
*
* It also supports unit testing these components without having to
* load the complete application by providing mock instances of this
* interface instead of a fully initialised controller.
*/
internal interface Application.AccountInterface : GLib.Object {
/**
* Emitted when an account is added or is enabled.
*
* This will be emitted after an account is opened and added to
* the controller.
*
* The `is_startup` argument will be true if the application is in
* the middle of starting up, otherwise if the account was newly
* added when the application was already running then it will be
* false.
*/
public signal void account_available(
AccountContext context,
bool is_startup
);
/**
* 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
);
/** Returns a context for an account, if any. */
internal abstract AccountContext? get_context_for_account(Geary.AccountInformation account);
/** Returns a read-only collection of contexts each active account. */
internal abstract Gee.Collection<AccountContext> get_account_contexts();
}

View file

@ -13,7 +13,8 @@
* A single instance of this class is constructed by {@link Client}
* when the primary application instance is started.
*/
internal class Application.Controller : Geary.BaseObject {
internal class Application.Controller :
Geary.BaseObject, AccountInterface {
private const uint MAX_AUTH_ATTEMPTS = 3;
@ -78,6 +79,7 @@ internal class Application.Controller : Geary.BaseObject {
// Primary collection of the application's open accounts
private Gee.Map<Geary.AccountInformation,AccountContext> accounts =
new Gee.HashMap<Geary.AccountInformation,AccountContext>();
private bool is_loading_accounts = true;
// Cancelled if the controller is closed
private GLib.Cancellable controller_open;
@ -98,31 +100,6 @@ internal class Application.Controller : Geary.BaseObject {
private GLib.Cancellable? storage_cleanup_cancellable;
/**
* 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.
*/
@ -213,8 +190,9 @@ internal class Application.Controller : Geary.BaseObject {
yield this.account_manager.connect_goa(cancellable);
// Start loading accounts
// Load accounts
yield this.account_manager.load_accounts(cancellable);
this.is_loading_accounts = false;
// Expunge any deleted accounts in the background, so we're
// not blocking the app continuing to open.
@ -1003,7 +981,7 @@ internal class Application.Controller : Geary.BaseObject {
// Notify before opening so that listeners have a chance to
// hook into it before signals start getting fired by folders
// becoming available, etc.
account_available(context);
account_available(context, this.is_loading_accounts);
bool retry = false;
do {

View file

@ -12,6 +12,7 @@ client_package = '@0@-@1@'.format(
client_vala_sources = files(
'application/application-account-context.vala',
'application/application-account-interface.vala',
'application/application-attachment-manager.vala',
'application/application-avatar-store.vala',
'application/application-certificate-manager.vala',