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.
This commit is contained in:
Michael Gratton 2020-03-21 09:06:05 +11:00 committed by Michael James Gratton
parent 3202a9c018
commit f0c1dfe375
5 changed files with 227 additions and 158 deletions

View file

@ -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

View file

@ -0,0 +1,210 @@
/*
* 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.
*/
/**
* 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<Plugin.Email> get_email(
Gee.Collection<Plugin.EmailIdentifier> plugin_ids,
GLib.Cancellable? cancellable
) throws GLib.Error {
var emails = new Gee.HashSet<Plugin.Email>();
// 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.EmailIdentifier>
>();
Geary.AccountInformation? current_account = null;
Gee.Set<Geary.EmailIdentifier>? 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<Geary.EmailIdentifier>();
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<Geary.Email> 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>, 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<EmailStoreImpl> stores =
new Gee.HashSet<EmailStoreImpl>();
/**
* 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<Plugin.EmailIdentifier> to_plugin_ids(
Gee.Collection<Geary.EmailIdentifier> engine_ids,
Geary.AccountInformation account
) {
var plugin_ids = new Gee.HashSet<Plugin.EmailIdentifier>();
foreach (var id in engine_ids) {
plugin_ids.add(new IdImpl(id, account));
}
return plugin_ids;
}
}

View file

@ -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>, 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<Plugin.Email> get_email(
Gee.Collection<Plugin.EmailIdentifier> plugin_ids,
GLib.Cancellable? cancellable
) throws GLib.Error {
var emails = new Gee.HashSet<Plugin.Email>();
// 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.EmailIdentifier>
>();
Geary.AccountInformation? current_account = null;
Gee.Set<Geary.EmailIdentifier>? 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<Geary.EmailIdentifier>();
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<Geary.Email> 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<Plugin.EmailIdentifier> get_plugin_ids(
Gee.Collection<Geary.EmailIdentifier> engine_ids,
Geary.AccountInformation account
) {
var plugin_ids = new Gee.HashSet<Plugin.EmailIdentifier>();
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;

View file

@ -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<Peas.PluginInfo,PluginContext> plugin_set =
new Gee.HashMap<Peas.PluginInfo,PluginContext>();
@ -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;

View file

@ -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',