[geary/wip/714802-chron: 2/3] Fix, simplify AccountSynchronizer



commit f057e65c99573ffe86498862fa2bb20418865e9e
Author: Jim Nelson <jim yorba org>
Date:   Fri Jan 23 17:28:42 2015 -0800

    Fix, simplify AccountSynchronizer

 .../imap-engine-account-synchronizer.vala          |  236 +++++++++-----------
 .../imap-engine/imap-engine-minimal-folder.vala    |    4 +-
 .../imap-engine-abstract-list-email.vala           |    3 +-
 .../replay-ops/imap-engine-list-email-by-id.vala   |    4 +-
 .../imap-engine-server-search-email.vala           |   23 +--
 5 files changed, 117 insertions(+), 153 deletions(-)
---
diff --git a/src/engine/imap-engine/imap-engine-account-synchronizer.vala 
b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
index de64620..97c8e85 100644
--- a/src/engine/imap-engine/imap-engine-account-synchronizer.vala
+++ b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
@@ -9,10 +9,24 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
     private const int SYNC_DELAY_SEC = 10;
     private const int RETRY_SYNC_DELAY_SEC = 60;
     
+    private enum Reason {
+        MADE_AVAILABLE,
+        ALTERED
+    }
+    
+    private class SyncMessage : BaseObject {
+        public MinimalFolder folder;
+        public Reason reason;
+        
+        public SyncMessage(MinimalFolder folder, Reason reason) {
+            this.folder = folder;
+            this.reason = reason;
+        }
+    }
+    
     public GenericAccount account { get; private set; }
     
-    private Nonblocking.Mailbox<MinimalFolder> bg_queue = new 
Nonblocking.Mailbox<MinimalFolder>(bg_queue_comparator);
-    private Gee.HashSet<MinimalFolder> made_available = new Gee.HashSet<MinimalFolder>();
+    private Nonblocking.Mailbox<SyncMessage> bg_queue = new 
Nonblocking.Mailbox<SyncMessage>(bg_queue_comparator);
     private MinimalFolder? current_folder = null;
     private Cancellable? bg_cancellable = null;
     private Nonblocking.Semaphore stopped = new Nonblocking.Semaphore();
