[geary/wip/improve-claiming-folder-session: 3/9] Tidy up ReplayOperation API



commit 7b4f30c41b5bced6de32f1f4ad36b6204ae2d8c9
Author: Michael Gratton <mike vee net>
Date:   Sun Nov 11 07:52:57 2018 +1100

    Tidy up ReplayOperation API
    
    Make most abstract methods virtual and provide appropriate no-op impls
    to simplify subclasses. Add a FolderSession arg to replay local_async
    since in 90% of the cases the operations needs one and since this makes
    the queue itself the only remaining user of
    MinimalFolder.claim_remote_session async, pending its removal.
    
    Also tidyied up ListEmailById so there is no chance of the local op
    executing a remote call.

 .../imap-engine/imap-engine-replay-operation.vala  | 57 ++++++++++++----
 .../imap-engine/imap-engine-replay-queue.vala      | 50 +++++++--------
 .../imap-engine-abstract-list-email.vala           | 63 ++++--------------
 .../replay-ops/imap-engine-copy-email.vala         | 46 ++++++-------
 .../replay-ops/imap-engine-create-email.vala       | 58 +++++++----------
 .../replay-ops/imap-engine-empty-folder.vala       | 11 +---
 .../replay-ops/imap-engine-fetch-email.vala        | 22 ++-----
 .../replay-ops/imap-engine-list-email-by-id.vala   | 75 +++++++++++++---------
 .../replay-ops/imap-engine-mark-email.vala         | 32 ++++-----
 .../replay-ops/imap-engine-move-email-commit.vala  | 70 +++++++++-----------
 .../replay-ops/imap-engine-move-email-prepare.vala | 13 +---
 .../replay-ops/imap-engine-move-email-revoke.vala  | 13 +---
 .../replay-ops/imap-engine-remove-email.vala       | 25 ++++----
 .../replay-ops/imap-engine-replay-append.vala      | 28 ++------
 .../replay-ops/imap-engine-replay-removal.vala     |  7 +-
 .../replay-ops/imap-engine-replay-update.vala      | 16 -----
 .../imap-engine-server-search-email.vala           | 29 +++++----
 .../replay-ops/imap-engine-user-close.vala         | 28 +++-----
 18 files changed, 264 insertions(+), 379 deletions(-)
---
diff --git a/src/engine/imap-engine/imap-engine-replay-operation.vala 
b/src/engine/imap-engine/imap-engine-replay-operation.vala
index 9cecd2b0..8047e389 100644
--- a/src/engine/imap-engine/imap-engine-replay-operation.vala
+++ b/src/engine/imap-engine/imap-engine-replay-operation.vala
@@ -60,7 +60,7 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
         this.scope = scope;
         this.on_remote_error = on_remote_error;
     }
-    
+
     /**
      * Notify the operation that a message has been removed by position (SequenceNumber).
      *
@@ -72,8 +72,10 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
      *
      * This won't be called while replay_local_async() or replay_remote_async() are executing.
      */
-    public abstract void notify_remote_removed_position(Imap.SequenceNumber removed);
-    
+    public virtual void notify_remote_removed_position(Imap.SequenceNumber removed) {
+        // noop
+    }
+
     /**
      * Notify the operation that a message has been removed by UID (EmailIdentifier).
      *
@@ -90,8 +92,10 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
      *
      * This won't be called while replay_local_async() or replay_remote_async() are executing.
      */
-    public abstract void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids);
-    
+    public virtual void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
+        // noop
+    }
+
     /**
      * Add to the Collection EmailIdentifiers that will be removed in replay_remote_async().
      *
@@ -104,12 +108,16 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
      * invocation (i.e. the Folder closed before the server could notify the engine that they were
      * removed).
      */
-    public abstract void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids);
-    
+    public virtual void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
+        // noop
+    }
+
     /**
+     * Executes the local parts of this operation, if any.
+     *
      * See Scope for conditions where this method will be called.
      *
-     * If an error is thrown, {@link backout_local_async} will will
+     * If an error is thrown, {@link backout_local_async} will
      * *not* be executed.
      *
      * @return {@link Status.COMPLETED} if the operation has completed
@@ -118,11 +126,22 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
      * remote portion must be executed as well. This is treated as
      * `COMPLETED` if get_scope() returns {@link Scope.LOCAL_ONLY}.
      */
