[geary/wip/remote-retry] Expanded concept to all replay operations and Folder interface



commit 2b7555dfcd25b3a734c34c5bb9017b52559bc4bd
Author: Jim Nelson <jim yorba org>
Date:   Fri Jan 16 10:19:08 2015 -0800

    Expanded concept to all replay operations and Folder interface

 src/engine/api/geary-folder-supports-archive.vala  |   15 +----
 src/engine/api/geary-folder-supports-copy.vala     |    5 +-
 src/engine/api/geary-folder-supports-create.vala   |    6 +-
 src/engine/api/geary-folder-supports-empty.vala    |    5 +-
 src/engine/api/geary-folder-supports-mark.vala     |   14 ----
 src/engine/api/geary-folder-supports-move.vala     |    5 +-
 src/engine/api/geary-folder-supports-remove.vala   |   18 +----
 src/engine/api/geary-future.vala                   |    4 +-
 src/engine/api/geary-search-folder.vala            |    6 +-
 src/engine/app/app-draft-manager.vala              |    3 +-
 src/engine/imap-db/outbox/smtp-outbox-folder.vala  |    4 +-
 .../gmail/imap-engine-gmail-all-mail-folder.vala   |    4 +-
 .../gmail/imap-engine-gmail-drafts-folder.vala     |    4 +-
 .../gmail/imap-engine-gmail-folder.vala            |   23 +++++---
 .../gmail/imap-engine-gmail-search-folder.vala     |    4 +-
 .../gmail/imap-engine-gmail-spam-trash-folder.vala |    8 +-
 .../imap-engine/imap-engine-generic-folder.vala    |   14 ++--
 .../imap-engine/imap-engine-minimal-folder.vala    |   63 ++++++++++++-------
 .../imap-engine/imap-engine-replay-operation.vala  |   20 ++++---
 .../imap-engine/imap-engine-replay-queue.vala      |   48 ++++++++-------
 .../imap-engine-send-replay-operation.vala         |    8 +-
 .../imap-engine-abstract-list-email.vala           |    2 +-
 .../replay-ops/imap-engine-copy-email.vala         |    2 +-
 .../replay-ops/imap-engine-create-email.vala       |    2 +-
 .../replay-ops/imap-engine-empty-folder.vala       |    2 +-
 .../replay-ops/imap-engine-fetch-email.vala        |    2 +-
 .../replay-ops/imap-engine-mark-email.vala         |    2 +-
 .../replay-ops/imap-engine-move-email.vala         |    2 +-
 .../replay-ops/imap-engine-remove-email.vala       |    5 +-
 .../replay-ops/imap-engine-replay-append.vala      |    4 +-
 .../replay-ops/imap-engine-replay-removal.vala     |    3 +-
 .../imap-engine-server-search-email.vala           |    3 +
 32 files changed, 166 insertions(+), 144 deletions(-)
---
diff --git a/src/engine/api/geary-folder-supports-archive.vala 
b/src/engine/api/geary-folder-supports-archive.vala
index 9796a76..9cfedb1 100644
--- a/src/engine/api/geary-folder-supports-archive.vala
+++ b/src/engine/api/geary-folder-supports-archive.vala
@@ -19,20 +19,7 @@ public interface Geary.FolderSupport.Archive : Geary.Folder {
      *
      * The { link Geary.Folder} must be opened prior to attempting this operation.
      */
-    public abstract async void archive_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public abstract async Geary.Future? archive_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error;
-    
-    /**
-     * Archive one email from the folder.
-     *
-     * The { link Geary.Folder} must be opened prior to attempting this operation.
-     */
-    public virtual async void archive_single_email_async(Geary.EmailIdentifier email_id,
-        Cancellable? cancellable = null) throws Error {
-        Gee.ArrayList<Geary.EmailIdentifier> ids = new Gee.ArrayList<Geary.EmailIdentifier>();
-        ids.add(email_id);
-        
-        yield archive_email_async(ids, cancellable);
-    }
 }
 