@@ -78,10 +92,8 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
     
     private void on_account_prefetch_changed() {
         try {
-            // treat as an availability check (i.e. as if the account had just opened) because
-            // just because this value has changed doesn't mean the contents in the folders
-            // have changed
-            delayed_send_all(account.list_folders(), true, SYNC_DELAY_SEC);
+            // if prefetch value changes, reschedule all folders to account for new value
+            delayed_send_all(account.list_folders(), Reason.MADE_AVAILABLE, SYNC_DELAY_SEC);
         } catch (Error err) {
             debug("Unable to schedule re-sync for %s due to prefetch time changing: %s",
                 account.to_string(), err.message);
@@ -97,7 +109,7 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
             foreach (Folder folder in available)
                 unavailable_paths.remove(folder.path);
             
-            delayed_send_all(available, true, SYNC_DELAY_SEC);
+            delayed_send_all(available, Reason.MADE_AVAILABLE, SYNC_DELAY_SEC);
         }
         
         if (unavailable != null) {
@@ -109,35 +121,28 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
     }
     
     private void on_folders_contents_altered(Gee.Collection<Folder> altered) {
-        delayed_send_all(altered, false, SYNC_DELAY_SEC);
+        delayed_send_all(altered, Reason.ALTERED, SYNC_DELAY_SEC);
     }
     
     private void on_email_sent() {
         try {
             Folder? sent_mail = account.get_special_folder(SpecialFolderType.SENT);
             if (sent_mail != null)
-                send_all(iterate<Folder>(sent_mail).to_array_list(), false);
+                send_all(iterate<Folder>(sent_mail).to_array_list(), Reason.ALTERED);
         } catch (Error err) {
             debug("Unable to retrieve Sent Mail from %s: %s", account.to_string(), err.message);
         }
     }
     
-    private void delayed_send_all(Gee.Collection<Folder> folders, bool reason_available, int sec) {
+    private void delayed_send_all(Gee.Collection<Folder> folders, Reason reason, int sec) {
         Timeout.add_seconds(sec, () => {
-            // remove any unavailable folders
-            Gee.ArrayList<Folder> trimmed_folders = new Gee.ArrayList<Folder>();
-            foreach (Folder folder in folders) {
-                if (!unavailable_paths.contains(folder.path))
-                    trimmed_folders.add(folder);
-            }
-            
-            send_all(trimmed_folders, reason_available);
+            send_all(folders, reason);
             
             return false;
         });
     }
     
-    private void send_all(Gee.Collection<Folder> folders, bool reason_available) {
+    private void send_all(Gee.Collection<Folder> folders, Reason reason) {
         foreach (Folder folder in folders) {
             MinimalFolder? imap_folder = folder as MinimalFolder;
             
@@ -145,48 +150,65 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
             if (imap_folder == null)
                 continue;
             
+            // don't bother with unselectable or local-only folders
+            if (!folder.properties.is_openable.is_possible() || folder.properties.is_local_only)
+                continue;
+            
+            // drop unavailable folders
+            if (unavailable_paths.contains(folder.path))
+                continue;
+            
             // if considering folder not because it's available (i.e. because its contents changed),
             // and the folder is open, don't process it; MinimalFolder will take care of changes as
             // they occur, in order to remain synchronized
-            if (imap_folder.get_open_state() != Folder.OpenState.CLOSED)
+            if (reason != Reason.MADE_AVAILABLE && folder.get_open_state() != Folder.OpenState.CLOSED)
                 continue;
             
             // don't requeue the currently processing folder
-            if (imap_folder != current_folder)
-                bg_queue.send(imap_folder);
+            if (imap_folder == current_folder)
+                continue;
+            
+            // if altered, remove made_available sync message; if made_available, comparator will
+            // drop if altered message already present; this prioritizes altered over made_available
+            if (reason == Reason.ALTERED) {
+                bg_queue.revoke_matching((msg) => {
+                    return msg.folder == imap_folder && msg.reason == Reason.MADE_AVAILABLE;
+                });
+            }
             
-            // If adding because now available, make sure it's flagged as such, since there's an
-            // additional check for available folders ... if not, remove from the map so it's
-            // not treated as such, in case both of these come in back-to-back
-            if (reason_available && imap_folder != current_folder)
-                made_available.add(imap_folder);
-            else
-                made_available.remove(imap_folder);
+            bg_queue.send(new SyncMessage(imap_folder, reason));
         }
     }
     
     private void revoke_all(Gee.Collection<Folder> folders) {
         foreach (Folder folder in folders) {
-            MinimalFolder? generic_folder = folder as MinimalFolder;
-            if (generic_folder != null) {
-                bg_queue.revoke(generic_folder);
-                made_available.remove(generic_folder);
-            }
+            MinimalFolder? imap_folder = folder as MinimalFolder;
+            if (imap_folder == null)
+                continue;
+            
+            bg_queue.revoke_matching((msg) => {
+                return msg.folder == imap_folder;
+            });
         }
     }
     
     // This is used to ensure that certain special folders get prioritized over others, so folders
     // important to the user (i.e. Inbox) go first while less-used folders (Spam) are fetched last
-    private static int bg_queue_comparator(MinimalFolder a, MinimalFolder b) {
+    private static int bg_queue_comparator(SyncMessage a, SyncMessage b) {
         if (a == b)
             return 0;
         
-        int cmp = score_folder(a) - score_folder(b);
+        // don't allow the same folder in the queue at the same time; it's up to the submitter to
+        // remove existing folders based on reason
+        if (a.folder == b.folder)
+            return 0;
+        
+        int cmp = score_folder(a.folder) - score_folder(b.folder);
         if (cmp != 0)
             return cmp;
         
         // sort by path to stabilize the sort
-        return a.path.compare_to(b.path);
+        return a.folder.path.compare_to(b.folder.path);
     }
     
     // Lower the score, the higher the importance.
@@ -236,9 +258,9 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
     
     private async void process_queue_async() {
         for (;;) {
-            MinimalFolder folder;
+            SyncMessage msg;
             try {
-                folder = yield bg_queue.recv_async(bg_cancellable);
+                msg = yield bg_queue.recv_async(bg_cancellable);
             } catch (Error err) {
                 if (!(err is IOError.CANCELLED))
                     debug("Failed to receive next folder for background sync: %s", err.message);
@@ -247,7 +269,7 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
             }
             
             // mark as current folder to prevent requeues while processing
-            current_folder = folder;
+            current_folder = msg.folder;
             
             // generate the current epoch for synchronization (could cache this value, obviously, but
             // doesn't seem like this biggest win in this class)
@@ -259,37 +281,36 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
                 epoch = max_epoch;
             }
             
-            bool ok = yield process_folder_async(folder, made_available.remove(folder), epoch);
+            bool not_cancelled = yield process_folder_async(msg.folder, msg.reason, epoch);
             
             // clear current folder in every event
             current_folder = null;
             
-            if (!ok)
+            if (!not_cancelled)
                 break;
         }
         
         // clear queue of any remaining folders so references aren't held
         bg_queue.clear();
         
-        // same with made_available table
-        made_available.clear();
-        
         // flag as stopped for any waiting tasks
         stopped.blind_notify();
     }
     
     // Returns false if IOError.CANCELLED received
-    private async bool process_folder_async(MinimalFolder folder, bool availability_check, DateTime epoch) {
-        // get oldest local email and its time, as well as number of messages in local store
-        DateTime? oldest_local = null;
+    private async bool process_folder_async(MinimalFolder folder, Reason reason, DateTime epoch) {
+        // get oldest local email; when folders are more-or-less synchronized, this reduces the
+        // IMAP SEARCH results by only asking for messages since a certain date that exist in the
+        // UID message set prior to this earliest one, reducing network traffic by not returning
+        // UIDs for messages already in the local store
+        DateTime? oldest_local_date = null;
         Geary.EmailIdentifier? oldest_local_id = null;
         int local_count = 0;
         try {
             Gee.List<Geary.Email>? list = yield folder.local_folder.list_email_by_id_async(null, 1,
-                Email.Field.PROPERTIES, ImapDB.Folder.ListFlags.NONE | 
ImapDB.Folder.ListFlags.OLDEST_TO_NEWEST,
-                bg_cancellable);
+                Email.Field.PROPERTIES, ImapDB.Folder.ListFlags.OLDEST_TO_NEWEST, bg_cancellable);
             if (list != null && list.size > 0) {
-                oldest_local = list[0].properties.date_received;
+                oldest_local_date = list[0].properties.date_received;
                 oldest_local_id = list[0].id;
             }
             
@@ -299,35 +320,26 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
             debug("Unable to fetch oldest local email for %s: %s", folder.to_string(), err.message);
         }
         
-        if (availability_check) {
-            // Compare the oldest mail in the local store and see if it is before the epoch; if so, no
-            // need to synchronize simply because this Folder is available; wait for its contents to
-            // change instead
-            if (oldest_local != null) {
-                if (oldest_local.compare(epoch) < 0) {
-                    // Oldest local email before epoch, don't sync from network
+        // altered folders will always been synchronized, but if only made available (i.e. the
+        // Account just opened up or the folder was just discovered), do some checking to avoid
+        // round-tripping to the server
+        if (reason == Reason.MADE_AVAILABLE) {
+            if (oldest_local_id != null) {
+                if (oldest_local_date.compare(epoch) < 0) {
+                    // Oldest local is before epoch
                     return true;
                 } else if (folder.properties.email_total == local_count) {
-                    // Local earliest email is after epoch, but there's nothing before it
+                    // Completely synchronized with remote
                     return true;
-                } else {
-                    debug("Oldest local email in %s not old enough (%s vs. %s), email_total=%d vs. 
local_count=%d, synchronizing...",
-                        folder.to_string(), oldest_local.to_string(), epoch.to_string(),
-                        folder.properties.email_total, local_count);
                 }
             } else if (folder.properties.email_total == 0) {
-                // no local messages, no remote messages -- this is as good as having everything up
-                // to the epoch
+                // no local messages, no remote messages
                 return true;
-            } else {
-                debug("No oldest message found for %s, synchronizing...", folder.to_string());
             }
-        } else {
-            debug("Folder %s changed, synchronizing...", folder.to_string());
         }
         
         try {
-            yield folder.open_async(Folder.OpenFlags.FAST_OPEN, bg_cancellable);
+            yield folder.open_async(Folder.OpenFlags.NO_DELAY, bg_cancellable);
         } catch (Error err) {
             // don't need to close folder; if either calls throws an error, the folder is not open
             if (err is IOError.CANCELLED)
@@ -336,14 +348,14 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
             debug("Unable to open %s: %s", folder.to_string(), err.message);
             
             // retry later
-            delayed_send_all(iterate<Folder>(folder).to_array_list(), false, RETRY_SYNC_DELAY_SEC);
+            delayed_send_all(iterate<Folder>(folder).to_array_list(), reason, RETRY_SYNC_DELAY_SEC);
             
             return true;
         }
         
         bool not_cancelled = true;
         try {
-            yield sync_folder_async(folder, epoch, oldest_local, oldest_local_id);
+            yield sync_folder_async(folder, epoch, oldest_local_id);
         } catch (Error err) {
             if (err is IOError.CANCELLED) {
                 not_cancelled = false;
@@ -351,7 +363,7 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
                 debug("Error background syncing folder %s: %s", folder.to_string(), err.message);
                 
                 // retry later
-                delayed_send_all(iterate<Folder>(folder).to_array_list(), false, RETRY_SYNC_DELAY_SEC);
+                delayed_send_all(iterate<Folder>(folder).to_array_list(), reason, RETRY_SYNC_DELAY_SEC);
             }
             
             // fallthrough and close
@@ -367,7 +379,7 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
         return not_cancelled;
     }
     
-    private async void sync_folder_async(MinimalFolder folder, DateTime epoch, DateTime? oldest_local,
+    private async void sync_folder_async(MinimalFolder folder, DateTime epoch,
         Geary.EmailIdentifier? oldest_local_id) throws Error {
         debug("Background sync'ing %s", folder.to_string());
         
@@ -375,67 +387,23 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
         // information
         yield folder.wait_for_open_async(bg_cancellable);
         
-        // only perform vector expansion if oldest isn't old enough
-        if (oldest_local == null || oldest_local.compare(epoch) > 0) {
-            // go back three months at a time to the epoch, performing a little vector expansion at a
-            // time rather than all at once (which will stall the replay queue)
-            DateTime current_epoch = (oldest_local != null) ? oldest_local : new DateTime.now_local();
-            do {
-                // look for complete synchronization of UIDs (i.e. complete vector normalization)
-                // no need to keep searching once this happens
-                int local_count = yield 
folder.local_folder.get_email_count_async(ImapDB.Folder.ListFlags.NONE,
-                    bg_cancellable);
-                if (local_count >= folder.properties.email_total) {
-                    debug("Total vector normalization for %s: %d/%d emails", folder.to_string(), local_count,
-                        folder.properties.email_total);
-                    
-                    break;
-                }
-                
-                current_epoch = current_epoch.add_months(-1);
-                
-                // if past max_epoch, then just pull in everything and be done with it
-                if (current_epoch.compare(max_epoch) < 0) {
-                    debug("Background sync reached max epoch of %s, fetching all mail from %s",
-                        max_epoch.to_string(), folder.to_string());
-                    
-                    yield folder.list_email_by_id_async(null, 1, Geary.Email.Field.NONE,
-                        Geary.Folder.ListFlags.OLDEST_TO_NEWEST, bg_cancellable);
-                } else {
-                    // don't go past proscribed epoch
-                    if (current_epoch.compare(epoch) < 0)
-                        current_epoch = epoch;
-                    
-                    debug("Background sync'ing %s to %s", folder.to_string(), current_epoch.to_string());
-                    Geary.EmailIdentifier? earliest_span_id = yield 
folder.find_earliest_email_async(current_epoch,
-                        oldest_local_id, bg_cancellable);
-                    if (earliest_span_id == null && current_epoch.compare(epoch) <= 0) {
-                        debug("Unable to locate epoch messages on remote folder %s%s, fetching one past 
oldest...",
-                            folder.to_string(),
-                            (oldest_local_id != null) ? " earlier than oldest local" : "");
-                        
-                        // if there's nothing between the oldest local and the epoch, that means the
-                        // mail just prior to our local oldest is oldest than the epoch; rather than
-                        // continually thrashing looking for something that's just out of reach, add it
-                        // to the folder and be done with it ... note that this even works if oldest_local_id
-                        // is null, as that means the local folder is empty and so we should at least
-                        // pull the first one to get a marker of age
-                        yield folder.list_email_by_id_async(oldest_local_id, 1, Geary.Email.Field.NONE,
-                            Geary.Folder.ListFlags.NONE, bg_cancellable);
-                    } else if (earliest_span_id != null) {
-                        // use earliest email from that span for the next round
-                        oldest_local_id = earliest_span_id;
-                    }
-                }
-                
-                yield Scheduler.sleep_ms_async(200);
-            } while (current_epoch.compare(epoch) > 0);
-        } else {
-            debug("No expansion necessary for %s, oldest local (%s) is before epoch (%s)",
-                folder.to_string(), oldest_local.to_string(), epoch.to_string());
-        }
+        // find the first email (from 1 to n) that is since the epoch date but before the oldest local
+        // id; this will expand the vector in the database to accomodate it, pulling in the
+        // ImapDB.Folder.REQUIRED_FIELDS for all email from the end of the vector (n) to it
+        Geary.EmailIdentifier? earliest_id = yield folder.find_earliest_email_async(epoch,
+            oldest_local_id, bg_cancellable);
+        
+        // found an email in the range that is older than epoch, list *one email earlier* than
+        // it to act as a sentinal; that email will be before the epoch and prevent future
+        // resynchronization when MADE_AVAILABLE triggers (which happens each startup) ...
+        // if not found, use the oldest_local_id to pull one before it to act as the sentinel;
+        // if both are null, use that, which will pull the most recent email for the folder and
+        // that becomes the sentinal
+        yield folder.list_email_by_id_async(earliest_id ?? oldest_local_id, 1, Email.Field.NONE,
+            Folder.ListFlags.NONE, bg_cancellable);
         
-        // always give email prefetcher time to finish its work
+        // always give email prefetcher time to finish its work; this synchronizes the folder's
+        // contents with the (possibly expanded) vector
         debug("Waiting for email prefetcher to complete %s...", folder.to_string());
         try {
             yield folder.email_prefetcher.active_sem.wait_async(bg_cancellable);
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala 
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index 66d39b7..39d1e26 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -1388,8 +1388,8 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         notify_email_flags_changed(changed);
     }
     
-    // TODO: A proper public search mechanism; note that this always round-trips to the remote,
-    // doesn't go through the replay queue, and doesn't deal with messages marked for deletion
+    // TODO: A proper public search mechanism; note that this always round-trips to the remote
+    // and ignores messages marked for deletion
     internal async Geary.EmailIdentifier? find_earliest_email_async(DateTime datetime,
         Geary.EmailIdentifier? before_id, Cancellable? cancellable) throws Error {
         check_open("find_earliest_email_async");
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
index 758c29b..ac6cb58 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
@@ -238,7 +238,8 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
             // if initial_id is null or no local earliest UID, then vector expansion is simple:
             // merely count backwards from the top of the locally available vector
             if (initial_uid == null || local_count == 0) {
-                low_pos = new Imap.SequenceNumber(Numeric.int_floor((remote_count - local_count) - count, 
1));
+                low_pos = new Imap.SequenceNumber(
+                    Numeric.int_floor((remote_count - local_count) - count + 1, 1));
                 
                 // don't set high_pos, leave null to use symbolic "highest" in MessageSet
                 high_pos = null;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
index af07b35..f8a1c2c 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala
@@ -101,7 +101,7 @@ private class Geary.ImapEngine.ListEmailByID : Geary.ImapEngine.AbstractListEmai
         if (is_fully_expanded == Trillian.FALSE) {
             if (flags.is_oldest_to_newest()) {
                 if (initial_id != null) {
-                    // expand vector if not initial_id not discovered
+                    // expand vector if initial uid is unknown
                     expansion_required = (initial_uid == null);
                 } else {
                     // initial_id == null, expansion required if not fully already
@@ -113,7 +113,7 @@ private class Geary.ImapEngine.ListEmailByID : Geary.ImapEngine.AbstractListEmai
                     // if infinite count, expansion required if not already
                     expansion_required = true;
                 } else if (initial_id != null) {
-                    // finite count, expansion required if initial not found *or* not enough
+                    // finite count, expansion required if initial UID not found *or* not enough
                     // items were pulled in
                     expansion_required = (initial_uid == null) || (fulfilled_count + get_unfulfilled_count() 
< count);
                 } else {
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
index 5b1443d..2dd2cfa 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
@@ -39,22 +39,17 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
             yield expand_vector_async(uids.first(), 1);
         
         // Convert UIDs into EmailIdentifiers for lookup
-        Gee.HashSet<ImapDB.EmailIdentifier> local_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
-        foreach (Imap.UID uid in uids) {
-            // if null, presumably was picked up in the vector expansion (but hasn't been assigned
-            // to the database yet)
-            //
-            // TODO: We need a sparse version of this to scoop them up all at once
-            ImapDB.EmailIdentifier? id = yield owner.local_folder.get_id_async(uid,
-                ImapDB.Folder.ListFlags.NONE, cancellable);
-            if (id != null)
-                local_ids.add(id);
-        }
+        Gee.Set<ImapDB.EmailIdentifier>? local_ids = yield owner.local_folder.get_ids_async(uids,
+            ImapDB.Folder.ListFlags.NONE, cancellable);
         
-        Gee.List<Geary.Email>? local_list = yield owner.local_folder.list_email_by_sparse_id_async(
-            local_ids, required_fields, ImapDB.Folder.ListFlags.PARTIAL_OK, cancellable);
+        // Fetch what is in local store currently for those UIDs
+        Gee.List<Geary.Email>? local_list = null;
+        if (local_ids != null) {
+            local_list = yield owner.local_folder.list_email_by_sparse_id_async(local_ids, required_fields,
+                ImapDB.Folder.ListFlags.PARTIAL_OK, cancellable);
+        }
         
-        // Build list of local email
+        // Build map of local email
         Gee.Map<ImapDB.EmailIdentifier, Geary.Email> map = new Gee.HashMap<ImapDB.EmailIdentifier, 
Geary.Email>();
         if (local_list != null) {
             foreach (Geary.Email email in local_list)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]