-    public abstract async Status replay_local_async() throws Error;
+    public virtual async Status replay_local_async()
+        throws GLib.Error {
+        if (this.scope != Scope.REMOTE_ONLY) {
+            throw new GLib.IOError.NOT_SUPPORTED("Local operation is not implemented");
+        }
+        return (this.scope == Scope.LOCAL_ONLY)
+            ? Status.COMPLETED : Status.CONTINUE;
+    }
 
     /**
+     * Executes the remote parts of this operation, if any.
+     *
      * See Scope for conditions where this method will be called.
      *
+     * Passed a folder session with the current folder selected.
+     *
      * If an error is thrown, {@link backout_local_async} will be
      * executed only if scope is LOCAL_AND_REMOTE.
      *
@@ -131,14 +150,24 @@ private abstract class Geary.ImapEngine.ReplayOperation : Geary.BaseObject, Gee.
      * Status.CONTINUE} if treated as `COMPLETED`.
      *
      */
-    public abstract async Status replay_remote_async() throws Error;
+    public virtual async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
+        if (this.scope != Scope.LOCAL_ONLY) {
+            throw new GLib.IOError.NOT_SUPPORTED("Remote operation is not implemented");
+        }
+    }
 
     /**
-     * See Scope, replay_local_async(), and replay_remote_async() for conditions for this where this
-     * will be called.
+     * Reverts any local effects of this operation.
+     *
+     * See {@link Scope}, {@link replay_local_async}, and {@link
+     * replay_remote_async} for conditions for this where this will be
+     * called.
      */
-    public abstract async void backout_local_async() throws Error;
-    
+    public virtual async void backout_local_async() throws Error {
+        // noop
+    }
+
     /**
      * Completes when the operation has completed execution.  If the operation threw an error
      * during execution, it will be thrown here.
diff --git a/src/engine/imap-engine/imap-engine-replay-queue.vala 
b/src/engine/imap-engine/imap-engine-replay-queue.vala
index 6457ef79..b565971d 100644
--- a/src/engine/imap-engine/imap-engine-replay-queue.vala
+++ b/src/engine/imap-engine/imap-engine-replay-queue.vala
@@ -21,36 +21,32 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
         CLOSING,
         CLOSED
     }
-    
+
     private class CloseReplayQueue : ReplayOperation {
+
+        bool local_closed = false;
+        bool remote_closed = false;
+
         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, OnError.IGNORE);
         }
-        
-        public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
-        }
-        
-        public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-        }
-        
-        public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-        }
-        
-        public override async ReplayOperation.Status replay_local_async() throws Error {
+
+        public override async ReplayOperation.Status replay_local_async()
+            throws GLib.Error {
+            this.local_closed = true;
             return Status.CONTINUE;
         }
-        
-        public override async ReplayOperation.Status replay_remote_async() throws Error {
-            return Status.COMPLETED;
-        }
-        
-        public override async void backout_local_async() throws Error {
-            // nothing to backout (and should never be called, to boot)
+
+        public override async void replay_remote_async(Imap.FolderSession remote)
+            throws GLib.Error {
+            this.remote_closed = true;
         }
-        
+
         public override string describe_state() {
-            return "";
+            return "local_closed: %s, remote_closed: %s".printf(
+                this.local_closed.to_string(), this.remote_closed.to_string()
+            );
         }
     }
     
@@ -492,9 +488,13 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
                 queue_running = false;
             
             // wait until the remote folder is opened (or throws an exception, in which case closed)
+            Imap.FolderSession? remote = null;
             try {
-                if (!is_close_op && folder_opened && state == State.OPEN)
-                    yield owner.claim_remote_session(this.remote_wait_cancellable);
+                if (!is_close_op && folder_opened && state == State.OPEN) {
+                    remote = yield owner.claim_remote_session(
+                        this.remote_wait_cancellable
+                    );
+                }
             } catch (Error remote_err) {
                 debug("Folder %s closed or failed to open, remote replay queue closing: %s",
                       to_string(), remote_err.message);
@@ -511,9 +511,9 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
             if (folder_opened || is_close_op) {
                 if (op.remote_retry_count > 0)
                     debug("Retrying op %s on %s", op.to_string(), to_string());
-                
+
                 try {
-                    yield op.replay_remote_async();
+                    yield op.replay_remote_async(remote);
                 } catch (Error replay_err) {
                     debug("Replay remote error for %s on %s: %s (%s)", op.to_string(), to_string(),
                         replay_err.message, op.on_remote_error.to_string());
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 11974947..d58a232a 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
@@ -127,16 +127,14 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
                 unfulfilled.unset(id.uid);
         }
     }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
+
     // Child class should execute its own calls *before* calling this base method
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
         // only deal with unfulfilled email, child class must deal with everything else
         if (unfulfilled.size == 0)
-            return ReplayOperation.Status.COMPLETED;
-        
+           return;
+
         // since list and search commands ahead of this one in the queue may have fulfilled some of
         // the emails thought to be unfulfilled when first checked locally, look for them now
         int fetches_avoided = yield remove_fulfilled_uids_async();
@@ -145,12 +143,12 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
             
             debug("[%s] %d previously-fulfilled fetches avoided in list operation, %d total",
                 owner.to_string(), fetches_avoided, total_fetches_avoided);
-            
+
             // if all fulfilled, emails were added to accumulator in remove call, so done
             if (unfulfilled.size == 0)
-                return ReplayOperation.Status.COMPLETED;
+                return;
         }
-        
+
         // convert UID -> needed fields mapping to needed fields -> UIDs, as they can be grouped
         // and submitted at same time
         Gee.HashMultiMap<Geary.Email.Field, Imap.UID> reverse_unfulfilled = new Gee.HashMultiMap<
@@ -158,9 +156,6 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
         foreach (Imap.UID uid in unfulfilled.keys)
             reverse_unfulfilled.set(unfulfilled.get(uid), uid);
 
-        Imap.FolderSession remote =
-            yield this.owner.claim_remote_session(cancellable);
-
         // schedule operations to remote for each set of email with unfulfilled fields and merge
         // in results, pulling out the entire email
         Nonblocking.Batch batch = new Nonblocking.Batch();
@@ -206,34 +201,6 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
             owner.replay_notify_email_inserted(created_ids);
             owner.replay_notify_email_locally_inserted(created_ids);
         }
-        
-        return ReplayOperation.Status.COMPLETED;
-    }
-
-    /**
-     * Determines if the owning folder's vector is fully expanded.
-     */
-    protected async Trillian is_fully_expanded_async() throws Error {
-        Trillian is_fully_expanded = Trillian.UNKNOWN;
-        if (this.owner.account.is_online) {
-            Imap.FolderSession remote =
-                yield this.owner.claim_remote_session(this.cancellable);
-            int remote_count = remote.folder.properties.email_total;
-
-            // include marked for removed in the count in case this is
-            // being called while a removal is in process, in which
-            // case don't want to expand vector this moment because
-            // the vector is in flux
-            int local_count_with_marked =
-                yield owner.local_folder.get_email_count_async(
-                    ImapDB.Folder.ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable
-                );
-
-            is_fully_expanded = Trillian.from_boolean(
-                local_count_with_marked >= remote_count
-            );
-        }
-        return is_fully_expanded;
     }
 
     /**
@@ -250,10 +217,11 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
      * be examined and added to the messages to be fulfilled if
      * needed.
      */