diff --git a/src/engine/api/geary-folder-supports-copy.vala b/src/engine/api/geary-folder-supports-copy.vala
index 351a16a..eb12b48 100644
--- a/src/engine/api/geary-folder-supports-copy.vala
+++ b/src/engine/api/geary-folder-supports-copy.vala
@@ -19,9 +19,12 @@ public interface Geary.FolderSupport.Copy : Geary.Folder {
      * If the destination is this { link Folder}, the operation will not make a copy of the message
      * but will return success.
      *
+     * If a { link Future} is returned, the operation has been scheduled for completion in the
+     * background.  The Future can be monitored or waited upon for final completion.
+     *
      * The Folder must be opened prior to attempting this operation.
      */
-    public abstract async void copy_email_async(Gee.List<Geary.EmailIdentifier> to_copy,
+    public abstract async Geary.Future? copy_email_async(Gee.List<Geary.EmailIdentifier> to_copy,
         Geary.FolderPath destination, Cancellable? cancellable = null) throws Error;
 }
 
diff --git a/src/engine/api/geary-folder-supports-create.vala 
b/src/engine/api/geary-folder-supports-create.vala
index babffe6..54cb9d6 100644
--- a/src/engine/api/geary-folder-supports-create.vala
+++ b/src/engine/api/geary-folder-supports-create.vala
@@ -26,9 +26,9 @@ public interface Geary.FolderSupport.Create : Geary.Folder {
      * Like EmailFlags, this is optional if not applicable.
      * 
      * If an id is passed, this will replace the existing message by deleting it after the new
-     * message is created.  The new message's ID is returned.
+     * message is created.  The new message's ID is returned in the { link Future}.
      */
-    public abstract async Geary.EmailIdentifier? create_email_async(Geary.RFC822.Message rfc822, EmailFlags? 
flags,
-        DateTime? date_received, Geary.EmailIdentifier? id = null, Cancellable? cancellable = null) throws 
Error;
+    public abstract async Geary.Future? create_email_async(Geary.RFC822.Message rfc822, EmailFlags? flags,
+        DateTime? date_received, Geary.EmailIdentifier? id, Cancellable? cancellable = null) throws Error;
 }
 
diff --git a/src/engine/api/geary-folder-supports-empty.vala b/src/engine/api/geary-folder-supports-empty.vala
index 69f1e48..69b48c2 100644
--- a/src/engine/api/geary-folder-supports-empty.vala
+++ b/src/engine/api/geary-folder-supports-empty.vala
@@ -19,7 +19,10 @@
 public interface Geary.FolderSupport.Empty : Geary.Folder {
     /**
      * Removes all email from the folder.
+     *
+     * If a { link Future} is returned, the operation has been scheduled for completion in the
+     * background.  The Future can be monitored or waited upon for final completion.
      */
-    public abstract async void empty_folder_async(Cancellable? cancellable = null) throws Error;
+    public abstract async Geary.Future? empty_folder_async(Cancellable? cancellable = null) throws Error;
 }
 
diff --git a/src/engine/api/geary-folder-supports-mark.vala b/src/engine/api/geary-folder-supports-mark.vala
index 4556fa7..eb28a61 100644
--- a/src/engine/api/geary-folder-supports-mark.vala
+++ b/src/engine/api/geary-folder-supports-mark.vala
@@ -21,19 +21,5 @@ public interface Geary.FolderSupport.Mark : Geary.Folder {
     public abstract async Geary.Future? mark_email_async(Gee.List<Geary.EmailIdentifier> to_mark,
         Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove,
         Cancellable? cancellable = null) throws Error;
-    
-    /**
-     * Adds and removes flags from a single message.
-     *
-     * The { link Geary.Folder} must be opened prior to attempting this operation.
-     */
-    public virtual async Geary.Future? mark_single_email_async(Geary.EmailIdentifier to_mark,
-        Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove,
-        Cancellable? cancellable = null) throws Error {
-        Gee.ArrayList<Geary.EmailIdentifier> list = new Gee.ArrayList<Geary.EmailIdentifier>();
-        list.add(to_mark);
-        
-        return yield mark_email_async(list, flags_to_add, flags_to_remove, cancellable);
-    }
 }
 
diff --git a/src/engine/api/geary-folder-supports-move.vala b/src/engine/api/geary-folder-supports-move.vala
index 58ededf..a43b4ae 100644
--- a/src/engine/api/geary-folder-supports-move.vala
+++ b/src/engine/api/geary-folder-supports-move.vala
@@ -18,9 +18,12 @@ public interface Geary.FolderSupport.Move : Geary.Folder {
      * If the destination is this { link Folder}, the operation will not move the message in any
      * way but will return success.
      *
+     * If a { link Future} is returned, the operation has been scheduled for completion in the
+     * background.  The Future can be monitored or waited upon for final completion.
+     *
      * The { link Geary.Folder} must be opened prior to attempting this operation.
      */
-    public abstract async void move_email_async(Gee.List<Geary.EmailIdentifier> to_move,
+    public abstract async Geary.Future? move_email_async(Gee.List<Geary.EmailIdentifier> to_move,
         Geary.FolderPath destination, Cancellable? cancellable = null) throws Error;
 }
 
diff --git a/src/engine/api/geary-folder-supports-remove.vala 
b/src/engine/api/geary-folder-supports-remove.vala
index 8326fb5..fec1fb5 100644
--- a/src/engine/api/geary-folder-supports-remove.vala
+++ b/src/engine/api/geary-folder-supports-remove.vala
@@ -23,22 +23,12 @@ public interface Geary.FolderSupport.Remove : Geary.Folder {
     /**
      * Removes the specified emails from the folder.
      *
-     * The { link Geary.Folder} must be opened prior to attempting this operation.
-     */
-    public abstract async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
-        Cancellable? cancellable = null) throws Error;
-    
-    /**
-     * Removes one email from the folder.
+     * If a { link Future} is returned, the operation has been scheduled for completion in the
+     * background.  The Future can be monitored or waited upon for final completion.
      *
      * The { link Geary.Folder} must be opened prior to attempting this operation.
      */
-    public virtual async void remove_single_email_async(Geary.EmailIdentifier email_id,
-        Cancellable? cancellable = null) throws Error {
-        Gee.ArrayList<Geary.EmailIdentifier> ids = new Gee.ArrayList<Geary.EmailIdentifier>();
-        ids.add(email_id);
-        
-        yield remove_email_async(ids, cancellable);
-    }
+    public abstract async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+        Cancellable? cancellable = null) throws Error;
 }
 
