From f0c1dfe37517d7d3cae7e71805923a86034de3df Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Sat, 21 Mar 2020 09:06:05 +1100 Subject: [PATCH] Application: Make Plugin.EmailStore impl usable by other cxts Break out the plugin mail store implmeentation out of NotificationContext into its own EmailStoreFactory class, so it can be re-used by other plugin context types. --- po/POTFILES.in | 1 + .../application-email-store-factory.vala | 210 ++++++++++++++++++ .../application-notification-context.vala | 167 +------------- .../application-plugin-manager.vala | 6 +- src/client/meson.build | 1 + 5 files changed, 227 insertions(+), 158 deletions(-) create mode 100644 src/client/application/application-email-store-factory.vala diff --git a/po/POTFILES.in b/po/POTFILES.in index a1f9c866..d2494804 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -23,6 +23,7 @@ src/client/application/application-configuration.vala src/client/application/application-contact-store.vala src/client/application/application-contact.vala src/client/application/application-controller.vala +src/client/application/application-email-store-factory.vala src/client/application/application-folder-context.vala src/client/application/application-folder-store-factory.vala src/client/application/application-main-window.vala diff --git a/src/client/application/application-email-store-factory.vala b/src/client/application/application-email-store-factory.vala new file mode 100644 index 00000000..2072fc68 --- /dev/null +++ b/src/client/application/application-email-store-factory.vala @@ -0,0 +1,210 @@ +/* + * Copyright © 2020 Michael Gratton + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +/** + * A factory for constructing plugin email stores and objects. + * + * This class provides a common implementation that shares email + * objects between different plugin context instances. + */ +internal class Application.EmailStoreFactory : Geary.BaseObject { + + + private class EmailStoreImpl : Geary.BaseObject, Plugin.EmailStore { + + + private Client backing; + + + public EmailStoreImpl(Client backing) { + this.backing = backing; + } + + public async Gee.Collection get_email( + Gee.Collection plugin_ids, + GLib.Cancellable? cancellable + ) throws GLib.Error { + var emails = new Gee.HashSet(); + + // The email could theoretically come from any account, so + // group them by account up front. The common case will be + // only a single account, so optimise for that a bit. + + var accounts = new Gee.HashMap< + Geary.AccountInformation, + Gee.Set + >(); + Geary.AccountInformation? current_account = null; + Gee.Set? engine_ids = null; + foreach (Plugin.EmailIdentifier plugin_id in plugin_ids) { + IdImpl? id_impl = plugin_id as IdImpl; + if (id_impl != null) { + if (id_impl.account != current_account) { + current_account = id_impl.account; + engine_ids = accounts.get(current_account); + if (engine_ids == null) { + engine_ids = new Gee.HashSet(); + accounts.set(current_account, engine_ids); + } + } + engine_ids.add(id_impl.backing); + } + } + + foreach (var account in accounts.keys) { + AccountContext context = + this.backing.controller.get_context_for_account(account); + Gee.Collection batch = + yield context.emails.list_email_by_sparse_id_async( + accounts.get(account), + ENVELOPE, + NONE, + context.cancellable + ); + if (batch != null) { + foreach (var email in batch) { + emails.add(new EmailImpl(email, account)); + } + } + } + + return emails; + } + + internal void destroy() { + // noop + } + + } + + + private class EmailImpl : Geary.BaseObject, Plugin.Email { + + + public Plugin.EmailIdentifier identifier { + get { + if (this._id == null) { + this._id = new IdImpl(this.backing.id, this.account); + } + return this._id; + } + } + private IdImpl? _id = null; + + public string subject { + get { return this._subject; } + } + string _subject; + + internal Geary.Email backing; + // Remove this when EmailIdentifier is updated to include + // the account + internal Geary.AccountInformation account { get; private set; } + + + public EmailImpl(Geary.Email backing, + Geary.AccountInformation account) { + this.backing = backing; + this.account = account; + Geary.RFC822.Subject? subject = this.backing.subject; + this._subject = subject != null ? subject.to_string() : ""; + } + + public Geary.RFC822.MailboxAddress? get_primary_originator() { + return Util.Email.get_primary_originator(this.backing); + } + + } + + + private class IdImpl : Geary.BaseObject, + Gee.Hashable, Plugin.EmailIdentifier { + + + internal Geary.EmailIdentifier backing { get; private set; } + // Remove this when EmailIdentifier is updated to include + // the account + internal Geary.AccountInformation account { get; private set; } + + + public IdImpl(Geary.EmailIdentifier backing, + Geary.AccountInformation account) { + this.backing = backing; + this.account = account; + } + + public GLib.Variant to_variant() { + return this.backing.to_variant(); + } + + public bool equal_to(Plugin.EmailIdentifier other) { + if (this == other) { + return true; + } + IdImpl? impl = other as IdImpl; + return ( + impl != null && + this.backing.equal_to(impl.backing) && + this.account.equal_to(impl.account) + ); + } + + public uint hash() { + return this.backing.hash(); + } + + } + + + private Client application; + private Gee.Set stores = + new Gee.HashSet(); + + + /** + * Constructs a new factory instance. + */ + public EmailStoreFactory(Client application) throws GLib.Error { + this.application = application; + } + + /** Clearing all state of the store. */ + public void destroy() throws GLib.Error { + foreach (EmailStoreImpl store in this.stores) { + store.destroy(); + } + this.stores.clear(); + } + + /** Constructs a new email store for use by plugin contexts. */ + public Plugin.EmailStore new_email_store() { + var store = new EmailStoreImpl(this.application); + this.stores.add(store); + return store; + } + + /** Destroys a folder store once is no longer required. */ + public void destroy_email_store(Plugin.EmailStore plugin) { + EmailStoreImpl? impl = plugin as EmailStoreImpl; + if (impl != null) { + impl.destroy(); + this.stores.remove(impl); + } + } + + public Gee.Collection to_plugin_ids( + Gee.Collection engine_ids, + Geary.AccountInformation account + ) { + var plugin_ids = new Gee.HashSet(); + foreach (var id in engine_ids) { + plugin_ids.add(new IdImpl(id, account)); + } + return plugin_ids; + } + +} diff --git a/src/client/application/application-notification-context.vala b/src/client/application/application-notification-context.vala index 3c4c018a..1ff2e1c5 100644 --- a/src/client/application/application-notification-context.vala +++ b/src/client/application/application-notification-context.vala @@ -16,159 +16,6 @@ internal class Application.NotificationContext : private const Geary.Email.Field REQUIRED_FIELDS = FLAGS; - private class EmailStoreImpl : Geary.BaseObject, Plugin.EmailStore { - - - private class EmailImpl : Geary.BaseObject, Plugin.Email { - - - public Plugin.EmailIdentifier identifier { - get { - if (this._id == null) { - this._id = new IdImpl(this.backing.id, this.account); - } - return this._id; - } - } - private IdImpl? _id = null; - - public string subject { - get { return this._subject; } - } - string _subject; - - internal Geary.Email backing; - // Remove this when EmailIdentifier is updated to include - // the account - internal Geary.AccountInformation account { get; private set; } - - - public EmailImpl(Geary.Email backing, - Geary.AccountInformation account) { - this.backing = backing; - this.account = account; - Geary.RFC822.Subject? subject = this.backing.subject; - this._subject = subject != null ? subject.to_string() : ""; - } - - public Geary.RFC822.MailboxAddress? get_primary_originator() { - return Util.Email.get_primary_originator(this.backing); - } - - } - - - private class IdImpl : Geary.BaseObject, - Gee.Hashable, Plugin.EmailIdentifier { - - - internal Geary.EmailIdentifier backing { get; private set; } - // Remove this when EmailIdentifier is updated to include - // the account - internal Geary.AccountInformation account { get; private set; } - - - public IdImpl(Geary.EmailIdentifier backing, - Geary.AccountInformation account) { - this.backing = backing; - this.account = account; - } - - public GLib.Variant to_variant() { - return this.backing.to_variant(); - } - - public bool equal_to(Plugin.EmailIdentifier other) { - if (this == other) { - return true; - } - IdImpl? impl = other as IdImpl; - return ( - impl != null && - this.backing.equal_to(impl.backing) && - this.account.equal_to(impl.account) - ); - } - - public uint hash() { - return this.backing.hash(); - } - - } - - - private Client backing; - - - public EmailStoreImpl(Client backing) { - this.backing = backing; - } - - public async Gee.Collection get_email( - Gee.Collection plugin_ids, - GLib.Cancellable? cancellable - ) throws GLib.Error { - var emails = new Gee.HashSet(); - - // The email could theoretically come from any account, so - // group them by account up front. The common case will be - // only a single account, so optimise for that a bit. - - var accounts = new Gee.HashMap< - Geary.AccountInformation, - Gee.Set - >(); - Geary.AccountInformation? current_account = null; - Gee.Set? engine_ids = null; - foreach (Plugin.EmailIdentifier plugin_id in plugin_ids) { - IdImpl? id_impl = plugin_id as IdImpl; - if (id_impl != null) { - if (id_impl.account != current_account) { - current_account = id_impl.account; - engine_ids = accounts.get(current_account); - if (engine_ids == null) { - engine_ids = new Gee.HashSet(); - accounts.set(current_account, engine_ids); - } - } - engine_ids.add(id_impl.backing); - } - } - - foreach (var account in accounts.keys) { - AccountContext context = - this.backing.controller.get_context_for_account(account); - Gee.Collection batch = - yield context.emails.list_email_by_sparse_id_async( - accounts.get(account), - ENVELOPE, - NONE, - context.cancellable - ); - if (batch != null) { - foreach (var email in batch) { - emails.add(new EmailImpl(email, account)); - } - } - } - - return emails; - } - - internal Gee.Collection get_plugin_ids( - Gee.Collection engine_ids, - Geary.AccountInformation account - ) { - var plugin_ids = new Gee.HashSet(); - foreach (var id in engine_ids) { - plugin_ids.add(new IdImpl(id, account)); - } - return plugin_ids; - } - - } - - private class ContactStoreImpl : Geary.BaseObject, Plugin.ContactStore { @@ -221,15 +68,18 @@ internal class Application.NotificationContext : private unowned Client application; private FolderStoreFactory folders_factory; private Plugin.FolderStore folders; - private EmailStoreImpl email; + private EmailStoreFactory email_factory; + private Plugin.EmailStore email; internal NotificationContext(Client application, - FolderStoreFactory folders_factory) { + FolderStoreFactory folders_factory, + EmailStoreFactory email_factory) { this.application = application; this.folders_factory = folders_factory; this.folders = folders_factory.new_folder_store(); - this.email = new EmailStoreImpl(application); + this.email_factory = email_factory; + this.email = email_factory.new_email_store(); } public async Plugin.EmailStore get_email() @@ -353,6 +203,7 @@ internal class Application.NotificationContext : foreach (Geary.Folder monitored in this.folder_information.keys.to_array()) { remove_folder(monitored); } + this.email_factory.destroy_email_store(this.email); } internal void clear_new_messages(Geary.Folder location, @@ -414,7 +265,9 @@ internal class Application.NotificationContext : new_messages_arrived( folder, info.recent_ids.size, - this.email.get_plugin_ids(delta, info.folder.account.information) + this.email_factory.to_plugin_ids( + delta, info.folder.account.information + ) ); } else { this._total_new_messages -= delta.size; diff --git a/src/client/application/application-plugin-manager.vala b/src/client/application/application-plugin-manager.vala index 84a30921..fd89c5d7 100644 --- a/src/client/application/application-plugin-manager.vala +++ b/src/client/application/application-plugin-manager.vala @@ -125,6 +125,7 @@ public class Application.PluginManager : GLib.Object { private string trusted_path; private FolderStoreFactory folders_factory; + private EmailStoreFactory email_factory; private Gee.Map plugin_set = new Gee.HashMap(); @@ -136,6 +137,7 @@ public class Application.PluginManager : GLib.Object { this.application = application; this.plugins = Peas.Engine.get_default(); this.folders_factory = new FolderStoreFactory(application); + this.email_factory = new EmailStoreFactory(application); this.trusted_path = application.get_app_plugins_dir().get_path(); this.plugins.add_search_path(trusted_path, null); @@ -229,6 +231,7 @@ public class Application.PluginManager : GLib.Object { this.plugins.set_loaded_plugins(null); this.plugins.garbage_collect(); this.folders_factory.destroy(); + this.email_factory.destroy(); } internal inline bool is_autoload(Peas.PluginInfo info) { @@ -266,7 +269,8 @@ public class Application.PluginManager : GLib.Object { if (notification != null) { var context = new NotificationContext( this.application, - this.folders_factory + this.folders_factory, + this.email_factory ); this.notification_contexts.set(info, context); notification.notifications = context; diff --git a/src/client/meson.build b/src/client/meson.build index 36962f3e..2f6b250d 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -10,6 +10,7 @@ geary_client_vala_sources = files( 'application/application-contact-store.vala', 'application/application-contact.vala', 'application/application-controller.vala', + 'application/application-email-store-factory.vala', 'application/application-folder-context.vala', 'application/application-folder-store-factory.vala', 'application/application-main-window.vala',