-    protected async Gee.Set<Imap.UID>? expand_vector_async(Imap.UID? initial_uid, int count) throws Error {
+    protected async Gee.Set<Imap.UID>? expand_vector_async(Imap.FolderSession remote,
+                                                           Imap.UID? initial_uid,
+                                                           int count)
+        throws GLib.Error {
         debug("%s: expanding vector...", owner.to_string());
-        Imap.FolderSession remote =
-            yield this.owner.claim_remote_session(cancellable);
         int remote_count = remote.folder.properties.email_total;
 
         // include marked for removed in the count in case this is being called while a removal
@@ -401,14 +369,9 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
         
         return fetch_avoided;
     }
-    
-    public override async void backout_local_async() throws Error {
-        // R/O, no backout
-    }
-    
+
     public override string describe_state() {
         return "required_fields=%Xh local_only=%s force_update=%s".printf(required_fields,
             flags.is_local_only().to_string(), flags.is_force_update().to_string());
     }
 }
-
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 8049714d..b5140525 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
@@ -26,10 +26,7 @@ private class Geary.ImapEngine.CopyEmail : Geary.ImapEngine.SendReplayOperation
     public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
         to_copy.remove_all(ids);
     }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
+
     public override async ReplayOperation.Status replay_local_async() throws Error {
         if (to_copy.size == 0)
             return ReplayOperation.Status.COMPLETED;
@@ -38,35 +35,30 @@ private class Geary.ImapEngine.CopyEmail : Geary.ImapEngine.SendReplayOperation
         // existing there.
         return ReplayOperation.Status.CONTINUE;
     }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
-        if (to_copy.size == 0)
-            return ReplayOperation.Status.COMPLETED;
-        
-        Gee.Set<Imap.UID>? uids = yield engine.local_folder.get_uids_async(to_copy,
-            ImapDB.Folder.ListFlags.NONE, cancellable);
 