diff --git a/src/engine/api/geary-future.vala b/src/engine/api/geary-future.vala
index 9d76cbf..4632239 100644
--- a/src/engine/api/geary-future.vala
+++ b/src/engine/api/geary-future.vala
@@ -5,10 +5,12 @@
  */
 
 /**
- * A generic mechanism for a method to return a result value after completing.
+ * A generic mechanism for a method to return a result value after an asynchronous call completes.
  *
  * Although designed with Geary's asynchronous interface in mind, this can be expanded for
  * synchronous (nonblocking) methods as well.
+ *
+ * See also [[http://c2.com/cgi/wiki?FutureValue]]
  */
 
 public class Geary.Future : BaseObject {
diff --git a/src/engine/api/geary-search-folder.vala b/src/engine/api/geary-search-folder.vala
index c064a62..1beeb8f 100644
--- a/src/engine/api/geary-search-folder.vala
+++ b/src/engine/api/geary-search-folder.vala
@@ -380,12 +380,12 @@ public class Geary.SearchFolder : Geary.AbstractLocalFolder, Geary.FolderSupport
         return yield account.local_fetch_email_async(id, required_fields, cancellable);
     }
     
-    public virtual async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public virtual async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
         Gee.MultiMap<Geary.EmailIdentifier, Geary.FolderPath>? ids_to_folders
             = yield account.get_containing_folders_async(email_ids, cancellable);
         if (ids_to_folders == null)
-            return;
+            return null;
         
         Gee.MultiMap<Geary.FolderPath, Geary.EmailIdentifier> folders_to_ids
             = Geary.Collection.reverse_multi_map<Geary.EmailIdentifier, Geary.FolderPath>(ids_to_folders);
@@ -424,6 +424,8 @@ public class Geary.SearchFolder : Geary.AbstractLocalFolder, Geary.FolderSupport
                 }
             }
         }
+        
+        return null;
     }
     
     /**
diff --git a/src/engine/app/app-draft-manager.vala b/src/engine/app/app-draft-manager.vala
index 985f700..f52f7c7 100644
--- a/src/engine/app/app-draft-manager.vala
+++ b/src/engine/app/app-draft-manager.vala
@@ -423,7 +423,8 @@ public class Geary.App.DraftManager : BaseObject {
         if (current_draft_id != null && op.draft == null) {
             bool success = false;
             try {
-                yield remove_support.remove_single_email_async(current_draft_id);
+                yield remove_support.remove_email_async(
+                    iterate<EmailIdentifier>(current_draft_id).to_array_list());
                 success = true;
             } catch (Error err) {
                 debug("%s: Unable to remove existing draft %s: %s", to_string(), 
current_draft_id.to_string(),
diff --git a/src/engine/imap-db/outbox/smtp-outbox-folder.vala 
b/src/engine/imap-db/outbox/smtp-outbox-folder.vala
index 745967b..85a7991 100644
--- a/src/engine/imap-db/outbox/smtp-outbox-folder.vala
+++ b/src/engine/imap-db/outbox/smtp-outbox-folder.vala
@@ -552,11 +552,13 @@ private class Geary.SmtpOutboxFolder : Geary.AbstractLocalFolder, Geary.FolderSu
         notify_email_flags_changed(changed_map);
     }
     
-    public virtual async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public virtual async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
         check_open();
         
         yield internal_remove_email_async(email_ids, cancellable);
+        
+        return null;
     }
     
     // Like remove_email_async(), but can be called even when the folder isn't open
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-all-mail-folder.vala 
b/src/engine/imap-engine/gmail/imap-engine-gmail-all-mail-folder.vala
index ce46e75..3c42875 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-all-mail-folder.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-all-mail-folder.vala
@@ -14,8 +14,8 @@ private class Geary.ImapEngine.GmailAllMailFolder : MinimalFolder, FolderSupport
         base (account, remote, local, local_folder, special_folder_type);
     }
     
-    public async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
-        yield GmailFolder.true_remove_email_async(this, email_ids, cancellable);
+        return yield GmailFolder.true_remove_email_async(this, email_ids, cancellable);
     }
 }
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-drafts-folder.vala 
b/src/engine/imap-engine/gmail/imap-engine-gmail-drafts-folder.vala
index 57ea1df..4176f28 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-drafts-folder.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-drafts-folder.vala
@@ -22,8 +22,8 @@ private class Geary.ImapEngine.GmailDraftsFolder : MinimalFolder, FolderSupport.
         return yield base.create_email_async(rfc822, flags, date_received, id, cancellable);
     }
     
-    public async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
-        yield GmailFolder.true_remove_email_async(this, email_ids, cancellable);
+        return yield GmailFolder.true_remove_email_async(this, email_ids, cancellable);
     }
 }
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala 
b/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala
index 16e5529..dba3c42 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-folder.vala
@@ -17,14 +17,14 @@ private class Geary.ImapEngine.GmailFolder : MinimalFolder, FolderSupport.Archiv
         return yield base.create_email_async(rfc822, flags, date_received, id, cancellable);
     }
     
-    public async void archive_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public async Geary.Future? archive_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
-        yield expunge_email_async(email_ids, cancellable);
+        return yield expunge_email_async(email_ids, cancellable);
     }
     
-    public async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
-        yield true_remove_email_async(this, email_ids, cancellable);
+        return yield true_remove_email_async(this, email_ids, cancellable);
     }
     
     /**
@@ -36,7 +36,7 @@ private class Geary.ImapEngine.GmailFolder : MinimalFolder, FolderSupport.Archiv
      * no connection (or the connection dies) there's no record that Geary needs to perform the
      * final remove when a connection is reestablished.
      */
