diff --git a/po/POTFILES.in b/po/POTFILES.in index 533634ab..fd5d51c3 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -166,6 +166,7 @@ src/engine/app/email-store/app-copy-operation.vala src/engine/app/email-store/app-fetch-operation.vala src/engine/app/email-store/app-list-operation.vala src/engine/app/email-store/app-mark-operation.vala +src/engine/common/common-contact-store-impl.vala src/engine/common/common-message-data.vala src/engine/db/db.vala src/engine/db/db-connection.vala @@ -218,7 +219,6 @@ src/engine/imap/command/imap-status-command.vala src/engine/imap/command/imap-store-command.vala src/engine/imap-db/imap-db-account.vala src/engine/imap-db/imap-db-attachment.vala -src/engine/imap-db/imap-db-contact.vala src/engine/imap-db/imap-db-database.vala src/engine/imap-db/imap-db-email-identifier.vala src/engine/imap-db/imap-db-folder.vala @@ -239,7 +239,6 @@ src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala src/engine/imap-engine/imap-engine-account-operation.vala src/engine/imap-engine/imap-engine-account-processor.vala src/engine/imap-engine/imap-engine-account-synchronizer.vala -src/engine/imap-engine/imap-engine-contact-store.vala src/engine/imap-engine/imap-engine-email-prefetcher.vala src/engine/imap-engine/imap-engine-generic-account.vala src/engine/imap-engine/imap-engine-generic-folder.vala diff --git a/src/client/application/application-contact-store.vala b/src/client/application/application-contact-store.vala index 8603d8f8..7d029610 100644 --- a/src/client/application/application-contact-store.vala +++ b/src/client/application/application-contact-store.vala @@ -86,7 +86,9 @@ public class Application.ContactStore : Geary.BaseObject { } if (contact == null) { Geary.Contact? engine = - this.account.get_contact_store().get_by_rfc822(mailbox); + yield this.account.contact_store.get_by_rfc822( + mailbox, cancellable + ); contact = new Contact(this, individual, engine, mailbox); if (individual != null) { this.contact_id_cache.set_entry(individual.id, contact); diff --git a/src/client/application/application-contact.vala b/src/client/application/application-contact.vala index 40249834..bda158aa 100644 --- a/src/client/application/application-contact.vala +++ b/src/client/application/application-contact.vala @@ -182,14 +182,17 @@ public class Application.Contact : Geary.BaseObject { throws GLib.Error { ContactStore? store = this.store; if (store != null && this.contact != null) { - Geary.ContactFlags flags = new Geary.ContactFlags(); - flags.add(Geary.ContactFlags.ALWAYS_LOAD_REMOTE_IMAGES); + Geary.ContactFlags flags = ( + this.contact.contact_flags ?? new Geary.ContactFlags() + ); + if (enabled) { + flags.add(Geary.ContactFlags.ALWAYS_LOAD_REMOTE_IMAGES); + } else { + flags.remove(Geary.ContactFlags.ALWAYS_LOAD_REMOTE_IMAGES); + } - yield store.account.get_contact_store().mark_contacts_async( - Geary.Collection.single(this.contact), - enabled ? flags : null, - !enabled ? flags : null //, - // XXX cancellable + yield store.account.contact_store.update_contacts( + Geary.Collection.single(this.contact), cancellable ); } diff --git a/src/client/application/application-controller.vala b/src/client/application/application-controller.vala index cc58b48c..205e3fec 100644 --- a/src/client/application/application-controller.vala +++ b/src/client/application/application-controller.vala @@ -616,20 +616,16 @@ public class Application.Controller : Geary.BaseObject { ); account.report_problem.connect(on_report_problem); connect_account_async.begin(account, cancellable_open_account); - - ContactListStore list_store = this.contact_list_store_cache.create(account.get_contact_store()); - account.contacts_loaded.connect(list_store.set_sort_function); } private async void close_account(Geary.AccountInformation config) { AccountContext? context = this.accounts.get(config); if (context != null) { Geary.Account account = context.account; - Geary.ContactStore contact_store = account.get_contact_store(); + Geary.ContactStore contact_store = account.contact_store; ContactListStore list_store = this.contact_list_store_cache.get(contact_store); - account.contacts_loaded.disconnect(list_store.set_sort_function); this.contact_list_store_cache.unset(contact_store); if (this.current_account == account) { diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 0a340a94..5f1823cd 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -240,8 +240,6 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface { public Configuration config { get; set; } - private ContactListStore? contact_list_store = null; - private string body_html = ""; [GtkChild] @@ -380,8 +378,6 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface { // Is the composer closing (e.g. saving a draft or sending)? private bool is_closing = false; - private ContactListStoreCache contact_list_store_cache; - private ComposerContainer container { get { return (ComposerContainer) parent; } } @@ -406,7 +402,6 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface { Configuration config) { base_ref(); this.account = account; - this.contact_list_store_cache = contact_list_store_cache; this.config = config; this.compose_type = compose_type; if (this.compose_type == ComposeType.NEW_MESSAGE) @@ -653,22 +648,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface { * Loads and sets contact auto-complete data for the current account. */ private void load_entry_completions() { - Geary.ContactStore contacts = this.account.get_contact_store(); - if (this.contact_list_store == null || - this.contact_list_store.contact_store != contacts) { - ContactListStore? store = this.contact_list_store_cache.get(contacts); - - if (store == null) { - error("Error loading contact_list_store from cache"); - } else { - this.contact_list_store = store; - - this.to_entry.completion = new ContactEntryCompletion(store); - this.cc_entry.completion = new ContactEntryCompletion(store); - this.bcc_entry.completion = new ContactEntryCompletion(store); - this.reply_to_entry.completion = new ContactEntryCompletion(store); - } - } + Geary.ContactStore contacts = this.account.contact_store; } /** diff --git a/src/client/composer/contact-list-store.vala b/src/client/composer/contact-list-store.vala index 3af3cf11..d1d83a2e 100644 --- a/src/client/composer/contact-list-store.vala +++ b/src/client/composer/contact-list-store.vala @@ -70,29 +70,29 @@ public class ContactListStore : Gtk.ListStore, Geary.BaseInterface { base_ref(); set_column_types(Column.get_types()); this.contact_store = contact_store; - contact_store.contacts_added.connect(on_contacts_added); - contact_store.contacts_updated.connect(on_contacts_updated); + //contact_store.contacts_added.connect(on_contacts_added); + //contact_store.contacts_updated.connect(on_contacts_updated); } ~ContactListStore() { base_unref(); - this.contact_store.contacts_added.disconnect(on_contacts_added); - this.contact_store.contacts_updated.disconnect(on_contacts_updated); + //this.contact_store.contacts_added.disconnect(on_contacts_added); + //this.contact_store.contacts_updated.disconnect(on_contacts_updated); } /** * Loads contacts from the model's contact store. */ public async void load() { - uint count = 0; - foreach (Geary.Contact contact in this.contact_store.contacts) { - add_contact(contact); - count++; - if (count % LOAD_BATCH_SIZE == 0) { - Idle.add(load.callback); - yield; - } - } + // uint count = 0; + // foreach (Geary.Contact contact in this.contact_store.contacts) { + // add_contact(contact); + // count++; + // if (count % LOAD_BATCH_SIZE == 0) { + // Idle.add(load.callback); + // yield; + // } + // } } public void set_sort_function() { diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala index 41e74cfa..b0edd857 100644 --- a/src/engine/api/geary-account.vala +++ b/src/engine/api/geary-account.vala @@ -126,6 +126,11 @@ public abstract class Geary.Account : BaseObject { */ public ClientService outgoing { get; private set; } + /** + * The contact information store for this account. + */ + public Geary.ContactStore contact_store { get; protected set; } + public Geary.ProgressMonitor search_upgrade_monitor { get; protected set; } public Geary.ProgressMonitor db_upgrade_monitor { get; protected set; } public Geary.ProgressMonitor db_vacuum_monitor { get; protected set; } @@ -151,8 +156,6 @@ public abstract class Geary.Account : BaseObject { */ public signal void report_problem(Geary.ProblemReport problem); - public signal void contacts_loaded(); - /** * Fired when folders become available or unavailable in the account. * @@ -384,11 +387,6 @@ public abstract class Geary.Account : BaseObject { */ public abstract Gee.Collection list_folders() throws Error; - /** - * Gets a perpetually update-to-date collection of autocompletion contacts. - */ - public abstract Geary.ContactStore get_contact_store(); - /** * Returns the folder representing the given special folder type. If no such folder exists, * null is returned. diff --git a/src/engine/api/geary-contact-store.vala b/src/engine/api/geary-contact-store.vala index f7c522f1..43266274 100644 --- a/src/engine/api/geary-contact-store.vala +++ b/src/engine/api/geary-contact-store.vala @@ -1,50 +1,28 @@ -/* Copyright 2016 Software Freedom Conservancy Inc. +/* + * Copyright 2016 Software Freedom Conservancy Inc. + * Copyright 2019 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. + * (version 2.1 or later). See the COPYING file in this distribution. */ -public abstract class Geary.ContactStore : BaseObject { - public Gee.Collection contacts { - owned get { return contact_map.values; } - } +/** + * Interface for objects that provide contact information storage. + * + * Implementations of this class will typically be backed by a + * database. As such, to avoid IO overhead, batch calls together using + * collections of contacts wherever possible. + */ +public interface Geary.ContactStore : GLib.Object { - private Gee.Map contact_map; + /** Returns the contact matching the given email address, if any */ + public abstract async Contact? get_by_rfc822(Geary.RFC822.MailboxAddress address, + GLib.Cancellable? cancellable) + throws GLib.Error; - public signal void contacts_added(Gee.Collection contacts); + /** Updates (or adds) a set of contacts in the underlying store */ + public abstract async void update_contacts(Gee.Collection updated, + GLib.Cancellable? cancellable) + throws GLib.Error; - public signal void contacts_updated(Gee.Collection contacts); - - protected ContactStore() { - contact_map = new Gee.HashMap(); - } - - public void update_contacts(Gee.Collection new_contacts) { - Gee.LinkedList added = new Gee.LinkedList(); - Gee.LinkedList updated = new Gee.LinkedList(); - - foreach (Contact contact in new_contacts) { - Contact? old_contact = contact_map[contact.normalized_email]; - if (old_contact == null) { - contact_map[contact.normalized_email] = contact; - added.add(contact); - } else if (old_contact.highest_importance < contact.highest_importance) { - old_contact.highest_importance = contact.highest_importance; - updated.add(contact); - } - } - - if (!added.is_empty) - contacts_added(added); - - if (!updated.is_empty) - contacts_updated(updated); - } - - public abstract async void mark_contacts_async(Gee.Collection contacts, ContactFlags? to_add, - ContactFlags? to_remove) throws Error; - - public Contact? get_by_rfc822(Geary.RFC822.MailboxAddress address) { - return contact_map[address.address.normalize().casefold()]; - } } diff --git a/src/engine/common/common-contact-store-impl.vala b/src/engine/common/common-contact-store-impl.vala new file mode 100644 index 00000000..e060bbc0 --- /dev/null +++ b/src/engine/common/common-contact-store-impl.vala @@ -0,0 +1,141 @@ +/* + * Copyright 2016 Software Freedom Conservancy Inc. + * Copyright 2019 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. + */ + +/** + * An database-backed implementation of Geary.Contacts + */ +internal class Geary.ContactStoreImpl : BaseObject, Geary.ContactStore { + + + // Insert or update a contact in the ContactTable. If contact + // already exists, flags are merged and the importance is updated + // to the highest importance seen. + // + // Internal and static since it is used by ImapDB.Database during + // upgrades + internal static void do_update_contact(Db.Connection cx, + Contact contact, + GLib.Cancellable? cancellable) + throws GLib.Error { + Contact? existing = do_fetch_contact( + cx, contact.email, cancellable + ); + + if (contact == null) { + // Not found, so just insert it + Db.Statement stmt = cx.prepare( + "INSERT INTO ContactTable(normalized_email, email, real_name, flags, highest_importance) " + + "VALUES(?, ?, ?, ?, ?)"); + stmt.bind_string(0, contact.normalized_email); + stmt.bind_string(1, contact.email); + stmt.bind_string(2, contact.real_name); + stmt.bind_string(3, (contact.contact_flags != null) ? contact.contact_flags.serialize() : null); + stmt.bind_int(4, contact.highest_importance); + + stmt.exec(cancellable); + } else { + // Update existing contact + + // Merge two flags sets together + ContactFlags? merged_flags = contact.contact_flags; + if (existing.contact_flags != null) { + if (merged_flags != null) { + merged_flags.add_all(existing.contact_flags); + } else { + merged_flags = existing.contact_flags; + } + } + + // update remaining fields, careful not to overwrite + // non-null real_name with null (but using latest + // real_name if supplied) ... email is not updated (it's + // how existing was keyed), normalized_email is inserted at + // the same time as email, leaving only real_name, flags, + // and highest_importance + Db.Statement stmt = cx.prepare( + "UPDATE ContactTable SET real_name=?, flags=?, highest_importance=? WHERE email=?"); + stmt.bind_string( + 0, !String.is_empty(contact.real_name) ? contact.real_name : existing.real_name + ); + stmt.bind_string( + 1, (merged_flags != null) ? merged_flags.serialize() : null + ); + stmt.bind_int( + 2, int.max(contact.highest_importance, existing.highest_importance) + ); + stmt.bind_string( + 3, contact.email + ); + + stmt.exec(cancellable); + } + } + + // Static since it is indirectly used by ImapDB.Database during + // upgrades + private static Contact? do_fetch_contact(Db.Connection cx, + string email, + GLib.Cancellable? cancellable) + throws GLib.Error { + Db.Statement stmt = cx.prepare( + "SELECT real_name, highest_importance, normalized_email, flags FROM ContactTable " + + "WHERE email=?"); + stmt.bind_string(0, email); + + Db.Result result = stmt.exec(cancellable); + if (result.finished) + return null; + + return new Contact( + email, + result.string_at(0), + result.int_at(1), + result.string_at(2), + ContactFlags.deserialize(result.string_at(3)) + ); + } + + + private Geary.Db.Database backing; + + + internal ContactStoreImpl(Geary.Db.Database backing) { + base_ref(); + this.backing = backing; + } + + /** Returns the contact matching the given email address, if any */ + public async Contact? get_by_rfc822(Geary.RFC822.MailboxAddress address, + GLib.Cancellable? cancellable) + throws GLib.Error { + Contact? contact = null; + yield this.backing.exec_transaction_async( + Db.TransactionType.RO, + (cx, cancellable) => { + contact = do_fetch_contact(cx, address.mailbox, cancellable); + return Db.TransactionOutcome.COMMIT; + }, + cancellable); + return contact; + } + + public async void update_contacts(Gee.Collection updated, + GLib.Cancellable? cancellable) + throws GLib.Error { + yield this.backing.exec_transaction_async( + Db.TransactionType.RW, + (cx, cancellable) => { + foreach (Contact contact in updated) { + do_update_contact(cx, contact, cancellable); + } + return Db.TransactionOutcome.COMMIT; + }, + cancellable); + } + +} diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala index c57fc837..87744343 100644 --- a/src/engine/imap-db/imap-db-account.vala +++ b/src/engine/imap-db/imap-db-account.vala @@ -74,7 +74,6 @@ private class Geary.ImapDB.Account : BaseObject { private static Gee.HashMap search_op_is_values = new Gee.HashMap(); - public signal void contacts_loaded(); /** * The root path for all remote IMAP folders. @@ -90,7 +89,7 @@ private class Geary.ImapDB.Account : BaseObject { } // Only available when the Account is opened - public ImapEngine.ContactStore contact_store { get; private set; } + public ContactStore contact_store { get; private set; } public IntervalProgressMonitor search_index_monitor { get; private set; default = new IntervalProgressMonitor(ProgressType.SEARCH_INDEX, 0, 0); } public SimpleProgressMonitor upgrade_monitor { get; private set; default = new SimpleProgressMonitor( @@ -257,7 +256,6 @@ private class Geary.ImapDB.Account : BaseObject { public Account(AccountInformation config) { this.account_information = config; - this.contact_store = new ImapEngine.ContactStore(this); this.name = config.id + ":db"; } @@ -354,7 +352,7 @@ private class Geary.ImapDB.Account : BaseObject { return false; }); - initialize_contacts(cancellable); + this.contact_store = new ContactStoreImpl(db); } public async void close_async(Cancellable? cancellable) throws Error { @@ -458,40 +456,6 @@ private class Geary.ImapDB.Account : BaseObject { }, cancellable); } - private void initialize_contacts(Cancellable? cancellable = null) throws Error { - check_open(); - - Gee.Collection contacts = new Gee.LinkedList(); - Db.TransactionOutcome outcome = db.exec_transaction(Db.TransactionType.RO, - (context) => { - Db.Statement statement = context.prepare( - "SELECT email, real_name, highest_importance, normalized_email, flags " + - "FROM ContactTable"); - - Db.Result result = statement.exec(cancellable); - while (!result.finished) { - try { - Contact contact = new Contact(result.nonnull_string_at(0), result.string_at(1), - result.int_at(2), result.string_at(3), ContactFlags.deserialize(result.string_at(4))); - contacts.add(contact); - } catch (Geary.DatabaseError err) { - // We don't want to abandon loading all contacts just because there was a - // problem with one. - debug("Problem loading contact: %s", err.message); - } - - result.next(); - } - - return Db.TransactionOutcome.DONE; - }, cancellable); - - if (outcome == Db.TransactionOutcome.DONE) { - contact_store.update_contacts(contacts); - contacts_loaded(); - } - } - /** * Lists all children of a given folder. * @@ -1392,21 +1356,6 @@ private class Geary.ImapDB.Account : BaseObject { return email; } - public async void update_contact_flags_async(Geary.Contact contact, Cancellable? cancellable) - throws Error{ - check_open(); - - yield db.exec_transaction_async(Db.TransactionType.RW, (cx, cancellable) => { - Db.Statement update_stmt = - cx.prepare("UPDATE ContactTable SET flags=? WHERE email=?"); - update_stmt.bind_string(0, contact.contact_flags.serialize()); - update_stmt.bind_string(1, contact.email); - update_stmt.exec(cancellable); - - return Db.TransactionOutcome.COMMIT; - }, cancellable); - } - /** * Return a map of each passed-in email identifier to the set of folders * that contain it. If an email id doesn't appear in the resulting map, diff --git a/src/engine/imap-db/imap-db-contact.vala b/src/engine/imap-db/imap-db-contact.vala deleted file mode 100644 index 7ed96b40..00000000 --- a/src/engine/imap-db/imap-db-contact.vala +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright 2016 Software Freedom Conservancy Inc. - * - * This software is licensed under the GNU Lesser General Public License - * (version 2.1 or later). See the COPYING file in this distribution. - */ - -namespace Geary.ImapDB { - -private Contact? do_fetch_contact(Db.Connection cx, string email, Cancellable? cancellable) - throws Error { - Db.Statement stmt = cx.prepare( - "SELECT real_name, highest_importance, normalized_email, flags FROM ContactTable " - + "WHERE email=?"); - stmt.bind_string(0, email); - - Db.Result result = stmt.exec(cancellable); - if (result.finished) - return null; - - return new Contact(email, result.string_at(0), result.int_at(1), result.string_at(2), - ContactFlags.deserialize(result.string_at(3))); -} - -// Insert or update a contact in the ContactTable. If contact already exists, flags are merged -// and the importance is updated to the highest importance seen. -private void do_update_contact(Db.Connection connection, Contact contact, - Cancellable? cancellable) throws Error { - Contact? existing_contact = do_fetch_contact(connection, contact.email, cancellable); - - // If not found, insert and done - if (existing_contact == null) { - Db.Statement stmt = connection.prepare( - "INSERT INTO ContactTable(normalized_email, email, real_name, flags, highest_importance) " - + "VALUES(?, ?, ?, ?, ?)"); - stmt.bind_string(0, contact.normalized_email); - stmt.bind_string(1, contact.email); - stmt.bind_string(2, contact.real_name); - stmt.bind_string(3, (contact.contact_flags != null) ? contact.contact_flags.serialize() : null); - stmt.bind_int(4, contact.highest_importance); - - stmt.exec(cancellable); - - return; - } - - // merge two flags sets together - ContactFlags? merged_flags = contact.contact_flags; - if (existing_contact.contact_flags != null) { - if (merged_flags != null) - merged_flags.add_all(existing_contact.contact_flags); - else - merged_flags = existing_contact.contact_flags; - } - - // update remaining fields, careful not to overwrite non-null real_name with null (but - // using latest real_name if supplied) ... email is not updated (it's how existing_contact was - // keyed), normalized_email is inserted at the same time as email, leaving only real_name, - // flags, and highest_importance - Db.Statement stmt = connection.prepare( - "UPDATE ContactTable SET real_name=?, flags=?, highest_importance=? WHERE email=?"); - stmt.bind_string(0, !String.is_empty(contact.real_name) ? contact.real_name : existing_contact.real_name); - stmt.bind_string(1, (merged_flags != null) ? merged_flags.serialize() : null); - stmt.bind_int(2, int.max(contact.highest_importance, existing_contact.highest_importance)); - stmt.bind_string(3, contact.email); - - stmt.exec(cancellable); -} - -} - diff --git a/src/engine/imap-db/imap-db-database.vala b/src/engine/imap-db/imap-db-database.vala index b963320f..02672f52 100644 --- a/src/engine/imap-db/imap-db-database.vala +++ b/src/engine/imap-db/imap-db-database.vala @@ -190,7 +190,7 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase { MessageAddresses message_addresses = new MessageAddresses.from_result(account_owner_email, result); foreach (Contact contact in message_addresses.contacts) { - do_update_contact(cx, contact, null); + ContactStoreImpl.do_update_contact(cx, contact, null); } result.next(); } diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala index 8737af70..a28147d6 100644 --- a/src/engine/imap-db/imap-db-folder.vala +++ b/src/engine/imap-db/imap-db-folder.vala @@ -321,8 +321,11 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { return Db.TransactionOutcome.COMMIT; }, cancellable); - if (updated_contacts.size > 0) - contact_store.update_contacts(updated_contacts); + if (updated_contacts.size > 0) { + yield this.contact_store.update_contacts( + updated_contacts, cancellable + ); + } if (update_totals) { // Update the email_unread properties. diff --git a/src/engine/imap-engine/imap-engine-contact-store.vala b/src/engine/imap-engine/imap-engine-contact-store.vala deleted file mode 100644 index 75cb618c..00000000 --- a/src/engine/imap-engine/imap-engine-contact-store.vala +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2016 Software Freedom Conservancy Inc. - * - * This software is licensed under the GNU Lesser General Public License - * (version 2.1 or later). See the COPYING file in this distribution. - */ - -internal class Geary.ImapEngine.ContactStore : Geary.ContactStore { - private weak ImapDB.Account account; - - internal ContactStore(ImapDB.Account account) { - this.account = account; - } - - public override async void mark_contacts_async(Gee.Collection contacts, ContactFlags? to_add, - ContactFlags? to_remove) throws Error{ - foreach (Contact contact in contacts) { - if (contact.contact_flags == null) - contact.contact_flags = new Geary.ContactFlags(); - - if (to_add != null) - contact.contact_flags.add_all(to_add); - - if (to_remove != null) - contact.contact_flags.remove_all(to_remove); - - yield account.update_contact_flags_async(contact, null); - } - } -} diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala b/src/engine/imap-engine/imap-engine-generic-account.vala index 8386419a..2f74b8b9 100644 --- a/src/engine/imap-engine/imap-engine-generic-account.vala +++ b/src/engine/imap-engine/imap-engine-generic-account.vala @@ -83,7 +83,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { base(config, imap, smtp); this.local = local; - this.local.contacts_loaded.connect(() => { contacts_loaded(); }); + this.contact_store = local.contact_store; imap.min_pool_size = IMAP_MIN_POOL_SIZE; imap.notify["current-status"].connect( @@ -485,10 +485,6 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account { return all_folders; } - public override Geary.ContactStore get_contact_store() { - return local.contact_store; - } - public override async Geary.Folder get_required_special_folder_async(Geary.SpecialFolderType special, Cancellable? cancellable) throws Error { if (!(special in get_supported_special_folders())) { diff --git a/src/engine/meson.build b/src/engine/meson.build index a9f962c8..d85f0c63 100644 --- a/src/engine/meson.build +++ b/src/engine/meson.build @@ -68,6 +68,7 @@ geary_engine_vala_sources = files( 'app/email-store/app-list-operation.vala', 'app/email-store/app-mark-operation.vala', + 'common/common-contact-store-impl.vala', 'common/common-message-data.vala', 'db/db.vala', @@ -172,7 +173,6 @@ geary_engine_vala_sources = files( 'imap-db/imap-db-account.vala', 'imap-db/imap-db-attachment.vala', - 'imap-db/imap-db-contact.vala', 'imap-db/imap-db-database.vala', 'imap-db/imap-db-email-identifier.vala', 'imap-db/imap-db-folder.vala', @@ -189,7 +189,6 @@ geary_engine_vala_sources = files( 'imap-engine/imap-engine-account-operation.vala', 'imap-engine/imap-engine-account-processor.vala', 'imap-engine/imap-engine-account-synchronizer.vala', - 'imap-engine/imap-engine-contact-store.vala', 'imap-engine/imap-engine-email-prefetcher.vala', 'imap-engine/imap-engine-generic-account.vala', 'imap-engine/imap-engine-generic-folder.vala', diff --git a/test/engine/api/geary-account-mock.vala b/test/engine/api/geary-account-mock.vala index ce5304e3..7fb35b34 100644 --- a/test/engine/api/geary-account-mock.vala +++ b/test/engine/api/geary-account-mock.vala @@ -16,18 +16,24 @@ public class Geary.MockAccount : Account, MockObject { } - public class MockContactStore : ContactStore { + public class MockContactStore : GLib.Object, ContactStore { internal MockContactStore() { } - public override async void - mark_contacts_async(Gee.Collection contacts, - ContactFlags? to_add, - ContactFlags? to_remove) throws Error { - throw new EngineError.UNSUPPORTED("Mock method"); - } + public async Contact? get_by_rfc822(Geary.RFC822.MailboxAddress address, + GLib.Cancellable? cancellable) + throws GLib.Error { + throw new EngineError.UNSUPPORTED("Mock method"); + } + + public async void update_contacts(Gee.Collection contacts, + GLib.Cancellable? cancellable) + throws GLib.Error { + throw new EngineError.UNSUPPORTED("Mock method"); + } + } @@ -171,10 +177,6 @@ public class Geary.MockAccount : Account, MockObject { ); } - public override Geary.ContactStore get_contact_store() { - return new MockContactStore(); - } - public override Folder? get_special_folder(SpecialFolderType special) throws Error { return object_call(