-        if (uids != null && uids.size > 0) {
-            Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(uids);
-            Imap.FolderSession remote =
-                yield this.engine.claim_remote_session(cancellable);
-            foreach (Imap.MessageSet msg_set in msg_sets) {
-                Gee.Map<Imap.UID, Imap.UID>? src_dst_uids =
-                    yield remote.copy_email_async(msg_set, destination, cancellable);
-                if (src_dst_uids != null)
-                    destination_uids.add_all(src_dst_uids.values);
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
+        if (to_copy.size > 0) {
+            Gee.Set<Imap.UID>? uids = yield engine.local_folder.get_uids_async(
+                to_copy, ImapDB.Folder.ListFlags.NONE, cancellable
+            );
+
+            if (uids != null && uids.size > 0) {
+                Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(uids);
+                foreach (Imap.MessageSet msg_set in msg_sets) {
+                    Gee.Map<Imap.UID, Imap.UID>? src_dst_uids =
+                        yield remote.copy_email_async(
+                            msg_set, destination, cancellable
+                        );
+                    if (src_dst_uids != null)
+                        destination_uids.add_all(src_dst_uids.values);
+                }
             }
         }
-
-        return ReplayOperation.Status.COMPLETED;
-    }
-
-    public override async void backout_local_async() throws Error {
-        // Nothing to undo.
     }
 
     public override string describe_state() {
         return "%d email IDs to %s".printf(to_copy.size, destination.to_string());
     }
-}
 
+}
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 3a4ea8a8..9cd5a89a 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
@@ -24,32 +24,13 @@ private class Geary.ImapEngine.CreateEmail : Geary.ImapEngine.SendReplayOperatio
         this.date_received = date_received;
         this.cancellable = cancellable;
     }
-    
-    public override async ReplayOperation.Status replay_local_async() throws Error {
-        return ReplayOperation.Status.CONTINUE;
-    }
-    
-    public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
-    public override async void backout_local_async() throws Error {
-    }
-    
-    public override string describe_state() {
-        return "";
-    }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
+
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
         // Deal with cancellable manually since create_email_async cannot be cancelled.
         if (cancellable.is_cancelled())
             throw new IOError.CANCELLED("CreateEmail op cancelled immediately");
 
-        Imap.FolderSession remote =
-            yield this.engine.claim_remote_session(cancellable);
-
         // use IMAP APPEND command on remote folders, which doesn't require opening a folder ...
         // if retrying after a successful create, rfc822 will be null
         if (rfc822 != null)
@@ -71,19 +52,24 @@ private class Geary.ImapEngine.CreateEmail : Geary.ImapEngine.SendReplayOperatio
 
             throw new IOError.CANCELLED("CreateEmail op cancelled after create");
         }
-        
-        if (created_id == null)
-            return ReplayOperation.Status.COMPLETED;
-        
-        // TODO: need to prevent gaps that may occur here
-        Geary.Email created = new Geary.Email(created_id);
-        Gee.Map<Geary.Email, bool> results = yield engine.local_folder.create_or_merge_email_async(
-            Geary.iterate<Geary.Email>(created).to_array_list(), cancellable);
-        if (results.size > 0)
-            created_id = Collection.get_first<Geary.Email>(results.keys).id;
-        else
-            created_id = null;
-        
-        return ReplayOperation.Status.COMPLETED;
+
+        if (created_id != null) {
+            // TODO: need to prevent gaps that may occur here
+            Geary.Email created = new Geary.Email(created_id);
+            Gee.Map<Geary.Email, bool> results = yield engine.local_folder.create_or_merge_email_async(
+                Geary.iterate<Geary.Email>(created).to_array_list(), cancellable);
+            if (results.size > 0) {
+                created_id = Collection.get_first<Geary.Email>(results.keys).id;
+            } else {
+                created_id = null;
+            }
+        }
+    }
+
+    public override string describe_state() {
+        return "created_id: %s".printf(
+            this.created_id != null ? this.created_id.to_string() :  "none"
+        );
     }
+
 }
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 e2133dfb..8a045ecc 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
@@ -21,9 +21,6 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
         this.engine = engine;
         this.cancellable = cancellable;
     }
-    
-    public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
 
     public override async ReplayOperation.Status replay_local_async() throws Error {
         this.original_count = this.engine.properties.email_total;
@@ -52,16 +49,12 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
             ids.add_all(removed_ids);
     }
 
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
         // STORE and EXPUNGE using positional addressing: "1:*"
-        Imap.FolderSession remote =
-            yield this.engine.claim_remote_session(cancellable);
         Imap.MessageSet msg_set = new Imap.MessageSet.range_to_highest(
             new Imap.SequenceNumber(Imap.SequenceNumber.MIN));
-
         yield remote.remove_email_async(msg_set.to_list(), cancellable);
-
-        return ReplayOperation.Status.COMPLETED;
     }
 
     public override async void backout_local_async() throws Error {
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 a6da5c01..22ae0949 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
@@ -38,10 +38,7 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
     public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
         remote_removed = ids.contains(id);
     }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