-    public static async void true_remove_email_async(MinimalFolder folder,
+    public static async Geary.Future? true_remove_email_async(MinimalFolder folder,
         Gee.List<Geary.EmailIdentifier> email_ids, Cancellable? cancellable) throws Error {
         // Get path to Trash folder
         Geary.Folder? trash = folder.account.get_special_folder(SpecialFolderType.TRASH);
@@ -45,14 +45,19 @@ private class Geary.ImapEngine.GmailFolder : MinimalFolder, FolderSupport.Archiv
         
         // Copy to Trash, collect UIDs (note that copying to Trash is like a move; the copied
         // messages are removed from all labels)
-        Gee.Set<Imap.UID>? uids = yield folder.copy_email_uids_async(email_ids, trash.path, cancellable);
+        Gee.Set<Imap.UID>? uids;
+        Geary.Future? future = yield folder.copy_email_uids_async(email_ids, trash.path, out uids,
+            cancellable);
+        
+        if (future != null)
+            uids = (Gee.Set<Imap.UID>?) yield future.wait_for_fulfillment_async(cancellable);
+        
         if (uids == null || uids.size == 0) {
             debug("%s: Can't true-remove %d emails, no COPYUIDs returned", folder.to_string(),
                 email_ids.size);
             
-            return;
+            return null;
         }
-        
         // For speed reasons, use a detached Imap.Folder object to delete moved emails; this is a
         // separate connection and is not synchronized with the database, but also avoids a full
         // folder normalization, which can be a heavyweight operation
@@ -73,6 +78,8 @@ private class Geary.ImapEngine.GmailFolder : MinimalFolder, FolderSupport.Archiv
         
         debug("%s: Successfully true-removed %d/%d emails", folder.to_string(), uids.size,
             email_ids.size);
+        
+        return null;
     }
 }
 
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-search-folder.vala 
b/src/engine/imap-engine/gmail/imap-engine-gmail-search-folder.vala
index 18aecee..caebbcd 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-search-folder.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-search-folder.vala
@@ -17,7 +17,7 @@ public class Geary.ImapEngine.GmailSearchFolder : Geary.SearchFolder {
         
     }
     
-    public override async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public override async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
         Geary.Folder? trash_folder = null;
         try {
@@ -34,5 +34,7 @@ public class Geary.ImapEngine.GmailSearchFolder : Geary.SearchFolder {
             // to fully trash the message.
             yield email_store.copy_email_async(email_ids, trash_folder.path, cancellable);
         }
+        
+        return null;
     }
 }
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala 
b/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala
index e9bb552..e82e269 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-spam-trash-folder.vala
@@ -16,13 +16,13 @@ private class Geary.ImapEngine.GmailSpamTrashFolder : MinimalFolder, FolderSuppo
         base (account, remote, local, local_folder, special_folder_type);
     }
     
-    public async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
-        yield expunge_email_async(email_ids, cancellable);
+        return yield expunge_email_async(email_ids, cancellable);
     }
     
-    public async void empty_folder_async(Cancellable? cancellable = null) throws Error {
-        yield expunge_all_async(cancellable);
+    public async Geary.Future? empty_folder_async(Cancellable? cancellable = null) throws Error {
+        return yield expunge_all_async(cancellable);
     }
 }
 
diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala 
b/src/engine/imap-engine/imap-engine-generic-folder.vala
index 2f234af..9cd77a0 100644
--- a/src/engine/imap-engine/imap-engine-generic-folder.vala
+++ b/src/engine/imap-engine/imap-engine-generic-folder.vala
@@ -11,18 +11,18 @@ private class Geary.ImapEngine.GenericFolder : MinimalFolder, Geary.FolderSuppor
         base (account, remote, local, local_folder, special_folder_type);
     }
     
-    public async void remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+    public async Geary.Future? remove_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
         Cancellable? cancellable = null) throws Error {
-        yield expunge_email_async(email_ids, cancellable);
+        return yield expunge_email_async(email_ids, cancellable);
     }
     
