[geary/wip/713150-conversations: 3/3] Further along, seeing speed improvements already
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/713150-conversations: 3/3] Further along, seeing speed improvements already
- Date: Fri, 20 Feb 2015 02:42:04 +0000 (UTC)
commit 7b8bd13d46d3a42b57ef3386c3174eabe894024b
Author: Jim Nelson <jim yorba org>
Date: Thu Feb 19 18:41:37 2015 -0800
Further along, seeing speed improvements already
Plenty of work to be done in ConversationMonitor
src/CMakeLists.txt | 1 +
src/engine/api/geary-account.vala | 4 +-
src/engine/api/geary-associated-emails.vala | 41 +++++
src/engine/app/app-conversation-monitor.vala | 185 +++++++++++++------
src/engine/imap-db/imap-db-account.vala | 129 +++++++++-----
.../imap-engine/imap-engine-generic-account.vala | 6 +-
6 files changed, 259 insertions(+), 107 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 35984d6..5ba1bc3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -7,6 +7,7 @@ engine/api/geary-abstract-local-folder.vala
engine/api/geary-account.vala
engine/api/geary-account-information.vala
engine/api/geary-aggregated-folder-properties.vala
+engine/api/geary-associated-emails.vala
engine/api/geary-attachment.vala
engine/api/geary-base-object.vala
engine/api/geary-composed-email.vala
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 14031d6..997b579 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -344,8 +344,8 @@ public abstract class Geary.Account : BaseObject {
* The particulars of the folder_blacklist and flag_blacklist parameters are the same as in
* local_search_message_id_async.
*/
- public abstract async Gee.MultiMap<Geary.Email, Geary.FolderPath?>? local_list_conversation_async(
- Geary.EmailIdentifier email_id, Geary.Email.Field requested_fields, bool partial_ok,
+ public abstract async Gee.Collection<Geary.AssociatedEmails>? local_search_associated_emails_async(
+ Gee.Set<Geary.EmailIdentifier> email_ids, Geary.Email.Field requested_fields, bool partial_ok,
Gee.Collection<Geary.FolderPath?> folder_blacklist, Geary.EmailFlags? flag_blacklist,
Cancellable? cancellable = null) throws Error;
diff --git a/src/engine/api/geary-associated-emails.vala b/src/engine/api/geary-associated-emails.vala
new file mode 100644
index 0000000..c51f74a
--- /dev/null
+++ b/src/engine/api/geary-associated-emails.vala
@@ -0,0 +1,41 @@
+/* Copyright 2015 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * An immutable representation of all known local { link Email}s which are associated with one
+ * another due to their Message-ID, In-Reply-To, and References headers.
+ *
+ * This object is free-form and does not impose any ordering or threading on the emails. It is
+ * also not updated as new email arrives and email is removed.
+ *
+ * @see Account.local_search_associated_emails_async
+ */
+
+public class Geary.AssociatedEmails : BaseObject {
+ /**
+ * All associated { link Email}s.
+ */
+ public Gee.Collection<Geary.Email> emails { get; private set; }
+
+ /**
+ * All known { link FolderPath}s for each { link Email}.
+ *
+ * null if the Email is currently associated with no { link Folder}s.
+ */
+ public Gee.MultiMap<Geary.Email, Geary.FolderPath?> known_paths { get; private set; }
+
+ public AssociatedEmails() {
+ emails = new Gee.ArrayList<Email>();
+ known_paths = new Gee.HashMultiMap<Email, FolderPath?>();
+ }
+
+ public void add(Geary.Email email, Gee.Collection<Geary.FolderPath?> paths) {
+ emails.add(email);
+ foreach (FolderPath path in paths)
+ known_paths.set(email, path);
+ }
+}
+
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index 567ee26..efbbae6 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -12,21 +12,9 @@ public class Geary.App.ConversationMonitor : BaseObject {
public const Geary.Email.Field REQUIRED_FIELDS = Geary.Email.Field.REFERENCES |
Geary.Email.Field.FLAGS | Geary.Email.Field.DATE;
-
// # of messages to load at a time as we attempt to fill the min window.
private const int WINDOW_FILL_MESSAGE_COUNT = 5;
- private class ProcessJobContext : BaseObject {
- public Gee.HashMap<Geary.EmailIdentifier, Geary.Email> emails
- = new Gee.HashMap<Geary.EmailIdentifier, Geary.Email>();
-
- public bool inside_scan;
-
- public ProcessJobContext(bool inside_scan) {
- this.inside_scan = inside_scan;
- }
- }
-
public Geary.Folder folder { get; private set; }
public bool is_monitoring { get; private set; default = false; }
public int min_window_count { get { return _min_window_count; }
@@ -38,7 +26,6 @@ public class Geary.App.ConversationMonitor : BaseObject {
public Geary.ProgressMonitor progress_monitor { get { return operation_queue.progress_monitor; } }
- private ConversationSet conversations = new ConversationSet();
private Geary.Email.Field required_fields;
private Geary.Folder.OpenFlags open_flags;
private Cancellable? cancellable_monitor = null;
@@ -46,6 +33,19 @@ public class Geary.App.ConversationMonitor : BaseObject {
private int _min_window_count = 0;
private ConversationOperationQueue operation_queue = new ConversationOperationQueue();
+ // All generated Conversations
+ private Gee.HashSet<Conversation> conversations = new Gee.HashSet<Conversation>();
+
+ // A logical map of Message-ID to Conversation ... these Message-IDs are merely referenced by
+ // emails and the email itself may not be present in the conversation
+ private Gee.HashMap<RFC822.MessageID, Conversation> message_id_to_conversation =
+ new Gee.HashMap<RFC822.MessageID, Conversation>();
+
+ // A map of EmailIdentifiers to Conversations ... unlike Message-IDs, these are known emails
+ // loaded into the conversations
+ private Gee.HashMap<EmailIdentifier, Conversation> email_id_to_conversation =
+ new Gee.HashMap<EmailIdentifier, Conversation>();
+
/**
* "monitoring-started" is fired when the Conversations folder has been opened for monitoring.
*/
@@ -184,9 +184,6 @@ public class Geary.App.ConversationMonitor : BaseObject {
~ConversationMonitor() {
if (is_monitoring)
debug("Warning: Conversations object destroyed without stopping monitoring");
-
- // Manually detach all the weak refs in the Conversation objects
- conversations.clear_owners();
}
protected virtual void notify_monitoring_started() {
@@ -239,12 +236,16 @@ public class Geary.App.ConversationMonitor : BaseObject {
return conversations.size;
}
+ public int get_email_count() {
+ return email_id_to_conversation.size;
+ }
+
public Gee.Collection<Conversation> get_conversations() {
- return conversations.conversations;
+ return conversations.read_only_view;
}
public Geary.App.Conversation? get_conversation_for_email(Geary.EmailIdentifier email_id) {
- return conversations.get_by_email_identifier(email_id);
+ return email_id_to_conversation[email_id];
}
public async bool start_monitoring_async(Cancellable? cancellable = null)
@@ -280,7 +281,6 @@ public class Geary.App.ConversationMonitor : BaseObject {
folder.opened.connect(on_folder_opened);
folder.account.email_flags_changed.connect(on_account_email_flags_changed);
folder.account.email_locally_complete.connect(on_account_email_locally_complete);
- // TODO: handle removed email
try {
yield folder.open_async(open_flags, cancellable);
@@ -356,32 +356,31 @@ public class Geary.App.ConversationMonitor : BaseObject {
throw close_err;
}
- /**
- * See Geary.Folder.list_email_by_id_async() for details of how these parameters operate. Instead
- * of returning emails, this method will load the Conversations object with them sorted into
- * Conversation objects.
- */
private async void load_by_id_async(Geary.EmailIdentifier? initial_id, int count,
Geary.Folder.ListFlags flags, Cancellable? cancellable) throws Error {
notify_scan_started();
try {
yield process_email_async(yield folder.list_email_by_id_async(initial_id,
- count, required_fields, flags, cancellable), new ProcessJobContext(true));
+ count, Email.Field.NONE, flags, cancellable));
} catch (Error err) {
- list_error(err);
+ notify_scan_error(err);
+
throw err;
+ } finally {
+ notify_scan_completed();
}
}
private async void load_by_sparse_id(Gee.Collection<Geary.EmailIdentifier> ids,
Geary.Folder.ListFlags flags, Cancellable? cancellable) {
notify_scan_started();
-
try {
yield process_email_async(yield folder.list_email_by_sparse_id_async(ids,
- required_fields, flags, cancellable), new ProcessJobContext(true));
+ Email.Field.NONE, flags, cancellable));
} catch (Error err) {
- list_error(err);
+ notify_scan_error(err);
+ } finally {
+ notify_scan_completed();
}
}
@@ -405,7 +404,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
foreach (Geary.Email email in emails) {
Gee.Set<RFC822.MessageID>? ancestors = email.get_ancestors();
if (ancestors != null &&
- Geary.traverse<RFC822.MessageID>(ancestors).any(id => conversations.has_message_id(id)))
+ Geary.traverse<RFC822.MessageID>(ancestors).any(id =>
message_id_to_conversation.contains(id)))
relevant_ids.add(email.id);
}
@@ -432,7 +431,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
debug("Fetched %d relevant emails locally", search_emails.size);
- yield process_email_async(search_emails, new ProcessJobContext(false));
+ // TODO: Only need id's for this
+ yield process_email_async(search_emails);
} catch (Error e) {
debug("Error loading external emails: %s", e.message);
if (opened) {
@@ -445,43 +445,102 @@ public class Geary.App.ConversationMonitor : BaseObject {
}
}
- private void list_error(Error err) {
- debug("Error while assembling conversations in %s: %s", folder.to_string(), err.message);
- notify_scan_error(err);
- notify_scan_completed();
- }
-
- private async void process_email_async(Gee.Collection<Geary.Email>? emails, ProcessJobContext job) {
- if (emails == null || emails.size == 0) {
- yield process_email_complete_async(job);
+ // Emails for this call only require Email.Field.NONE, as the EmailIdentifier is then used to
+ // load the associations and the required_fields
+ private async void process_email_async(Gee.Collection<Geary.Email>? emails) {
+ if (emails == null || emails.size == 0)
return;
- }
Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::process_email: %d emails",
folder.to_string(), emails.size);
- Gee.HashSet<RFC822.MessageID> new_message_ids = new Gee.HashSet<RFC822.MessageID>();
- foreach (Geary.Email email in emails) {
- if (!job.emails.has_key(email.id)) {
- job.emails.set(email.id, email);
+ Gee.HashSet<EmailIdentifier> email_ids = traverse<Email>(emails)
+ .map_nonnull<EmailIdentifier>(email => email.id)
+ .to_hash_set();
+
+ Gee.Collection<AssociatedEmails> associated;
+ try {
+ associated = yield folder.account.local_search_associated_emails_async(
+ email_ids, required_fields, false, get_search_blacklist(), get_search_flag_blacklist(),
+ null);
+ } catch (Error err) {
+ debug("Unable to search for associated emails: %s", err.message);
- Gee.Set<RFC822.MessageID>? ancestors = email.get_ancestors();
- if (ancestors != null) {
- Geary.traverse<RFC822.MessageID>(ancestors)
- .filter(id => !new_message_ids.contains(id))
- .add_all_to(new_message_ids);
- }
+ return;
+ }
+
+ Gee.HashSet<Conversation> added = new Gee.HashSet<Conversation>();
+ Gee.HashMultiMap<Conversation, Email> appended = new Gee.HashMultiMap<Conversation, Email>();
+
+ foreach (AssociatedEmails association in associated) {
+ // Get all ancestors for the associated emails
+ Gee.HashSet<RFC822.MessageID> ancestors = new Gee.HashSet<RFC822.MessageID>();
+ foreach (Email email in association.emails)
+ ancestors.add_all(email.get_ancestors());
+
+ // get all conversations for these emails (possible for multiple conversations to be
+ // started and then coalesce as new emails come in)
+ Gee.HashSet<Conversation> existing = new Gee.HashSet<Conversation>();
+ foreach (RFC822.MessageID ancestor in ancestors) {
+ Conversation? conversation = message_id_to_conversation[ancestor];
+ if (conversation != null)
+ existing.add(conversation);
+ }
+
+ // Create or pick conversation and reporting collection for these emails
+ Conversation conversation;
+ switch (existing.size) {
+ case 0:
+ conversation = new Conversation(this);
+ break;
+
+ case 1:
+ conversation = traverse<Conversation>(existing).first();
+ break;
+
+ default:
+ conversation = merge_conversations(existing);
+ break;
+ }
+
+ // add all emails and each known path(s) to the Conversation and EmailIdentifier mapping
+ foreach (Email email in association.emails) {
+ conversation.add(email, association.known_paths[email]);
+ email_id_to_conversation[email.id] = conversation;
+ }
+
+ // map all Message-IDs to this Conversation
+ foreach (RFC822.MessageID ancestor in ancestors)
+ message_id_to_conversation[ancestor] = conversation;
+
+ // if new, added, otherwise appended
+ if (!conversations.contains(conversation)) {
+ added.add(conversation);
+ conversations.add(conversation);
+ } else {
+ foreach (Email email in association.emails)
+ appended.set(conversation, email);
}
}
- // Expand the conversation to include any Message-IDs we know we need
- // and may have on disk, but aren't in the folder.
- yield expand_conversations_async(new_message_ids, job);
+ if (added.size > 0)
+ notify_conversations_added(added);
+
+ if (appended.size > 0) {
+ foreach (Conversation conversation in appended.get_keys())
+ notify_conversation_appended(conversation, appended.get(conversation));
+ }
Logging.debug(Logging.Flag.CONVERSATIONS, "[%s] ConversationMonitor::process_email completed: %d
emails",
folder.to_string(), emails.size);
}
+ // TODO
+ private Conversation merge_conversations(Gee.Set<Conversation> conversations) {
+ breakpoint();
+ return new Conversation(this);
+ }
+
private Gee.Collection<Geary.FolderPath> get_search_blacklist() {
Geary.SpecialFolderType[] blacklisted_folder_types = {
Geary.SpecialFolderType.SPAM,
@@ -514,6 +573,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
return flags;
}
+ /*
private async void expand_conversations_async(Gee.Set<RFC822.MessageID> needed_message_ids,
ProcessJobContext job) {
if (needed_message_ids.size == 0) {
@@ -564,7 +624,9 @@ public class Geary.App.ConversationMonitor : BaseObject {
"[%s] ConversationMonitor::expand_conversations completed: %d email ids (%d found)",
folder.to_string(), needed_message_ids.size, needed_messages.size);
}
+ */
+ /*
private async void process_email_complete_async(ProcessJobContext job) {
Gee.Collection<Geary.App.Conversation>? added = null;
Gee.MultiMap<Geary.App.Conversation, Geary.Email>? appended = null;
@@ -594,6 +656,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
if (job.inside_scan)
notify_scan_completed();
}
+ */
private void on_folder_email_appended(Gee.Collection<Geary.EmailIdentifier> appended_ids) {
operation_queue.add(new AppendOperation(this, appended_ids));
@@ -624,6 +687,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
debug("%d messages(s) removed from %s, trimming/removing conversations...", removed_ids.size,
folder.to_string());
+ /*
Gee.Collection<Geary.App.Conversation> removed;
Gee.MultiMap<Geary.App.Conversation, Geary.Email> trimmed;
yield conversations.remove_emails_and_check_in_folder_async(removed_ids, folder.account,
@@ -634,7 +698,9 @@ public class Geary.App.ConversationMonitor : BaseObject {
foreach (Conversation conversation in removed)
notify_conversation_removed(conversation);
+ */
+ /*
// For any still-existing conversations that we've trimmed messages
// from, do a search for any messages that should still be there due to
// full conversations. This way, some removed messages are instead
@@ -644,6 +710,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
foreach (Conversation conversation in trimmed.get_keys())
search_message_ids.add_all(conversation.get_message_ids());
yield expand_conversations_async(search_message_ids, new ProcessJobContext(false));
+ */
}
internal async void external_append_emails_async(Geary.Folder folder,
@@ -663,7 +730,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
private void on_account_email_flags_changed(Geary.Folder folder,
Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map) {
foreach (Geary.EmailIdentifier id in map.keys) {
- Conversation? conversation = conversations.get_by_email_identifier(id);
+ Conversation? conversation = email_id_to_conversation[id];
if (conversation == null)
continue;
@@ -679,8 +746,8 @@ public class Geary.App.ConversationMonitor : BaseObject {
private async Geary.EmailIdentifier? get_lowest_email_id_async(Cancellable? cancellable) {
Geary.EmailIdentifier? earliest_id = null;
try {
- yield folder.find_boundaries_async(conversations.get_email_identifiers(),
- out earliest_id, null, cancellable);
+ yield folder.find_boundaries_async(email_id_to_conversation.keys, out earliest_id, null,
+ cancellable);
} catch (Error e) {
debug("Error finding earliest email identifier: %s", e.message);
}
@@ -728,7 +795,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
if (!is_insert && min_window_count <= conversations.size)
return;
- int initial_message_count = conversations.get_email_count();
+ int initial_message_count = get_email_count();
// only do local-load if the Folder isn't completely opened, otherwise this operation
// will block other (more important) operations while it waits for the folder to
@@ -773,7 +840,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
}
// Run again to make sure we're full unless we ran out of messages.
- if (conversations.get_email_count() != initial_message_count)
+ if (get_email_count() != initial_message_count)
operation_queue.add(new FillWindowOperation(this, is_insert));
}
}
diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala
index 3beee1f..0c55aba 100644
--- a/src/engine/imap-db/imap-db-account.vala
+++ b/src/engine/imap-db/imap-db-account.vala
@@ -659,8 +659,16 @@ private class Geary.ImapDB.Account : BaseObject {
Db.Result result = stmt.exec(cancellable);
while (!result.finished) {
+ Email? email;
+ Gee.Collection<FolderPath?>? known_paths;
do_fetch_message(cx, result.int64_at(0), requested_fields, partial_ok, folder_blacklist,
- flag_blacklist, messages, cancellable);
+ flag_blacklist, out email, out known_paths, cancellable);
+ if (email != null) {
+ assert(known_paths != null);
+
+ foreach (FolderPath known_path in known_paths)
+ messages.set(email, known_path);
+ }
result.next(cancellable);
}
@@ -671,91 +679,126 @@ private class Geary.ImapDB.Account : BaseObject {
return (messages.size == 0 ? null : messages);
}
- public async Gee.MultiMap<Geary.Email, Geary.FolderPath?>? list_conversation_async(
- Geary.EmailIdentifier email_id, Email.Field requested_fields, bool partial_ok,
+ public async Gee.Collection<Geary.AssociatedEmails>? search_associated_emails_async(
+ Gee.Set<Geary.EmailIdentifier> email_ids, Email.Field requested_fields, bool partial_ok,
Gee.Collection<Geary.FolderPath?>? folder_blacklist, Geary.EmailFlags? flag_blacklist,
Cancellable? cancellable) throws Error {
check_open();
- ImapDB.EmailIdentifier? imapdb_id = email_id as ImapDB.EmailIdentifier;
- if (imapdb_id == null)
- throw new EngineError.BAD_PARAMETERS("Invalid identifier supplied to list conversation");
+ // Store in a casted HashSet that can be modified internally, as associated identifiers
+ // will be weeded out as loaded with other identifiers
+ Gee.HashSet<ImapDB.EmailIdentifier> db_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
+ foreach (Geary.EmailIdentifier email_id in email_ids) {
+ ImapDB.EmailIdentifier? db_id = email_id as ImapDB.EmailIdentifier;
+ if (db_id == null)
+ throw new EngineError.BAD_PARAMETERS("Invalid identifier supplied to list conversation");
+
+ db_ids.add(db_id);
+ }
- Gee.HashMultiMap<Geary.Email, Geary.FolderPath?> messages
- = new Gee.HashMultiMap<Geary.Email, Geary.FolderPath?>();
+ Gee.Collection<AssociatedEmails> associations = new Gee.ArrayList<AssociatedEmails>();
+ Gee.HashSet<ImapDB.EmailIdentifier> found_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
if (flag_blacklist != null)
requested_fields = requested_fields | Geary.Email.Field.FLAGS;
+ debug("Searching %d ids for associations...", email_ids.size);
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
- Db.Statement stmt = cx.prepare("""
- SELECT conversation_id
- FROM MessageTable
- WHERE id = ?
- """);
- stmt.bind_rowid(0, imapdb_id.message_id);
-
- Db.Result result = stmt.exec(cancellable);
- if (result.finished || result.is_null_at(0))
- return Db.TransactionOutcome.DONE;
-
- int64 conversation_id = result.rowid_at(0);
-
- stmt = cx.prepare("""
- SELECT id
- FROM MessageTable
- WHERE conversation_id = ?
- """);
- stmt.bind_rowid(0, conversation_id);
-
- result = stmt.exec(cancellable);
- while (!result.finished) {
- do_fetch_message(cx, result.int64_at(0), requested_fields, partial_ok, folder_blacklist,
- flag_blacklist, messages, cancellable);
+ foreach (ImapDB.EmailIdentifier db_id in db_ids) {
+ if (found_ids.contains(db_id))
+ continue;
- result.next(cancellable);
+ Db.Statement stmt = cx.prepare("""
+ SELECT conversation_id
+ FROM MessageTable
+ WHERE id = ?
+ """);
+ stmt.bind_rowid(0, db_id.message_id);
+
+ Db.Result result = stmt.exec(cancellable);
+ if (result.finished || result.is_null_at(0))
+ continue;
+
+ int64 conversation_id = result.rowid_at(0);
+
+ stmt = cx.prepare("""
+ SELECT id
+ FROM MessageTable
+ WHERE conversation_id = ?
+ """);
+ stmt.bind_rowid(0, conversation_id);
+
+ AssociatedEmails association = new AssociatedEmails();
+
+ result = stmt.exec(cancellable);
+ while (!result.finished) {
+ Email? email;
+ Gee.Collection<FolderPath?>? known_paths;
+ do_fetch_message(cx, result.int64_at(0), requested_fields, partial_ok, folder_blacklist,
+ flag_blacklist, out email, out known_paths, cancellable);
+ if (email != null) {
+ association.add(email, known_paths);
+ found_ids.add((ImapDB.EmailIdentifier) email.id);
+ }
+
+ result.next(cancellable);
+ }
+
+ associations.add(association);
}
return Db.TransactionOutcome.DONE;
}, cancellable);
+ debug("Found %d associations from %d ids", associations.size, email_ids.size);
- return messages.size > 0 ? messages : null;
+ return associations.size > 0 ? associations : null;
}
private void do_fetch_message(Db.Connection cx, int64 message_id, Email.Field required_fields,
bool partial_ok, Gee.Collection<Geary.FolderPath?>? folder_blacklist, Geary.EmailFlags?
flag_blacklist,
- Gee.HashMultiMap<Geary.Email, Geary.FolderPath?> messages, Cancellable? cancellable) throws Error {
+ out Email? email, out Gee.Collection<FolderPath?>? known_paths, Cancellable? cancellable) throws
Error {
Email.Field actual_fields;
MessageRow row = ImapDB.Folder.do_fetch_message_row(cx, message_id, required_fields,
out actual_fields, cancellable);
+ // prepare for the worst
+ email = null;
+ known_paths = null;
+
// Ignore any messages that don't have the required fields unless partial is ok
if (!partial_ok && !row.fields.fulfills(required_fields))
return;
- Email email = row.to_email(new Geary.ImapDB.EmailIdentifier(message_id, null));
+ email = row.to_email(new Geary.ImapDB.EmailIdentifier(message_id, null));
ImapDB.Folder.do_add_attachments(cx, email, message_id, cancellable);
+ // Check for blacklisted flags.
+ if (flag_blacklist != null && email.email_flags != null &&
email.email_flags.contains_any(flag_blacklist)) {
+ email = null;
+
+ return;
+ }
+
+ known_paths = new Gee.HashSet<FolderPath?>();
+
// Add folders email is found in, respecting blacklist
Gee.Set<Geary.FolderPath>? folders = do_find_email_folders(cx, message_id, true, cancellable);
if (folders == null) {
if (folder_blacklist == null || !folder_blacklist.contains(null))
- messages.set(email, null);
+ known_paths.add(null);
} else {
foreach (Geary.FolderPath path in folders) {
// If it's in a blacklisted folder, we don't report it at all.
if (folder_blacklist != null && folder_blacklist.contains(path)) {
- messages.remove_all(email);
+ email = null;
+ known_paths = null;
+
break;
} else {
- messages.set(email, path);
+ known_paths.add(path);
}
}
}
-
- // Check for blacklisted flags.
- if (flag_blacklist != null && email.email_flags != null &&
email.email_flags.contains_any(flag_blacklist))
- messages.remove_all(email);
}
private string? extract_field_from_token(string[] parts, ref string token) {
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala
b/src/engine/imap-engine/imap-engine-generic-account.vala
index fa029e0..8a3d3ea 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -859,11 +859,11 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
message_id, requested_fields, partial_ok, folder_blacklist, flag_blacklist, cancellable);
}
- public override async Gee.MultiMap<Geary.Email, Geary.FolderPath?>? local_list_conversation_async(
- Geary.EmailIdentifier email_id, Geary.Email.Field requested_fields, bool partial_ok,
+ public override async Gee.Collection<Geary.AssociatedEmails>? local_search_associated_emails_async(
+ Gee.Set<Geary.EmailIdentifier> email_ids, Geary.Email.Field requested_fields, bool partial_ok,
Gee.Collection<Geary.FolderPath?> folder_blacklist, Geary.EmailFlags? flag_blacklist,
Cancellable? cancellable = null) throws Error {
- return yield local.list_conversation_async(email_id, requested_fields, partial_ok,
+ return yield local.search_associated_emails_async(email_ids, requested_fields, partial_ok,
folder_blacklist, flag_blacklist, cancellable);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]