+
     public override async ReplayOperation.Status replay_local_async() throws Error {
         // If forcing an update, skip local operation and go direct to replay_remote()
         if (flags.is_all_set(Folder.ListFlags.FORCE_UPDATE))
@@ -84,16 +81,14 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
         
         return ReplayOperation.Status.CONTINUE;
     }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
+
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
         if (remote_removed) {
             throw new EngineError.NOT_FOUND("Unable to fetch %s in %s (removed from remote)",
                 id.to_string(), engine.to_string());
         }
 
-        Imap.FolderSession remote =
-            yield this.engine.claim_remote_session(cancellable);
-
         // fetch only the remaining fields from the remote folder (if only pulling partial information,
         // will merge at end of this method)
         Gee.List<Geary.Email>? list = yield remote.list_email_async(
@@ -124,17 +119,10 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
                 ImapDB.Folder.ListFlags.NONE, cancellable);
             assert(email != null);
         }
-        
-        return ReplayOperation.Status.COMPLETED;
-    }
-    
-    public override async void backout_local_async() throws Error {
-        // read-only
     }
-    
+
     public override string describe_state() {
         return "id=%s required_fields=%Xh remaining_fields=%Xh flags=%Xh".printf(id.to_string(),
             required_fields, remaining_fields, flags);
     }
 }
-
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 703e0cc6..22987fb7 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
@@ -81,31 +81,27 @@ private class Geary.ImapEngine.ListEmailByID : Geary.ImapEngine.AbstractListEmai
             // an initial_id
             finished = (get_unfulfilled_count() == 0 && fulfilled_count >= count);
         } else {
-            // count == int.MAX
-            // This sentinel means "get everything from this point", so this has different meanings
-            // depending on direction
-            if (flags.is_newest_to_oldest()) {
-                // only finished if the folder is entirely normalized
-                Trillian is_fully_expanded = yield is_fully_expanded_async();
-                finished = (is_fully_expanded == Trillian.TRUE);
-            } else {
-                // for oldest-to-newest, finished if no unfulfilled items
-                finished = (get_unfulfilled_count() == 0);
-            }
+            // Here, count == int.MAX, but this sentinel means "get
+            // everything from this point", so this has different
+            // meanings depending on direction. If
+            // flags.is_newest_to_oldest(), only finished if the
+            // folder is entirely normalized, but we don't know here
+            // since we don't have a remote. Else for
+            // oldest-to-newest, finished if no unfulfilled items
+            finished = (
+                !flags.is_newest_to_oldest() && get_unfulfilled_count() == 0
+            );
         }
-        
-        // local-only operations stop here; also, since the local store is normalized from the top
-        // of the vector on down, if enough items came back fulfilled, then done
-        if (finished)
-            return ReplayOperation.Status.COMPLETED;
-        
-        return ReplayOperation.Status.CONTINUE;
+
+        return finished
+            ? ReplayOperation.Status.COMPLETED
+            : ReplayOperation.Status.CONTINUE;
     }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
+
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
         bool expansion_required = false;
-        Trillian is_fully_expanded = yield is_fully_expanded_async();
-        if (is_fully_expanded == Trillian.FALSE) {
+        if (!(yield is_fully_expanded_async(remote))) {
             if (flags.is_oldest_to_newest()) {
                 if (initial_id != null) {
                     // expand vector if not initial_id not discovered
@@ -130,27 +126,48 @@ private class Geary.ImapEngine.ListEmailByID : Geary.ImapEngine.AbstractListEmai
                 }
             }
         }
-        
+
         // If the vector is too short, expand it now
         if (expansion_required) {
-            Gee.Set<Imap.UID>? uids = yield expand_vector_async(initial_uid, count);
+            Gee.Set<Imap.UID>? uids = yield expand_vector_async(
+                remote, initial_uid, count
+            );
             if (uids != null) {
                 // add required_fields as well as basic required fields for new email
                 add_many_unfulfilled_fields(uids, required_fields);
             }
         }
-        
+
         // Even after expansion it's possible for the local_list_count + unfulfilled to be less
         // than count if the folder has fewer messages or the user is requesting a span near
         // either end of the vector, so don't do that kind of sanity checking here
-        
-        return yield base.replay_remote_async();
+
+        yield base.replay_remote_async(remote);
     }
-    
+
     public override string describe_state() {
         return "%s initial_id=%s count=%u incl=%s newest_to_oldest=%s".printf(base.describe_state(),
             (initial_id != null) ? initial_id.to_string() : "(null)", count,
             flags.is_including_id().to_string(), flags.is_newest_to_oldest().to_string());
     }
-}
 