-    public async void empty_folder_async(Cancellable? cancellable = null) throws Error {
-        yield expunge_all_async(cancellable);
+    public async Geary.Future? empty_folder_async(Cancellable? cancellable = null) throws Error {
+        return yield expunge_all_async(cancellable);
     }
     
-    public new async Geary.EmailIdentifier? create_email_async(
-        RFC822.Message rfc822, Geary.EmailFlags? flags, DateTime? date_received,
-        Geary.EmailIdentifier? id, Cancellable? cancellable = null) throws Error {
+    public new async Geary.Future? create_email_async(RFC822.Message rfc822, Geary.EmailFlags? flags,
+        DateTime? date_received, Geary.EmailIdentifier? id, Cancellable? cancellable = null)
+        throws Error {
         return yield base.create_email_async(rfc822, flags, date_received, id, cancellable);
     }
 }
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala 
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index a86a327..7b3d5c8 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -206,6 +206,11 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         assert(local_earliest_id.has_uid());
         assert(local_latest_id.has_uid());
         
+        //
+        // TODO: Flush retry commands on the ReplayQueue (but not pending commands, as they may
+        // have come in naturally while waiting for open to complete)
+        //
+        
         // if any messages are still marked for removal from last time, that means the EXPUNGE
         // never arrived from the server, in which case the folder is "dirty" and needs a full
         // normalization
@@ -1224,8 +1229,8 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
     
     // Helper function for child classes dealing with the delete/archive question.  This method will
     // mark the message as deleted and expunge it.
-    protected async void expunge_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
-        Cancellable? cancellable = null) throws Error {
+    protected async Geary.Future? expunge_email_async(Gee.List<Geary.EmailIdentifier> email_ids,
+        Cancellable? cancellable) throws Error {
         check_open("expunge_email_async");
         check_ids("expunge_email_async", email_ids);
         
@@ -1233,16 +1238,16 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             cancellable);
         replay_queue.schedule(remove);
         
-        yield remove.wait_for_ready_async(cancellable);
+        return yield remove.wait_for_ready_async(cancellable);
     }
     