+    /**
+     * Determines if the owning folder's vector is fully expanded.
+     */
+    private async bool is_fully_expanded_async(Imap.FolderSession remote)
+        throws GLib.Error {
+        int remote_count = remote.folder.properties.email_total;
+
+        // include marked for removed in the count in case this is
+        // being called while a removal is in process, in which case
+        // don't want to expand vector this moment because the vector
+        // is in flux
+        int local_count_with_marked =
+            yield this.owner.local_folder.get_email_count_async(
+                ImapDB.Folder.ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable
+            );
+
+        return local_count_with_marked >= remote_count;
+    }
+
+}
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 53686711..2330ecc6 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
@@ -30,10 +30,7 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
         if (original_flags != null)
             Collection.map_unset_all_keys<EmailIdentifier, Geary.EmailFlags>(original_flags, ids);
     }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
+
     public override async ReplayOperation.Status replay_local_async() throws Error {
         if (to_mark.size == 0)
             return ReplayOperation.Status.COMPLETED;
@@ -57,24 +54,19 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
         
         return ReplayOperation.Status.CONTINUE;
     }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
-        // potentially empty due to writebehind operation
-        if (original_flags.size == 0)
-            return ReplayOperation.Status.COMPLETED;
-
-        Imap.FolderSession remote =
-            yield this.engine.claim_remote_session(cancellable);
-
-        Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
-            ImapDB.EmailIdentifier.to_uids(original_flags.keys));
-        yield remote.mark_email_async(
-            msg_sets, flags_to_add, flags_to_remove, cancellable
-        );
 
-        return ReplayOperation.Status.COMPLETED;
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
+        // potentially empty due to writebehind operation
+        if (original_flags.size > 0) {
+            Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
+                ImapDB.EmailIdentifier.to_uids(original_flags.keys));
+            yield remote.mark_email_async(
+                msg_sets, flags_to_add, flags_to_remove, cancellable
+            );
+        }
     }
-    
+
     public override async void backout_local_async() throws Error {
         // Restore original flags (if fetched, which may not have occurred if an error happened
         // during transaction)
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
index 651a8de7..68420cc5 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
@@ -31,54 +31,47 @@ private class Geary.ImapEngine.MoveEmailCommit : Geary.ImapEngine.SendReplayOper
     public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
         to_move.remove_all(ids);
     }
-    
-    public override async ReplayOperation.Status replay_local_async() throws Error {
-        return ReplayOperation.Status.CONTINUE;
-    }
-    
+
     public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
         ids.add_all(to_move);
     }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
-        if (to_move.size == 0)
-            return ReplayOperation.Status.COMPLETED;
-        
-        // Remaining MessageSets are persisted in case of network retries
-        if (remaining_msg_sets == null)
-            remaining_msg_sets = Imap.MessageSet.uid_sparse(ImapDB.EmailIdentifier.to_uids(to_move));
-        
-        if (remaining_msg_sets == null || remaining_msg_sets.size == 0)
-            return ReplayOperation.Status.COMPLETED;
 
-        Imap.FolderSession remote =
-            yield this.engine.claim_remote_session(cancellable);
-
-        Gee.Iterator<Imap.MessageSet> iter = remaining_msg_sets.iterator();
-        while (iter.next()) {
-            // don't use Cancellable throughout I/O operations in order to assure transaction completes
-            // fully
-            if (cancellable != null && cancellable.is_cancelled()) {
-                throw new IOError.CANCELLED(
-                    "Move email to %s cancelled", this.destination.to_string()
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
+        if (to_move.size > 0) {
+            // Remaining MessageSets are persisted in case of network retries
+            if (remaining_msg_sets == null)
+                remaining_msg_sets = Imap.MessageSet.uid_sparse(
+                    ImapDB.EmailIdentifier.to_uids(to_move)
                 );
-            }
 
-            Imap.MessageSet msg_set = iter.get();
+            if (remaining_msg_sets == null || remaining_msg_sets.size == 0)
+                return;
 
-            Gee.Map<Imap.UID, Imap.UID>? map = yield remote.copy_email_async(
-                msg_set, destination, null
-            );
-            if (map != null)
-                destination_uids.add_all(map.values);
+            Gee.Iterator<Imap.MessageSet> iter = remaining_msg_sets.iterator();
+            while (iter.next()) {
+                // don't use Cancellable throughout I/O operations in
+                // order to assure transaction completes fully
+                if (cancellable != null && cancellable.is_cancelled()) {
+                    throw new IOError.CANCELLED(
+                        "Move email to %s cancelled", this.destination.to_string()
+                    );
+                }
 
-            yield remote.remove_email_async(msg_set.to_list(), null);
+                Imap.MessageSet msg_set = iter.get();
 
-            // completed successfully, remove from list in case of retry
-            iter.remove();
-        }
+                Gee.Map<Imap.UID, Imap.UID>? map = yield remote.copy_email_async(
+                    msg_set, destination, null
+                );
+                if (map != null)
+                    destination_uids.add_all(map.values);
 
-        return ReplayOperation.Status.COMPLETED;
+                yield remote.remove_email_async(msg_set.to_list(), null);
+
+                // completed successfully, remove from list in case of retry
+                iter.remove();
+            }
+        }
     }
 
     public override async void backout_local_async() throws Error {
@@ -99,4 +92,3 @@ private class Geary.ImapEngine.MoveEmailCommit : Geary.ImapEngine.SendReplayOper
         return "%d email IDs to %s".printf(to_move.size, destination.to_string());
     }
 }
-
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
index a6d79428..b0efd5d9 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
@@ -54,19 +54,8 @@ private class Geary.ImapEngine.MoveEmailPrepare : Geary.ImapEngine.SendReplayOpe
         
         return ReplayOperation.Status.COMPLETED;
     }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
-        return ReplayOperation.Status.COMPLETED;
-    }
-    
-    public override async void backout_local_async() throws Error {
-    }
-    
+
     public override string describe_state() {
         return "%d email IDs".printf(prepared_for_move != null ? prepared_for_move.size : 0);
     }
 }
-
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
index 7526d557..e2e673dc 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
@@ -47,19 +47,8 @@ private class Geary.ImapEngine.MoveEmailRevoke : Geary.ImapEngine.SendReplayOper
         
         return ReplayOperation.Status.COMPLETED;
     }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
-        return ReplayOperation.Status.COMPLETED;
-    }
-    
-    public override async void backout_local_async() throws Error {
-    }
-    
+
     public override string describe_state() {
         return "%d email IDs".printf(to_revoke.size);
     }
 }
-
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 47b3417e..cad3e574 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
@@ -53,20 +53,17 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
             ids.add_all(removed_ids);
     }
 
-    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.
-        Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
-            ImapDB.EmailIdentifier.to_uids(removed_ids));
-        Imap.FolderSession remote =
-            yield this.engine.claim_remote_session(cancellable);
-        yield remote.remove_email_async(msg_sets, cancellable);
-
-        return ReplayOperation.Status.COMPLETED;
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
+        if (removed_ids.size > 0) {
+            // 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.
+            Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
+                ImapDB.EmailIdentifier.to_uids(removed_ids));
+            yield remote.remove_email_async(msg_sets, cancellable);
+        }
     }
 
     public override async void backout_local_async() throws Error {
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 b3766378..7fbb05c3 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
@@ -49,26 +49,12 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
         // DON'T update remote_count, it is intended to report the remote count at the time the
         // appended messages arrived
     }
-    
-    public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-    
-    public override async ReplayOperation.Status replay_local_async() throws Error {
-        return ReplayOperation.Status.CONTINUE;
-    }
-    
-    public override async void backout_local_async() throws Error {
-    }
 