-    protected async void expunge_all_async(Cancellable? cancellable = null) throws Error {
+    protected async Geary.Future? expunge_all_async(Cancellable? cancellable = null) throws Error {
         check_open("expunge_all_async");
         
         EmptyFolder empty_folder = new EmptyFolder(this, cancellable);
         replay_queue.schedule(empty_folder);
         
-        yield empty_folder.wait_for_ready_async(cancellable);
+        return yield empty_folder.wait_for_ready_async(cancellable);
     }
     
     private void check_open(string method) throws EngineError {
@@ -1278,43 +1283,55 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         return yield mark.wait_for_ready_async(cancellable);
     }
     
-    public virtual async void copy_email_async(Gee.List<Geary.EmailIdentifier> to_copy,
+    public virtual async Geary.Future? copy_email_async(Gee.List<Geary.EmailIdentifier> to_copy,
         Geary.FolderPath destination, Cancellable? cancellable = null) throws Error {
-        yield copy_email_uids_async(to_copy, destination, cancellable);
+        return yield copy_email_uids_async(to_copy, destination, null, cancellable);
     }
     
     /**
      * Returns the destination folder's UIDs for the copied messages.
+     *
+     * If returned Future is non-null, must wait for Future to fulfill to get destination UIDs.
      */
-    public async Gee.Set<Imap.UID>? copy_email_uids_async(Gee.List<Geary.EmailIdentifier> to_copy,
-        Geary.FolderPath destination, Cancellable? cancellable = null) throws Error {
+    public async Geary.Future? copy_email_uids_async(Gee.List<Geary.EmailIdentifier> to_copy,
+        Geary.FolderPath destination, out Gee.Set<Imap.UID>? destination_uids,
+        Cancellable? cancellable = null) throws Error {
         check_open("copy_email_uids_async");
         check_ids("copy_email_uids_async", to_copy);
         
         // watch for copying to this folder, which is treated as a no-op
-        if (destination.equal_to(path))
+        if (destination.equal_to(path)) {
+            destination_uids = null;
+            
             return null;
+        }
         
         CopyEmail copy = new CopyEmail(this, (Gee.List<ImapDB.EmailIdentifier>) to_copy, destination);
         replay_queue.schedule(copy);
         
-        yield copy.wait_for_ready_async(cancellable);
+        Geary.Future? future = yield copy.wait_for_ready_async(cancellable);
+        
+        if (future != null)
+            destination_uids = copy.destination_uids.size > 0 ? copy.destination_uids : null;
+        else
+            destination_uids = null;
         
-        return copy.destination_uids.size > 0 ? copy.destination_uids : null;
+        return future;
     }
 
-    public virtual async void move_email_async(Gee.List<Geary.EmailIdentifier> to_move,
+    public virtual async Geary.Future? move_email_async(Gee.List<Geary.EmailIdentifier> to_move,
         Geary.FolderPath destination, Cancellable? cancellable = null) throws Error {
         check_open("move_email_async");
         check_ids("move_email_async", to_move);
         
         // watch for moving to this folder, which is treated as a no-op
         if (destination.equal_to(path))
-            return;
+            return null;
         
         MoveEmail move = new MoveEmail(this, (Gee.List<ImapDB.EmailIdentifier>) to_move, destination);
         replay_queue.schedule(move);
-        yield move.wait_for_ready_async(cancellable);
+        
+        return yield move.wait_for_ready_async(cancellable);
     }
     
     private void on_email_flags_changed(Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> changed) {
@@ -1372,21 +1389,21 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         return earliest_id;
     }
     
-    protected async Geary.EmailIdentifier? create_email_async(
-        RFC822.Message rfc822, Geary.EmailFlags? flags, DateTime? date_received,
-        Geary.EmailIdentifier? id, Cancellable? cancellable = null) throws Error {
+    protected async Geary.Future? create_email_async(RFC822.Message rfc822, Geary.EmailFlags? flags,
+        DateTime? date_received, Geary.EmailIdentifier? id, out Geary.EmailIdentifier? created_id,
+        Cancellable? cancellable = null) throws Error {
         check_open("create_email_async");
         if (id != null)
             check_id("create_email_async", id);
         
         Error? cancel_error = null;
-        Geary.EmailIdentifier? ret = null;
+        Geary.Future? future = null;
         try {
             CreateEmail create = new CreateEmail(this, rfc822, flags, date_received, cancellable);
             replay_queue.schedule(create);
-            yield create.wait_for_ready_async(cancellable);
             
-            ret = create.created_id;
+            future = yield create.wait_for_ready_async(cancellable);
+            ret = create.created_id ++
         } catch (Error e) {
             if (e is IOError.CANCELLED)
                 cancel_error = e;
@@ -1398,7 +1415,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         
         // Remove old message.
         if (id != null && remove_folder != null)
-            yield remove_folder.remove_single_email_async(id, null);
+            yield remove_folder.remove_email_async(iterate<EmailIdentifier>(id).to_array_list());
         
         // If the user cancelled the operation, throw the error here.
         if (cancel_error != null)
@@ -1407,7 +1424,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         // If the caller cancelled during the remove operation, delete the newly created message to
         // safely back out.
         if (cancellable != null && cancellable.is_cancelled() && ret != null && remove_folder != null)
-            yield remove_folder.remove_single_email_async(ret, null);
+            yield remove_folder.remove_email_async(iterate<EmailIdentifier>(ret).to_array_list());
         
         return ret;
     }
diff --git a/src/engine/imap-engine/imap-engine-replay-operation.vala 
b/src/engine/imap-engine/imap-engine-replay-operation.vala
index e5cf26b..0cba71c 100644
--- a/src/engine/imap-engine/imap-engine-replay-operation.vala
+++ b/src/engine/imap-engine/imap-engine-replay-operation.vala
@@ -29,10 +29,10 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject {
         CONTINUE
     }
     
-    [Flags]
-    public enum Retry {
-        NONE = 0,
-        REMOTE
+    public enum OnError {
+        THROW,
+        RETRY,
+        IGNORE
     }
     
     private static int next_opnum = 0;
@@ -40,19 +40,19 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject {
     public string name { get; set; }
     public int opnum { get; private set; }
     public Scope scope { get; private set; }
-    public Retry retry { get; protected set; }
-    public int retry_count { get; set; default = 0; }
+    public OnError on_remote_error { get; protected set; }
+    public int remote_retry_count { get; set; default = 0; }
     public Error? err { get; private set; default = null; }
     public bool notified { get { return semaphore.is_passed(); } }
     
     private Nonblocking.Semaphore semaphore = new Nonblocking.Semaphore();
     private Geary.Future? future = null;
     
-    public ReplayOperation(string name, Scope scope, Retry retry = Retry.NONE) {
+    public ReplayOperation(string name, Scope scope, OnError on_remote_error = OnError.THROW) {
         this.name = name;
         opnum = next_opnum++;
         this.scope = scope;
-        this.retry = retry;
+        this.on_remote_error = on_remote_error;
     }
     
     /**
@@ -135,6 +135,10 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject {
      * Completes when the operation has completed execution.  If the operation threw an error
      * during execution, it will be thrown here.
      *
+     * TODO: Always return a Future and require returning results to be fetched from the Future.
+     * Child classes must supply a fulfill_future() override method that does the right thing for
+     * the operation.  Or...they just know to do it.
+     *
      * Returns a Semaphore if the operation is retrying.  The Semaphore will be triggered when the
      * retry completes or errors out.
      */
diff --git a/src/engine/imap-engine/imap-engine-replay-queue.vala 
b/src/engine/imap-engine/imap-engine-replay-queue.vala
index f3c4ff4..7939396 100644
--- a/src/engine/imap-engine/imap-engine-replay-queue.vala
+++ b/src/engine/imap-engine/imap-engine-replay-queue.vala
@@ -12,7 +12,7 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
     private class CloseReplayQueue : ReplayOperation {
         public CloseReplayQueue() {
             // LOCAL_AND_REMOTE to make sure this operation is flushed all the way down the pipe
-            base ("CloseReplayQueue", ReplayOperation.Scope.LOCAL_AND_REMOTE, ReplayOperation.Retry.NONE);
+            base ("CloseReplayQueue", ReplayOperation.Scope.LOCAL_AND_REMOTE, OnError.IGNORE);
         }
         
         public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
@@ -50,10 +50,9 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
     } }
     
     private weak MinimalFolder owner;
-    private Nonblocking.Mailbox<ReplayOperation> local_queue = new Nonblocking.Mailbox<ReplayOperation>(
-        replay_operation_comparator);
+    private Nonblocking.Mailbox<ReplayOperation> local_queue = new Nonblocking.Mailbox<ReplayOperation>();
     private Nonblocking.Mailbox<ReplayOperation> remote_queue = new Nonblocking.Mailbox<ReplayOperation>(
-        replay_operation_comparator);
+        remote_replay_operation_comparator);
     private ReplayOperation? local_op_active = null;
     private ReplayOperation? remote_op_active = null;
     private Gee.ArrayList<ReplayOperation> notification_queue = new Gee.ArrayList<ReplayOperation>();
@@ -346,8 +345,8 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
         }
     }
     
-    private static int replay_operation_comparator(ReplayOperation a, ReplayOperation b) {
-        return a.retry_count - b.retry_count;
+    private static int remote_replay_operation_comparator(ReplayOperation a, ReplayOperation b) {
+        return a.remote_retry_count - b.remote_retry_count;
     }
     
     private async void do_replay_local_async() {
@@ -421,9 +420,6 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
                 }
             }
             
-            // completed one way or another, clear retry count for next round
-            op.retry_count = 0;
-            
             if (remote_enqueue) {
                 if (!remote_queue.send(op)) {
                     debug("Unable to enqueue operation %s for %s remote operation", op.to_string(),
@@ -492,34 +488,42 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
             
             Error? remote_err = null;
             if (folder_opened || is_close_op) {
-                if (op.retry_count > 0) {
-                    debug("RETRYING OP %s on %s: retry_count=%d", op.to_string(), to_string(),
-                        op.retry_count);
+                if (op.remote_retry_count > 0) {
+                    debug("Retrying op %s on %s: retry_count=%d", op.to_string(), to_string(),
+                        op.remote_retry_count);
                 }
                 
                 try {
                     yield op.replay_remote_async();
                 } catch (Error replay_err) {
-                    debug("Replay remote error for %s on %s: %s", op.to_string(), to_string(),
-                        replay_err.message);
+                    debug("Replay remote error for %s on %s: %s (%s)", op.to_string(), to_string(),
+                        replay_err.message, op.on_remote_error.to_string());
                     
                     // If a hard failure and operation allows remote replay, schedule now
-                    if (is_hard_failure(replay_err) && (op.retry & ReplayOperation.Retry.REMOTE) != 0) {
-                        debug("SCHEDULING RETRY OP %s on %s: retry_count=%d", op.to_string(),
-                            to_string(), op.retry_count);
+                    if ((op.on_remote_error == ReplayOperation.OnError.RETRY) && 
is_hard_failure(replay_err)) {
+                        debug("Schedule op retry %s on %s: remote_retry_count=%d", op.to_string(),
+                            to_string(), op.remote_retry_count);
                         
                         // if this is the first retry, complete the waiting async caller but allow
-                        // them to wait later or be signaled when completed
-                        if (op.retry_count == 0)
+                        // them to wait later or be signaled when completed ... this returns to the
+                        // original caller a Future they can use to be notified of final results
+                        if (op.remote_retry_count == 0)
                             op.notify_retrying();
                         
-                        op.retry_count++;
+                        // use the retry count to sort the command at the top of the queue and
+                        // take another go at it ... the Folder will disconnect and reconnect due
+                        // to the hard error and wait_for_open_async() will block this command until
+                        // reconnected and normalized
+                        op.remote_retry_count++;
                         remote_queue.send(op);
                         
                         continue;
+                    } else if (op.on_remote_error == ReplayOperation.OnError.IGNORE) {
+                        // ignoring error, simply notify as completed and continue
+                    } else {
+                        // store for notification
+                        remote_err = replay_err;
                     }
-                    
-                    remote_err = replay_err;
                 }
             } else if (!is_close_op) {
                 remote_err = new EngineError.SERVER_UNAVAILABLE("Folder %s not available", 
owner.to_string());
diff --git a/src/engine/imap-engine/imap-engine-send-replay-operation.vala 
b/src/engine/imap-engine/imap-engine-send-replay-operation.vala
index dcfc041..5ae0dce 100644
--- a/src/engine/imap-engine/imap-engine-send-replay-operation.vala
+++ b/src/engine/imap-engine/imap-engine-send-replay-operation.vala
@@ -5,12 +5,12 @@
  */
 
 private abstract class Geary.ImapEngine.SendReplayOperation : Geary.ImapEngine.ReplayOperation {
-    public SendReplayOperation(string name, ReplayOperation.Retry retry = ReplayOperation.Retry.NONE) {
-        base (name, ReplayOperation.Scope.LOCAL_AND_REMOTE, retry);
+    public SendReplayOperation(string name, ReplayOperation.OnError on_remote_error = OnError.THROW) {
+        base (name, ReplayOperation.Scope.LOCAL_AND_REMOTE, on_remote_error);
     }
     
-    public SendReplayOperation.only_remote(string name, ReplayOperation.Retry retry = 
ReplayOperation.Retry.NONE) {
-        base (name, ReplayOperation.Scope.REMOTE_ONLY, retry);
+    public SendReplayOperation.only_remote(string name, ReplayOperation.OnError on_remote_error = 
OnError.THROW) {
+        base (name, ReplayOperation.Scope.REMOTE_ONLY, on_remote_error);
     }
     
     public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
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..15243a7 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
@@ -66,7 +66,7 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
     
     public AbstractListEmail(string name, MinimalFolder owner, Geary.Email.Field required_fields,
         Folder.ListFlags flags, Cancellable? cancellable) {
-        base(name);
+        base(name, OnError.IGNORE);
         
         this.owner = owner;
         this.required_fields = required_fields;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
index f054780..703a0c3 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-copy-email.vala
@@ -14,7 +14,7 @@ private class Geary.ImapEngine.CopyEmail : Geary.ImapEngine.SendReplayOperation
 
     public CopyEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_copy, 
         Geary.FolderPath destination, Cancellable? cancellable = null) {
-        base("CopyEmail");
+        base("CopyEmail", OnError.RETRY);
         
         this.engine = engine;
         
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
index 3750ff0..bb8974b 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
@@ -15,7 +15,7 @@ private class Geary.ImapEngine.CreateEmail : Geary.ImapEngine.SendReplayOperatio
     
     public CreateEmail(MinimalFolder engine, RFC822.Message rfc822, Geary.EmailFlags? flags,
         DateTime? date_received, Cancellable? cancellable) {
-        base.only_remote("CreateEmail");
+        base.only_remote("CreateEmail", OnError.RETRY);
         
         this.engine = engine;
         
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
index 1cf774a..05aedb4 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-empty-folder.vala
@@ -16,7 +16,7 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
     private int original_count = 0;
     
     public EmptyFolder(MinimalFolder engine, Cancellable? cancellable) {
-        base("EmptyFolder");
+        base("EmptyFolder", OnError.RETRY);
         
         this.engine = engine;
         this.cancellable = cancellable;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
index a6090fd..604af6b 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
@@ -18,7 +18,7 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
     
     public FetchEmail(MinimalFolder engine, ImapDB.EmailIdentifier id, Email.Field required_fields,
         Folder.ListFlags flags, Cancellable? cancellable) {
-        base ("FetchEmail");
+        base ("FetchEmail", OnError.IGNORE);
         
         this.engine = engine;
         this.id = id;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
index f7c6069..aa3c019 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-mark-email.vala
@@ -15,7 +15,7 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
     public MarkEmail(MinimalFolder engine, Gee.List<Geary.EmailIdentifier> to_mark, 
         Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, 
         Cancellable? cancellable = null) {
-        base("MarkEmail", Retry.REMOTE);
+        base("MarkEmail", OnError.RETRY);
         
         this.engine = engine;
         
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
index 6be265d..45a1a60 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
@@ -14,7 +14,7 @@ private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation
 
     public MoveEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_move, 
         Geary.FolderPath destination, Cancellable? cancellable = null) {
-        base("MoveEmail");
+        base("MoveEmail", OnError.RETRY);
 
         this.engine = engine;
 
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
index 95cbed8..979c6f9 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-remove-email.vala
@@ -13,7 +13,7 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
     
     public RemoveEmail(MinimalFolder engine, Gee.List<ImapDB.EmailIdentifier> to_remove,
         Cancellable? cancellable = null) {
-        base("RemoveEmail");
+        base("RemoveEmail", OnError.RETRY);
         
         this.engine = engine;
         
@@ -57,6 +57,9 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
     }
     
     public override async ReplayOperation.Status replay_remote_async() throws Error {
+        if (removed_ids.size == 0)
+            return ReplayOperation.Status.COMPLETED;
+        
         // Remove from server. Note that this causes the receive replay queue to kick into
         // action, removing the e-mail but *NOT* firing a signal; the "remove marker" indicates
         // that the signal has already been fired.
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
index 6d561be..1950058 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-replay-append.vala
@@ -10,7 +10,9 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
     public Gee.List<Imap.SequenceNumber> positions;
     
     public ReplayAppend(MinimalFolder owner, int remote_count, Gee.List<Imap.SequenceNumber> positions) {
-        base ("Append", Scope.REMOTE_ONLY);
+        // IGNORE remote errors because the reconnect will re-normalize the folder, making this
+        // append moot
+        base ("Append", Scope.REMOTE_ONLY, OnError.IGNORE);
         
         this.owner = owner;
         this.remote_count = remote_count;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
index eeea5e2..5cacc8f 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
@@ -10,7 +10,8 @@ private class Geary.ImapEngine.ReplayRemoval : Geary.ImapEngine.ReplayOperation
     public Imap.SequenceNumber position;
     
     public ReplayRemoval(MinimalFolder owner, int remote_count, Imap.SequenceNumber position) {
-        base ("Removal", Scope.LOCAL_AND_REMOTE);
+        // remote error will cause folder to reconnect and re-normalize, making this remove moot
+        base ("Removal", Scope.LOCAL_AND_REMOTE, OnError.IGNORE);
         
         this.owner = owner;
         this.remote_count = remote_count;
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..b8d6f5d 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
@@ -19,6 +19,9 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
         base ("ServerSearchEmail", owner, required_fields, Geary.Folder.ListFlags.OLDEST_TO_NEWEST,
             cancellable);
         
+        // unlike list, need to retry this as there's no local component to return
+        on_remote_error = OnError.RETRY;
+        
         this.criteria = criteria;
     }
     


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