-    public override async ReplayOperation.Status replay_remote_async()
-        throws Error {
-        if (this.positions.size > 0)
-            yield do_replay_appended_messages();
-
-        return ReplayOperation.Status.COMPLETED;
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
+        if (this.positions.size > 0) {
+            yield do_replay_appended_messages(remote);
+        }
     }
 
     public override string describe_state() {
@@ -80,7 +66,7 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
     // properly relative to the end of the message list; once this is done, notify user of new
     // messages.  If duplicates, create_email_async() will fall through to an updated merge,
     // which is exactly what we want.
-    private async void do_replay_appended_messages()
+    private async void do_replay_appended_messages(Imap.FolderSession remote)
         throws Error {
         StringBuilder positions_builder = new StringBuilder("( ");
         foreach (Imap.SequenceNumber remote_position in this.positions)
@@ -93,8 +79,6 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
         Gee.HashSet<Geary.EmailIdentifier> created = new Gee.HashSet<Geary.EmailIdentifier>();
         Gee.HashSet<Geary.EmailIdentifier> appended = new Gee.HashSet<Geary.EmailIdentifier>();
         Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.sparse(this.positions);
-        Imap.FolderSession remote =
-            yield this.owner.claim_remote_session(this.cancellable);
         foreach (Imap.MessageSet msg_set in msg_sets) {
             Gee.List<Geary.Email>? list = yield remote.list_email_async(
                 msg_set, ImapDB.Folder.REQUIRED_FIELDS, this.cancellable
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 fc25e10c..1ff5b7c7 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
@@ -42,11 +42,9 @@ private class Geary.ImapEngine.ReplayRemoval : Geary.ImapEngine.ReplayOperation
         // processed in-order with ReplayAppend operations
         return ReplayOperation.Status.CONTINUE;
     }
-    
-    public override async void backout_local_async() throws Error {
-    }
 
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
         debug("%s: ReplayRemoval this.position=%s reported_remote_count=%d",
               this.owner.to_string(), this.position.value.to_string(), this.remote_count);
 
@@ -56,7 +54,6 @@ private class Geary.ImapEngine.ReplayRemoval : Geary.ImapEngine.ReplayOperation
             debug("%s do_replay_removed_message: ignoring, invalid remote position or count",
                 to_string());
         }
-        return ReplayOperation.Status.COMPLETED;
     }
 
     public override string describe_state() {
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
index d1d95293..574293c4 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-replay-update.vala
@@ -30,15 +30,6 @@ private class Geary.ImapEngine.ReplayUpdate : Geary.ImapEngine.ReplayOperation {
         this.data = data;
     }
 
-    public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
-    }
-
-    public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-
     public override async ReplayOperation.Status replay_local_async()
         throws Error {
         Imap.MessageFlags? message_flags =
@@ -79,13 +70,6 @@ private class Geary.ImapEngine.ReplayUpdate : Geary.ImapEngine.ReplayOperation {
         return ReplayOperation.Status.COMPLETED;
     }
 
-    public override async void backout_local_async() throws Error {
-    }
-
-    public override async ReplayOperation.Status replay_remote_async() {
-        return ReplayOperation.Status.CONTINUE;
-    }
-
     public override string describe_state() {
         Imap.MessageData? fetch_flags =
             this.data.data_map.get(Imap.FetchDataSpecifier.FLAGS);
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 cee124dd..2615e6bb 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
@@ -24,27 +24,28 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
         
         this.criteria = criteria;
     }
-    
-    public override async ReplayOperation.Status replay_local_async() throws Error {
-        // accumulate nothing, nothing unfulfilled (yet)
+
+    // XXX Shouldn't need to override this, but AbstractListEmail
+    // won't let us declare it as remote-only
+    public override async ReplayOperation.Status replay_local_async()
+        throws GLib.Error {
         return ReplayOperation.Status.CONTINUE;
     }
 
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
-        Imap.FolderSession remote =
-            yield this.owner.claim_remote_session(this.cancellable);
+    public override async void replay_remote_async(Imap.FolderSession remote)
+        throws GLib.Error {
         Gee.SortedSet<Imap.UID>? uids = yield remote.search_async(
             criteria, this.cancellable
         );
         if (uids == null || uids.size == 0)
-            return ReplayOperation.Status.COMPLETED;
+            return;
 
         // if the earliest UID is not in the local store, then need to expand vector to it
         Geary.EmailIdentifier? first_id = yield owner.local_folder.get_id_async(uids.first(),
             ImapDB.Folder.ListFlags.NONE, cancellable);
         if (first_id == null)
-            yield expand_vector_async(uids.first(), 1);
-        
+            yield expand_vector_async(remote, 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) {
@@ -78,12 +79,12 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
             else
                 accumulator.add(email);
         }
-        
-        // with unfufilled set and fulfilled added to accumulator, let base class do the rest of the
-        // work
-        return yield base.replay_remote_async();
+
+        // with unfufilled set and fulfilled added to accumulator, let
+        // base class do the rest of the work
+        yield base.replay_remote_async(remote);
     }
-    
+
     public override string describe_state() {
         return "criteria=%s".printf(criteria.to_string());
     }
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-user-close.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-user-close.vala
index 48814ba0..cc0684e8 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-user-close.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-user-close.vala
@@ -1,9 +1,18 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2018 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
  */
 
+/**
+ * Operation to close the folder.
+ *
+ * This is a replay queue operation to allow existing local ops to
+ * complete, and to ease the implementation. See comments in {@link
+ * MinimalFolder.close_async}.
+ */
 private class Geary.ImapEngine.UserClose : Geary.ImapEngine.ReplayOperation {
 
     /** Determines the state of the close operation. */
@@ -19,15 +28,6 @@ private class Geary.ImapEngine.UserClose : Geary.ImapEngine.ReplayOperation {
         this.cancellable = cancellable;
     }
 
-    public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
-    }
-
-    public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-
-    public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
-    }
-
     public override async ReplayOperation.Status replay_local_async() throws Error {
         bool closing = yield this.owner.close_internal(
             Folder.CloseReason.LOCAL_CLOSE,
@@ -38,14 +38,6 @@ private class Geary.ImapEngine.UserClose : Geary.ImapEngine.ReplayOperation {
         return ReplayOperation.Status.COMPLETED;
     }
 
-    public override async void backout_local_async() throws Error {
-    }
-
-    public override async ReplayOperation.Status replay_remote_async() throws Error {
-        // should not be called
-        return ReplayOperation.Status.COMPLETED;
-    }
-
     public override string describe_state() {
         return "is_closing: %s".printf(this.is_closing.to_string());
     }


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