[geary/mjog/invert-folder-class-hierarchy: 320/362] Geary: Rework email signals on Account and Folder




commit f2e292aa28111f0e5ab044064654b86cd88d0f64
Author: Michael Gratton <mike vee net>
Date:   Thu Sep 10 21:34:42 2020 +1000

    Geary: Rework email signals on Account and Folder
    
    Rename signals on `Account` to make it more obvious what they do,
    update API doc comments, and remove `notify_foo` methods, since they
    are redundant with virtual signals and get in the way for replay ops.
    
    Remove `locally_bar` methods from `Folder`, since being a concern of
    accounts don't belong there. Update signals that chain up to equivs
    in the folder's account simply call them directly in the default
    handler. Also remove `notify_foo` methods for the same reasons as
    above.

 src/client/application/application-controller.vala |  12 --
 .../application-notification-plugin-context.vala   |  10 +-
 .../plugin/mail-merge/mail-merge-folder.vala       |   4 +-
 src/engine/api/geary-abstract-local-folder.vala    |   6 +-
 src/engine/api/geary-account.vala                  | 121 +++++------------
 src/engine/api/geary-folder.vala                   | 134 +++++--------------
 src/engine/app/app-conversation-monitor.vala       |  38 ++----
 src/engine/app/app-search-folder.vala              |  36 ++---
 .../conversation-monitor/app-conversation-set.vala |   2 -
 .../imap-engine-account-synchronizer.vala          |  12 +-
 .../imap-engine/imap-engine-email-prefetcher.vala  |   8 +-
 .../imap-engine/imap-engine-generic-account.vala   |  90 ++++---------
 .../imap-engine/imap-engine-minimal-folder.vala    | 145 ++++++---------------
 .../imap-engine-abstract-list-email.vala           |   4 +-
 .../replay-ops/imap-engine-empty-folder.vala       |  11 +-
 .../replay-ops/imap-engine-fetch-email.vala        |   9 +-
 .../replay-ops/imap-engine-mark-email.vala         |   2 +-
 .../replay-ops/imap-engine-move-email-commit.vala  |   4 +-
 .../replay-ops/imap-engine-move-email-prepare.vala |  10 +-
 .../replay-ops/imap-engine-move-email-revoke.vala  |   7 +-
 .../replay-ops/imap-engine-remove-email.vala       |  15 ++-
 .../replay-ops/imap-engine-replay-append.vala      |  17 ++-
 .../replay-ops/imap-engine-replay-removal.vala     |  10 +-
 .../replay-ops/imap-engine-replay-update.vala      |   2 +-
 src/engine/outbox/outbox-folder.vala               |  13 +-
 test/engine/app/app-conversation-monitor-test.vala |   6 +-
 26 files changed, 233 insertions(+), 495 deletions(-)
---
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index 4bb2f631f..e28da2f6d 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -996,7 +996,6 @@ internal class Application.Controller :
         account.notify["current-status"].connect(
             on_account_status_notify
         );
-        account.email_removed.connect(on_account_email_removed);
         account.folders_available_unavailable.connect(on_folders_available_unavailable);
         account.report_problem.connect(on_report_problem);
 
@@ -1081,7 +1080,6 @@ internal class Application.Controller :
                 on_account_status_notify
             );
 
-            account.email_removed.disconnect(on_account_email_removed);
             account.folders_available_unavailable.disconnect(on_folders_available_unavailable);
 
             Geary.Smtp.ClientService? smtp = (
@@ -1307,16 +1305,6 @@ internal class Application.Controller :
         update_account_status();
     }
 
-    private void on_account_email_removed(Geary.Folder folder,
-                                          Gee.Collection<Geary.EmailIdentifier> ids) {
-        if (folder.used_as == OUTBOX) {
-            foreach (MainWindow window in this.application.get_main_windows()) {
-                window.status_bar.deactivate_message(StatusBar.Message.OUTBOX_SEND_FAILURE);
-                window.status_bar.deactivate_message(StatusBar.Message.OUTBOX_SAVE_SENT_MAIL_FAILED);
-            }
-        }
-    }
-
     private void on_sending_started() {
         foreach (MainWindow window in this.application.get_main_windows()) {
             window.status_bar.activate_message(StatusBar.Message.OUTBOX_SENDING);
diff --git a/src/client/application/application-notification-plugin-context.vala 
b/src/client/application/application-notification-plugin-context.vala
index 774a543f2..57f30f745 100644
--- a/src/client/application/application-notification-plugin-context.vala
+++ b/src/client/application/application-notification-plugin-context.vala
@@ -158,7 +158,7 @@ internal class Application.NotificationPluginContext :
         if (folder != null &&
             context != null &&
             !this.folder_information.has_key(folder)) {
-            folder.email_locally_appended.connect(on_email_locally_appended);
+            folder.email_appended.connect(on_email_added);
             folder.email_flags_changed.connect(on_email_flags_changed);
             folder.email_removed.connect(on_email_removed);
 
@@ -267,7 +267,7 @@ internal class Application.NotificationPluginContext :
     private void remove_folder(Geary.Folder target) {
         MonitorInformation? info = this.folder_information.get(target);
         if (info != null) {
-            target.email_locally_appended.disconnect(on_email_locally_appended);
+            target.email_appended.disconnect(on_email_added);
             target.email_flags_changed.disconnect(on_email_flags_changed);
             target.email_removed.disconnect(on_email_removed);
 
@@ -311,13 +311,13 @@ internal class Application.NotificationPluginContext :
         }
     }
 
-    private void on_email_locally_appended(Geary.Folder folder,
-        Gee.Collection<Geary.EmailIdentifier> email_ids) {
+    private void on_email_added(Geary.Folder folder,
+                                Gee.Collection<Geary.EmailIdentifier> email_ids) {
         do_process_new_email.begin(folder, email_ids);
     }
 
     private void on_email_flags_changed(Geary.Folder folder,
-        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> ids) {
+                                        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> ids) {
         retire_new_messages(folder, ids.keys);
     }
 
diff --git a/src/client/plugin/mail-merge/mail-merge-folder.vala 
b/src/client/plugin/mail-merge/mail-merge-folder.vala
index bd6cafca7..5c5a50c65 100644
--- a/src/client/plugin/mail-merge/mail-merge-folder.vala
+++ b/src/client/plugin/mail-merge/mail-merge-folder.vala
@@ -344,7 +344,7 @@ public class MailMerge.Folder : Geary.AbstractLocalFolder {
                 this._properties.set_total((int) next_id);
                 this.email_total = (uint) next_id;
 
-                notify_email_inserted(Geary.Collection.single(id));
+                email_inserted(Geary.Collection.single(id));
                 record = yield this.data.read_record();
             }
         } catch (GLib.Error err) {
@@ -384,7 +384,7 @@ public class MailMerge.Folder : Geary.AbstractLocalFolder {
                     this.email.unset(id);
                     this.composed.unset(id);
                     this._properties.set_total(last);
-                    notify_email_removed(Geary.Collection.single(id));
+                    email_removed(Geary.Collection.single(id));
 
                     // Rate limit to ~30/minute for now
                     GLib.Timeout.add_seconds(2, this.send_loop.callback);
diff --git a/src/engine/api/geary-abstract-local-folder.vala b/src/engine/api/geary-abstract-local-folder.vala
index e82173288..867d38a7e 100644
--- a/src/engine/api/geary-abstract-local-folder.vala
+++ b/src/engine/api/geary-abstract-local-folder.vala
@@ -40,7 +40,7 @@ public abstract class Geary.AbstractLocalFolder : Geary.Folder {
 
         closed_semaphore.reset();
 
-        notify_opened(Geary.Folder.OpenState.LOCAL, properties.email_total);
+        opened(LOCAL, properties.email_total);
 
         return true;
     }
@@ -51,8 +51,8 @@ public abstract class Geary.AbstractLocalFolder : Geary.Folder {
 
         closed_semaphore.blind_notify();
 
-        notify_closed(Geary.Folder.CloseReason.LOCAL_CLOSE);
-        notify_closed(Geary.Folder.CloseReason.FOLDER_CLOSED);
+        closed(LOCAL_CLOSE);
+        closed(FOLDER_CLOSED);
 
         return false;
     }
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 787fcbad9..8f9b9339a 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -157,11 +157,16 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
      */
     public GLib.DateTime? last_storage_cleanup { get; set; }
 
+    /** {@inheritDoc} */
+    public Logging.Source? logging_parent { get { return null; } }
+
+
+    /** Emitted when the account has been opened. */
     public signal void opened();
 
+    /** Emitted when the account has been closed. */
     public signal void closed();
 
-
     /**
      * Emitted to notify the client that some problem has occurred.
      *
@@ -226,48 +231,55 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
     public signal void folders_use_changed(Gee.Collection<Geary.Folder> altered);
 
     /**
-     * Fired when emails are appended to a folder in this account.
+     * Emitted when emails are appended to a folder for this account.
      */
-    public signal void email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
+    public signal void email_appended_to_folder(Folder folder,
+                                                Gee.Collection<EmailIdentifier> ids);
 
     /**
-     * Fired when emails are inserted to a folder in this account.
+     * Emitted when email messages are inserted into a folder for this account.
      *
      * @see Folder.email_inserted
      */
-    public signal void email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
+    public signal void email_inserted_into_folder(Folder folder,
+                                                  Gee.Collection<EmailIdentifier> ids);
 
     /**
-     * Fired when emails are removed from a folder in this account.
+     * Emitted when email messages are removed from a folder for this account.
      */
-    public signal void email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
+    public signal void email_removed_from_folder(Folder folder,
+                                                 Gee.Collection<EmailIdentifier> ids);
 
     /**
-     * Fired when emails are removed from a local folder in this account.
+     * Fired when the supplied email flags have changed from any folder.
      */
-    public signal void email_locally_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
+    public signal void email_flags_changed_in_folder(Folder folder,
+                                                     Gee.Map<EmailIdentifier,EmailFlags> map);
 
     /**
-     * Fired when one or more emails have been locally saved to a folder with
-     * the full set of Fields.
+     * Emitted when email has been added to the account.
+     *
+     * This will be fired for one or more email messages when they are
+     * added to an the account for the first time, including the
+     * folder in they first appear.
      */
-    public signal void email_locally_complete(Geary.Folder folder,
-        Gee.Collection<Geary.EmailIdentifier> ids);
+    public signal void email_added(Gee.Collection<EmailIdentifier> ids,
+                                   Folder containing_folder);
 
     /**
-     * Fired when one or more emails have been discovered (added) to the Folder, but not necessarily
-     * appended (i.e. old email pulled down due to user request or background fetching).
+     * Emitted when email has been removed from the account.
+     *
+     * This will be fired for one or more email messages when they
+     * have been removed from the account, at some point (not not
+     * necessarily immediately) after they have been removed from all
+     * folders they have been present.
      */
-    public signal void email_discovered(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids);
+    public signal void email_removed(Gee.Collection<EmailIdentifier> ids);
 
     /**
-     * Fired when the supplied email flags have changed from any folder.
+     * Emitted when email mesages have been fully downloaded.
      */
-    public signal void email_flags_changed(Geary.Folder folder,
-        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map);
-
-    /** {@inheritDoc} */
-    public Logging.Source? logging_parent { get { return null; } }
+    public signal void email_complete(Gee.Collection<EmailIdentifier> ids);
 
 
     protected Account(AccountInformation information,
@@ -558,71 +570,6 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
     public abstract async void cleanup_storage(GLib.Cancellable? cancellable)
         throws GLib.Error;
 
-    /** Fires a {@link opened} signal. */
-    protected virtual void notify_opened() {
-        opened();
-    }
-
-    /** Fires a {@link closed} signal. */
-    protected virtual void notify_closed() {
-        closed();
-    }
-
-    /** Fires a {@link folders_available_unavailable} signal. */
-    protected virtual void
-        notify_folders_available_unavailable(Gee.BidirSortedSet<Folder>? available,
-                                             Gee.BidirSortedSet<Folder>? unavailable) {
-        folders_available_unavailable(available, unavailable);
-    }
-
-    /** Fires a {@link folders_created} signal. */
-    protected virtual void notify_folders_created(Gee.BidirSortedSet<Geary.Folder> created) {
-        folders_created(created);
-    }
-
-    /** Fires a {@link folders_deleted} signal. */
-    protected virtual void notify_folders_deleted(Gee.BidirSortedSet<Geary.Folder> deleted) {
-        folders_deleted(deleted);
-    }
-
-    /** Fires a {@link email_appended} signal. */
-    protected virtual void notify_email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
-        email_appended(folder, ids);
-    }
-
-    /** Fires a {@link email_inserted} signal. */
-    protected virtual void notify_email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
-        email_inserted(folder, ids);
-    }
-
-    /** Fires a {@link email_removed} signal. */
-    protected virtual void notify_email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
-        email_removed(folder, ids);
-    }
-
-    /** Fires a {@link email_locally_removed} signal. */
-    protected virtual void notify_email_locally_removed(Geary.Folder folder, 
Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_locally_removed(folder, ids);
-    }
-
-    /** Fires a {@link email_locally_complete} signal. */
-    protected virtual void notify_email_locally_complete(Geary.Folder folder,
-        Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_locally_complete(folder, ids);
-    }
-
-    /** Fires a {@link email_discovered} signal. */
-    protected virtual void notify_email_discovered(Geary.Folder folder,
-        Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_discovered(folder, ids);
-    }
-
-    /** Fires a {@link email_flags_changed} signal. */
-    protected virtual void notify_email_flags_changed(Geary.Folder folder,
-        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map) {
-        email_flags_changed(folder, flag_map);
-    }
-
     /** Fires a {@link report_problem} signal for this account. */
     protected virtual void notify_report_problem(ProblemReport report) {
         report_problem(report);
diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala
index 587014273..ed11a7af4 100644
--- a/src/engine/api/geary-folder.vala
+++ b/src/engine/api/geary-folder.vala
@@ -365,52 +365,34 @@ public abstract class Geary.Folder : BaseObject, Logging.Source {
     public signal void closed(CloseReason reason);
 
     /**
-     * Fired when email has been appended to the list of messages in the folder.
+     * Fired when email has been appended to the folder.
      *
-     * The {@link EmailIdentifier} for all appended messages is supplied as a signal parameter.
+     * The {@link EmailIdentifier} for all appended messages is
+     * supplied as a signal parameter. The messages have been added to
+     * the "top" of the vector of messages, for example, newly
+     * delivered email.
      *
-     * @see email_locally_appended
+     * @see email_inserted
      */
-    public signal void email_appended(Gee.Collection<Geary.EmailIdentifier> ids);
+    public virtual signal void email_appended(Gee.Collection<EmailIdentifier> ids) {
+        this.account.email_appended_to_folder(this, ids);
+    }
 
     /**
-     * Fired when previously unknown messages have been appended to the list of email in the folder.
+     * Fired when email has been inserted into the folder.
      *
-     * This is similar to {@link email_appended}, but that signal lists ''all'' messages appended
-     * to the folder.  email_locally_appended only reports email that have not been downloaded
-     * prior to the database (and not removed permanently since).  Hence, an email that is removed
-     * from the folder and returned later will not be listed here (unless it was removed from the
-     * local store in the meantime).
+     * The {@link EmailIdentifier} for all inserted messages is
+     * supplied as a signal parameter. Inserted messages are not added
+     * to the "top" of the vector of messages, but rather into the
+     * middle or beginning. This can happen for a number of reasons,
+     * including vector expansion, but note that newly received
+     * messages are appended and notified via {@link email_appended}.
      *
      * @see email_appended
      */
-    public signal void email_locally_appended(Gee.Collection<Geary.EmailIdentifier> ids);
-
-    /**
-     * Fired when email has been inserted into the list of messages in the folder.
-     *
-     * The {@link EmailIdentifier} for all inserted messages is supplied as a signal parameter.
-     * Inserted messages are not added to the "top" of the vector of messages, but rather into
-     * the middle or beginning.  This can happen for a number of reasons.  Newly received messages
-     * are appended.
-     *
-     * @see email_locally_inserted
-     */
-    public signal void email_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
-
-    /**
-     * Fired when previously unknown messages have been appended to the list of email in the folder.
-     *
-     * This is similar to {@link email_inserted}, but that signal lists ''all'' messages inserted
-     * to the folder.  email_locally_inserted only reports email that have not been downloaded
-     * prior to the database (and not removed permanently since).  Hence, an email that is removed
-     * from the folder and returned later will not be listed here (unless it was removed from the
-     * local store in the meantime).
-     *
-     * @see email_inserted
-     * @see email_locally_inserted
-     */
-    public signal void email_locally_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
+    public virtual signal void email_inserted(Gee.Collection<EmailIdentifier> ids) {
+        this.account.email_inserted_into_folder(this, ids);
+    }
 
     /**
      * Fired when email has been removed (deleted or moved) from the folder.
@@ -423,33 +405,27 @@ public abstract class Geary.Folder : BaseObject, Logging.Source {
      * known locally (and therefore the caller could not have record of).  If this happens, this
      * signal will ''not'' fire, although {@link email_count_changed} will.
      */
-    public signal void email_removed(Gee.Collection<Geary.EmailIdentifier> ids);
+    public virtual signal void email_removed(Gee.Collection<EmailIdentifier> ids) {
+        this.account.email_removed_from_folder(this, ids);
+    }
 
     /**
-     * Fired when emails are removed from the local folder.
+     * Fired when the supplied email flags have changed, whether due to local action or reported by
+     * the server.
      */
-    public signal void email_locally_removed(Gee.Collection<Geary.EmailIdentifier> ids);
+    public virtual signal void email_flags_changed(Gee.Map<EmailIdentifier,EmailFlags> map) {
+        this.account.email_flags_changed_in_folder(this, map);
+    }
 
     /**
      * Fired when the total count of email in a folder has changed in any way.
      *
-     * Note that this signal will fire after {@link email_appended}, {@link email_locally_appended},
-     * and {@link email_removed} (although see the note at email_removed).
+     * Note that this signal will fire after {@link email_appended},
+     * and {@link email_removed} (although see the note at
+     * email_removed).
      */
     public signal void email_count_changed(int new_count, CountChangeReason reason);
 
-    /**
-     * Fired when the supplied email flags have changed, whether due to local action or reported by
-     * the server.
-     */
-    public signal void email_flags_changed(Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map);
-
-    /**
-     * Fired when one or more emails have been locally saved with the full set
-     * of Fields.
-     */
-    public signal void email_locally_complete(Gee.Collection<Geary.EmailIdentifier> ids);
-
     /**
     * Fired when the folder's special use has changed.
     *
@@ -459,56 +435,6 @@ public abstract class Geary.Folder : BaseObject, Logging.Source {
     public signal void use_changed(SpecialUse old_use, SpecialUse new_use);
 
 
-    protected virtual void notify_opened(Geary.Folder.OpenState state, int count) {
-        opened(state, count);
-    }
-
-    protected virtual void notify_open_failed(Geary.Folder.OpenFailed failure, Error? err) {
-        open_failed(failure, err);
-    }
-
-    protected virtual void notify_closed(Geary.Folder.CloseReason reason) {
-        closed(reason);
-    }
-
-    protected virtual void notify_email_appended(Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_appended(ids);
-    }
-
-    protected virtual void notify_email_locally_appended(Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_locally_appended(ids);
-    }
-
-    protected virtual void notify_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_inserted(ids);
-    }
-
-    protected virtual void notify_email_locally_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_locally_inserted(ids);
-    }
-
-    protected virtual void notify_email_removed(Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_removed(ids);
-    }
-
-    protected virtual void notify_email_count_changed(int new_count, Folder.CountChangeReason reason) {
-        email_count_changed(new_count, reason);
-    }
-
-    protected virtual void notify_email_flags_changed(Gee.Map<Geary.EmailIdentifier,
-        Geary.EmailFlags> flag_map) {
-        email_flags_changed(flag_map);
-    }
-
-    protected virtual void notify_email_locally_complete(Gee.Collection<Geary.EmailIdentifier> ids) {
-        email_locally_complete(ids);
-    }
-
-    protected virtual void notify_use_changed(SpecialUse old_use,
-                                              SpecialUse new_use) {
-        use_changed(old_use, new_use);
-    }
-
     /** Determines if a folder has been opened, and if so in which way. */
     public abstract OpenState get_open_state();
 
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index 6272c7d85..98d1a1a05 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -319,15 +319,12 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
 
         this.base_folder.email_appended.connect(on_folder_email_appended);
         this.base_folder.email_inserted.connect(on_folder_email_inserted);
-        this.base_folder.email_locally_complete.connect(on_folder_email_complete);
         this.base_folder.email_removed.connect(on_folder_email_removed);
-        this.base_folder.email_locally_removed.connect(on_folder_email_removed);
         this.base_folder.opened.connect(on_folder_opened);
-        this.base_folder.account.email_appended.connect(on_account_email_appended);
-        this.base_folder.account.email_inserted.connect(on_account_email_inserted);
-        this.base_folder.account.email_locally_complete.connect(on_account_email_complete);
-        this.base_folder.account.email_removed.connect(on_account_email_removed);
-        this.base_folder.account.email_flags_changed.connect(on_account_email_flags_changed);
+        this.base_folder.account.email_appended_to_folder.connect(on_account_email_appended);
+        this.base_folder.account.email_inserted_into_folder.connect(on_account_email_inserted);
+        this.base_folder.account.email_removed_from_folder.connect(on_account_email_removed);
+        this.base_folder.account.email_flags_changed_in_folder.connect(on_account_email_flags_changed);
 
         this.queue.operation_error.connect(on_operation_error);
         this.queue.add(new FillWindowOperation(this));
@@ -668,15 +665,12 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         throws Error {
         this.base_folder.email_appended.disconnect(on_folder_email_appended);
         this.base_folder.email_inserted.disconnect(on_folder_email_inserted);
-        this.base_folder.email_locally_complete.disconnect(on_folder_email_complete);
         this.base_folder.email_removed.disconnect(on_folder_email_removed);
-        this.base_folder.email_locally_removed.disconnect(on_folder_email_removed);
         this.base_folder.opened.disconnect(on_folder_opened);
-        this.base_folder.account.email_appended.disconnect(on_account_email_appended);
-        this.base_folder.account.email_inserted.disconnect(on_account_email_inserted);
-        this.base_folder.account.email_locally_complete.disconnect(on_account_email_complete);
-        this.base_folder.account.email_removed.disconnect(on_account_email_removed);
-        this.base_folder.account.email_flags_changed.disconnect(on_account_email_flags_changed);
+        this.base_folder.account.email_appended_to_folder.disconnect(on_account_email_appended);
+        this.base_folder.account.email_inserted_into_folder.disconnect(on_account_email_inserted);
+        this.base_folder.account.email_removed_from_folder.disconnect(on_account_email_removed);
+        this.base_folder.account.email_flags_changed_in_folder.disconnect(on_account_email_flags_changed);
 
         // Cancel outstanding ops so they don't block the queue closing
         this.operation_cancellable.cancel();
@@ -838,12 +832,6 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         this.queue.add(new AppendOperation(this, appended));
     }
 
-    private void on_folder_email_complete(Gee.Collection<EmailIdentifier> completed) {
-        // InsertOperation will add the emails only if they are after
-        // the earliest, which is what we want here.
-        this.queue.add(new InsertOperation(this, completed));
-    }
-
     private void on_folder_email_inserted(Gee.Collection<EmailIdentifier> inserted) {
         this.queue.add(new InsertOperation(this, inserted));
     }
@@ -859,16 +847,6 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         }
     }
 
-    private void on_account_email_complete(Folder folder,
-                                           Gee.Collection<EmailIdentifier> inserted) {
-        // ExternalAppendOperation will check to determine if the
-        // email is relevant for some existing conversation before
-        // adding it, which is what we want here.
-        if (folder != this.base_folder) {
-            this.queue.add(new ExternalAppendOperation(this, folder, inserted));
-        }
-    }
-
     private void on_account_email_inserted(Folder folder,
                                            Gee.Collection<EmailIdentifier> inserted) {
         // ExternalAppendOperation will check to determine if the
diff --git a/src/engine/app/app-search-folder.vala b/src/engine/app/app-search-folder.vala
index a8fa9404e..d08e45b73 100644
--- a/src/engine/app/app-search-folder.vala
+++ b/src/engine/app/app-search-folder.vala
@@ -127,9 +127,9 @@ public class Geary.App.SearchFolder :
 
         account.folders_available_unavailable.connect(on_folders_available_unavailable);
         account.folders_use_changed.connect(on_folders_use_changed);
-        account.email_locally_complete.connect(on_email_locally_complete);
-        account.email_removed.connect(on_account_email_removed);
-        account.email_locally_removed.connect(on_account_email_removed);
+        account.email_appended_to_folder.connect(on_email_added);
+        account.email_inserted_into_folder.connect(on_email_added);
+        account.email_removed_from_folder.connect(on_email_removed);
 
         this.entries = new_entry_set();
         this.ids = new_id_map();
@@ -142,9 +142,9 @@ public class Geary.App.SearchFolder :
     ~SearchFolder() {
         account.folders_available_unavailable.disconnect(on_folders_available_unavailable);
         account.folders_use_changed.disconnect(on_folders_use_changed);
-        account.email_locally_complete.disconnect(on_email_locally_complete);
-        account.email_removed.disconnect(on_account_email_removed);
-        account.email_locally_removed.disconnect(on_account_email_removed);
+        account.email_appended_to_folder.disconnect(on_email_added);
+        account.email_inserted_into_folder.disconnect(on_email_added);
+        account.email_removed_from_folder.disconnect(on_email_removed);
     }
 
     /**
@@ -180,8 +180,8 @@ public class Geary.App.SearchFolder :
         this.entries = new_entry_set();
         this.ids = new_id_map();
 
-        notify_email_removed(old_ids.keys);
-        notify_email_count_changed(0, REMOVED);
+        email_removed(old_ids.keys);
+        email_count_changed(0, REMOVED);
     }
 
     /**
@@ -609,7 +609,8 @@ public class Geary.App.SearchFolder :
 
             Folder.CountChangeReason reason = CountChangeReason.NONE;
             if (removed.size > 0) {
-                notify_email_removed(removed);
+                email_removed(removed);
+                this.account.email_removed_from_folder(this, removed);
                 reason |= Folder.CountChangeReason.REMOVED;
             }
             if (added.size > 0) {
@@ -618,11 +619,12 @@ public class Geary.App.SearchFolder :
                 // append a thousand results at once and the
                 // ConversationMonitor's inability to handle that
                 // gracefully (#7464), we always use INSERTED for now.
-                notify_email_inserted(added);
+                email_inserted(added);
+                this.account.email_inserted_into_folder(this, removed);
                 reason |= Folder.CountChangeReason.INSERTED;
             }
             if (reason != CountChangeReason.NONE) {
-                notify_email_count_changed(this.entries.size, reason);
+                email_count_changed(this.entries.size, reason);
             }
             debug("Processing done, entries/ids: %d/%d", entries.size, ids.size);
         } else {
@@ -672,17 +674,17 @@ public class Geary.App.SearchFolder :
         }
     }
 
-    private void on_email_locally_complete(Folder folder,
-                                           Gee.Collection<EmailIdentifier> ids) {
+    private void on_email_added(Geary.Folder source,
+                                Gee.Collection<EmailIdentifier> ids) {
         if (this.query != null) {
-            this.append.begin(folder, ids);
+            this.append.begin(source, ids);
         }
     }
 
-    private void on_account_email_removed(Folder folder,
-                                          Gee.Collection<EmailIdentifier> ids) {
+    private void on_email_removed(Geary.Folder source,
+                                  Gee.Collection<EmailIdentifier> ids) {
         if (this.query != null) {
-            this.remove.begin(folder, ids);
+            this.remove.begin(source, ids);
         }
     }
 
diff --git a/src/engine/app/conversation-monitor/app-conversation-set.vala 
b/src/engine/app/conversation-monitor/app-conversation-set.vala
index 8b5c8a0dd..60653c09f 100644
--- a/src/engine/app/conversation-monitor/app-conversation-set.vala
+++ b/src/engine/app/conversation-monitor/app-conversation-set.vala
@@ -195,8 +195,6 @@ private class Geary.App.ConversationSet : BaseObject, Logging.Source {
                 if (email != null) {
                     switch (conversation.get_folder_count(id)) {
                     case 0:
-                        warning("Email %s conversation %s not in any folders",
-                                id.to_string(), conversation.to_string());
                         break;
 
                     case 1:
diff --git a/src/engine/imap-engine/imap-engine-account-synchronizer.vala 
b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
index 5f7e6f50c..3342fabd8 100644
--- a/src/engine/imap-engine/imap-engine-account-synchronizer.vala
+++ b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
@@ -329,8 +329,10 @@ private class Geary.ImapEngine.FullFolderSync : RefreshFolderSync {
                 yield local_folder.detach_emails_before_timestamp(max_epoch,
                                                                   cancellable);
             if (detached_ids != null) {
-                this.account.email_locally_removed(this.folder, detached_ids);
-                this.folder.email_locally_removed(detached_ids);
+                this.folder.email_removed(detached_ids);
+                this.folder.account.email_removed_from_folder(
+                    this.folder, detached_ids
+                );
 
                 // Ensure a foreground GC is queued so any email now
                 // folderless will be reaped
@@ -502,8 +504,10 @@ private class Geary.ImapEngine.TruncateToEpochFolderSync : FolderSync {
                 yield local_folder.detach_emails_before_timestamp(max_epoch,
                                                                   cancellable);
             if (detached_ids != null) {
-                this.account.email_locally_removed(this.folder, detached_ids);
-                this.folder.email_locally_removed(detached_ids);
+                this.folder.email_removed(detached_ids);
+                this.folder.account.email_removed_from_folder(
+                    this.folder, detached_ids
+                );
                 this.post_idle_detach_op.messages_detached();
             }
         }
diff --git a/src/engine/imap-engine/imap-engine-email-prefetcher.vala 
b/src/engine/imap-engine/imap-engine-email-prefetcher.vala
index 3c12583b5..0bdfe7c20 100644
--- a/src/engine/imap-engine/imap-engine-email-prefetcher.vala
+++ b/src/engine/imap-engine/imap-engine-email-prefetcher.vala
@@ -53,8 +53,8 @@ private class Geary.ImapEngine.EmailPrefetcher : Geary.BaseObject {
     public void open() {
         this.cancellable = new Cancellable();
 
-        this.folder.email_locally_appended.connect(on_local_expansion);
-        this.folder.email_locally_inserted.connect(on_local_expansion);
+        this.folder.email_appended.connect(on_local_expansion);
+        this.folder.email_inserted.connect(on_local_expansion);
 
         // acquire here since .begin() only schedules for later
         this.active_sem.acquire();
@@ -71,8 +71,8 @@ private class Geary.ImapEngine.EmailPrefetcher : Geary.BaseObject {
             this.active_sem.blind_notify();
         }
 
-        this.folder.email_locally_appended.disconnect(on_local_expansion);
-        this.folder.email_locally_inserted.disconnect(on_local_expansion);
+        this.folder.email_appended.disconnect(on_local_expansion);
+        this.folder.email_inserted.disconnect(on_local_expansion);
         this.cancellable = null;
     }
 
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index a459274b6..ca569283a 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -149,9 +149,13 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
 
         this.last_storage_cleanup = yield this.local.fetch_last_cleanup_async(cancellable);
         this.notify["last_storage_cleanup"].connect(on_last_storage_cleanup_notify);
+        this.email_appended_to_folder.connect(on_folder_contents_altered);
+        this.email_inserted_into_folder.connect(on_folder_contents_altered);
+        this.email_removed_from_folder.connect(on_folder_contents_altered);
+        this.email_flags_changed_in_folder.connect(on_folder_contents_altered);
 
         this.open = true;
-        notify_opened();
+        opened();
 
         this.queue_operation(new LoadFolders(this, this.local));
 
@@ -179,6 +183,11 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             debug("Error stopping SMTP service: %s", err.message);
         }
 
+        this.email_appended_to_folder.disconnect(on_folder_contents_altered);
+        this.email_inserted_into_folder.disconnect(on_folder_contents_altered);
+        this.email_removed_from_folder.disconnect(on_folder_contents_altered);
+        this.email_flags_changed_in_folder.disconnect(on_folder_contents_altered);
+
         // Halt internal tasks early so they stop using local and
         // remote connections.
         this.refresh_folder_timer.reset();
@@ -195,11 +204,11 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
 
         var locals = sort_by_path(this.local_folders.values);
         this.local_folders.clear();
-        notify_folders_available_unavailable(null, locals);
+        folders_available_unavailable(null, locals);
 
         var remotes = sort_by_path(this.remote_folders.values);
         this.remote_folders.clear();
-        notify_folders_available_unavailable(null, remotes);
+        folders_available_unavailable(null, remotes);
         foreach (var folder in remotes) {
             debug("Waiting for remote to close: %s", folder.to_string());
             yield folder.wait_for_close_async();
@@ -220,7 +229,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             yield local.close_async(cancellable);
         } finally {
             this.open = false;
-            notify_closed();
+            closed();
         }
     }
 
@@ -535,7 +544,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             );
         }
         this.local_folders.set(path, local);
-        notify_folders_available_unavailable(
+        folders_available_unavailable(
             sort_by_path(Collection.single(local)), null
         );
     }
@@ -549,7 +558,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
                 "Unknown folder: %s", path.to_string()
             );
         }
-        notify_folders_available_unavailable(
+        folders_available_unavailable(
             null, sort_by_path(Collection.single(local))
         );
         this.local_folders.unset(path);
@@ -694,9 +703,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         }
 
         if (!built_folders.is_empty) {
-            notify_folders_available_unavailable(built_folders, null);
+            folders_available_unavailable(built_folders, null);
             if (!are_existing) {
-                notify_folders_created(built_folders);
+                folders_created(built_folders);
             }
         }
 
@@ -778,8 +787,8 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         }
 
         if (!removed.is_empty) {
-            notify_folders_available_unavailable(null, removed);
-            notify_folders_deleted(removed);
+            folders_available_unavailable(null, removed);
+            folders_deleted(removed);
         }
 
         return removed;
@@ -865,63 +874,6 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
      */
     protected abstract MinimalFolder new_folder(ImapDB.Folder local_folder);
 
-    /** {@inheritDoc} */
-    protected override void
-        notify_folders_available_unavailable(Gee.BidirSortedSet<Folder>? available,
-                                             Gee.BidirSortedSet<Folder>? unavailable) {
-        base.notify_folders_available_unavailable(available, unavailable);
-        if (available != null) {
-            foreach (Geary.Folder folder in available) {
-                folder.email_appended.connect(notify_email_appended);
-                folder.email_inserted.connect(notify_email_inserted);
-                folder.email_removed.connect(notify_email_removed);
-                folder.email_locally_removed.connect(notify_email_locally_removed);
-                folder.email_locally_complete.connect(notify_email_locally_complete);
-                folder.email_flags_changed.connect(notify_email_flags_changed);
-            }
-        }
-        if (unavailable != null) {
-            foreach (Geary.Folder folder in unavailable) {
-                folder.email_appended.disconnect(notify_email_appended);
-                folder.email_inserted.disconnect(notify_email_inserted);
-                folder.email_removed.disconnect(notify_email_removed);
-                folder.email_locally_removed.disconnect(notify_email_locally_removed);
-                folder.email_locally_complete.disconnect(notify_email_locally_complete);
-                folder.email_flags_changed.disconnect(notify_email_flags_changed);
-            }
-        }
-    }
-
-    /** {@inheritDoc} */
-    protected override void notify_email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
-        base.notify_email_appended(folder, ids);
-        schedule_unseen_update(folder);
-    }
-
-    /** {@inheritDoc} */
-    protected override void notify_email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
-        base.notify_email_inserted(folder, ids);
-        schedule_unseen_update(folder);
-    }
-
-    /** {@inheritDoc} */
-    protected override void notify_email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
-        base.notify_email_removed(folder, ids);
-        schedule_unseen_update(folder);
-    }
-
-    /** {@inheritDoc} */
-    protected override void notify_email_locally_removed(Geary.Folder folder, 
Gee.Collection<Geary.EmailIdentifier> ids) {
-        base.notify_email_locally_removed(folder, ids);
-    }
-
-    /** {@inheritDoc} */
-    protected override void notify_email_flags_changed(Geary.Folder folder,
-        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map) {
-        base.notify_email_flags_changed(folder, flag_map);
-        schedule_unseen_update(folder);
-    }
-
     /**
      * Hooks up and queues an {@link UpdateRemoteFolders} operation.
      */
@@ -1130,6 +1082,10 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         }
     }
 
+    private void on_folder_contents_altered(Folder folder) {
+        schedule_unseen_update(folder);
+    }
+
     private void on_last_storage_cleanup_notify() {
         this.local.set_last_cleanup_async.begin(
             this.last_storage_cleanup,
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala 
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index a004e1844..62e6c8645 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -153,31 +153,6 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         closing(final_ops);
     }
 
-    /*
-     * These signal notifiers are marked public (note this is a private class) so the various
-     * ReplayOperations can directly fire the associated signals while within the queue.
-     */
-
-    public void replay_notify_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
-        notify_email_inserted(ids);
-    }
-
-    public void replay_notify_email_locally_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
-        notify_email_locally_inserted(ids);
-    }
-
-    public void replay_notify_email_removed(Gee.Collection<Geary.EmailIdentifier> ids) {
-        notify_email_removed(ids);
-    }
-
-    public void replay_notify_email_count_changed(int new_count, Folder.CountChangeReason reason) {
-        notify_email_count_changed(new_count, reason);
-    }
-
-    public void replay_notify_email_flags_changed(Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map) 
{
-        notify_email_flags_changed(flag_map);
-    }
-
     public override void set_used_as_custom(bool enabled)
         throws EngineError.UNSUPPORTED {
         if (enabled) {
@@ -202,7 +177,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         var old_use = this._used_as;
         this._used_as = new_use;
         if (old_use != new_use) {
-            notify_use_changed(old_use, new_use);
+            use_changed(old_use, new_use);
             update_harvester();
         }
     }
@@ -366,8 +341,8 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         if (all != null && all.size > 0) {
             Gee.List<EmailIdentifier> ids =
                 traverse<Email>(all).map<EmailIdentifier>((email) => email.id).to_array_list();
-            notify_email_removed(ids);
-            notify_email_count_changed(0, Folder.CountChangeReason.REMOVED);
+            email_removed(ids);
+            email_count_changed(0, Folder.CountChangeReason.REMOVED);
         }
     }
 
@@ -607,11 +582,11 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
 
         check_open("normalize_folders (list remote appended/inserted required fields)");
 
-        // store new messages and add IDs to the appended/discovered EmailIdentifier buckets
-        Gee.Set<ImapDB.EmailIdentifier> appended_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
-        Gee.Set<ImapDB.EmailIdentifier> locally_appended_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
-        Gee.Set<ImapDB.EmailIdentifier> inserted_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
-        Gee.Set<ImapDB.EmailIdentifier> locally_inserted_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
+        // store new messages and add IDs to the appended/discovered
+        // EmailIdentifier buckets
+        var appended_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
+        var inserted_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
+        var created_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
         if (to_create.size > 0) {
             // Don't update the unread count here, since it'll get
             // updated once normalisation has finished anyway. See
@@ -627,20 +602,13 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             yield Nonblocking.Concurrent.global.schedule_async(() => {
                 foreach (Email email in created_or_merged.keys) {
                     ImapDB.EmailIdentifier id = (ImapDB.EmailIdentifier) email.id;
-                    bool created = created_or_merged.get(email);
-
-                    // report all appended email, but separate out email never seen before (created)
-                    // as locally-appended
+                    if (created_or_merged.get(email)) {
+                        created_ids.add(id);
+                    }
                     if (appended_uids.contains(id.uid)) {
                         appended_ids.add(id);
-
-                        if (created)
-                            locally_appended_ids.add(id);
                     } else if (inserted_uids.contains(id.uid)) {
                         inserted_ids.add(id);
-
-                        if (created)
-                            locally_inserted_ids.add(id);
                     }
                 }
             }, cancellable);
@@ -685,27 +653,25 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             // notify subscribers about emails that have been removed
             debug("Notifying of %d removed emails since last opened",
                   removed_ids.size);
-            notify_email_removed(removed_ids);
+            email_removed(removed_ids);
 
             count_change_reason |= Folder.CountChangeReason.REMOVED;
         }
 
+        // notify created (new email located somewhere inside the
+        // local vector that had to be created, i.e. no portion was
+        // stored locally)
+        if (created_ids.size > 0) {
+            debug("Notifying of %d added emails since last opened",
+                  created_ids.size);
+            this.account.email_added(created_ids, this);
+        }
+
         // notify inserted (new email located somewhere inside the local vector)
         if (inserted_ids.size > 0) {
             debug("Notifying of %d inserted emails since last opened",
                   inserted_ids.size);
-            notify_email_inserted(inserted_ids);
-
-            count_change_reason |= Folder.CountChangeReason.INSERTED;
-        }
-
-        // notify inserted (new email located somewhere inside the local vector that had to be
-        // created, i.e. no portion was stored locally)
-        if (locally_inserted_ids.size > 0) {
-            debug("Notifying of %d locally inserted emails since last opened",
-                  locally_inserted_ids.size);
-            notify_email_locally_inserted(locally_inserted_ids);
-
+            email_inserted(inserted_ids);
             count_change_reason |= Folder.CountChangeReason.INSERTED;
         }
 
@@ -713,25 +679,14 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         if (appended_ids.size > 0) {
             debug("Notifying of %d appended emails since last opened",
                   appended_ids.size);
-            notify_email_appended(appended_ids);
-
-            count_change_reason |= Folder.CountChangeReason.APPENDED;
-        }
-
-        // notify locally appended (new email never seen before added
-        // since the folder was last opened)
-        if (locally_appended_ids.size > 0) {
-            debug("Notifying of %d locally appended emails since last opened",
-                  locally_appended_ids.size);
-            notify_email_locally_appended(locally_appended_ids);
-
+            email_appended(appended_ids);
             count_change_reason |= Folder.CountChangeReason.APPENDED;
         }
 
         if (count_change_reason != Folder.CountChangeReason.NONE) {
             debug("Notifying of %Xh count change reason (%d remote messages)",
                   count_change_reason, remote_message_count);
-            notify_email_count_changed(remote_message_count, count_change_reason);
+            email_count_changed(remote_message_count, count_change_reason);
         }
 
         debug("Completed normalize_folder");
@@ -813,7 +768,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             this._properties.remove(session.folder.properties);
             yield this._account.release_folder_session(session);
 
-            notify_closed(remote_reason);
+            closed(remote_reason);
         }
     }
 
@@ -853,10 +808,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         this.email_prefetcher.open();
 
         // notify about the local open
-        notify_opened(
-            Geary.Folder.OpenState.LOCAL,
-            this.local_folder.get_properties().email_total
-        );
+        opened(LOCAL, this.local_folder.get_properties().email_total);
 
         // Unless NO_DELAY is set, do NOT open the remote side here;
         // wait for a folder session to be claimed ... this allows for
@@ -980,8 +932,8 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
 
         // need to call these every time, even if remote was not fully
         // opened, as some callers rely on order of signals
-        notify_closed(local_reason);
-        notify_closed(CloseReason.FOLDER_CLOSED);
+        closed(local_reason);
+        closed(CloseReason.FOLDER_CLOSED);
 
         // Notify waiting tasks
         this.closed_semaphore.blind_notify();
@@ -1059,7 +1011,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             } else {
                 debug("Recoverable error opening remote: %s",
                       context.format_full_error());
-                notify_open_failed(Folder.OpenFailed.REMOTE_ERROR, err);
+                open_failed(Folder.OpenFailed.REMOTE_ERROR, err);
             }
             return;
         }
@@ -1095,9 +1047,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
                 Folder.CloseReason local_reason = CloseReason.LOCAL_ERROR;
                 Folder.CloseReason remote_reason = CloseReason.REMOTE_CLOSE;
                 if (!is_remote_error(err)) {
-                    notify_open_failed(OpenFailed.LOCAL_ERROR, err);
+                    open_failed(OpenFailed.LOCAL_ERROR, err);
                 } else {
-                    notify_open_failed(OpenFailed.REMOTE_ERROR, err);
+                    open_failed(OpenFailed.REMOTE_ERROR, err);
                     local_reason =  CloseReason.LOCAL_CLOSE;
                     remote_reason = CloseReason.REMOTE_ERROR;
                 }
@@ -1118,7 +1070,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
             // problem, so handle as per above.
             yield this._account.release_folder_session(session);
             if (!(err is IOError.CANCELLED)) {
-                notify_open_failed(Folder.OpenFailed.LOCAL_ERROR, err);
+                open_failed(Folder.OpenFailed.LOCAL_ERROR, err);
                 yield force_close(CloseReason.LOCAL_ERROR, CloseReason.REMOTE_CLOSE);
             }
             return;
@@ -1138,10 +1090,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         // Phase 3: Notify tasks waiting for the connection
 
         // notify any subscribers with similar information
-        notify_opened(
-            Geary.Folder.OpenState.REMOTE,
-            session.folder.properties.email_total
-        );
+        opened(REMOTE, session.folder.properties.email_total);
 
         // notify any threads of execution waiting for the remote
         // folder to open that the result of that operation is ready
@@ -1154,7 +1103,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
     }
 
     private void on_email_complete(Gee.Collection<Geary.EmailIdentifier> email_ids) {
-        notify_email_locally_complete(email_ids);
+        this.account.email_complete(email_ids);
     }
 
     private void on_remote_appended(Imap.FolderSession session, int appended) {
@@ -1173,11 +1122,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         if (positions.size > 0) {
             // We don't pass in open_cancellable here since we want
             // the op to still run when closing and flushing the queue
-            ReplayAppend op = new ReplayAppend(this, remote_count, positions, null);
-            op.email_appended.connect(notify_email_appended);
-            op.email_locally_appended.connect(notify_email_locally_appended);
-            op.email_count_changed.connect(notify_email_count_changed);
-            this.replay_queue.schedule_server_notification(op);
+            this.replay_queue.schedule_server_notification(
+                new ReplayAppend(this, remote_count, positions, null)
+            );
         }
     }
 
@@ -1206,11 +1153,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         // notify of removal to all pending replay operations
         replay_queue.notify_remote_removed_position(position);
 
-        ReplayRemoval op = new ReplayRemoval(this, remote_count, position);
-        op.email_removed.connect(notify_email_removed);
-        op.marked_email_removed.connect(notify_marked_email_removed);
-        op.email_count_changed.connect(notify_email_count_changed);
-        this.replay_queue.schedule_server_notification(op);
+        this.replay_queue.schedule_server_notification(
+            new ReplayRemoval(this, remote_count, position)
+        );
     }
 
     /** {@inheritDoc} */
@@ -1544,11 +1489,6 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
         return op.created_id;
     }
 
-    /** Fires a {@link marked_email_removed} signal for this folder. */
-    protected virtual void notify_marked_email_removed(Gee.Collection<Geary.EmailIdentifier> removed) {
-        marked_email_removed(removed);
-    }
-
     private inline void notify_remote_waiters(bool successful) {
         try {
             this.remote_wait_semaphore.notify_result(successful, null);
@@ -1610,8 +1550,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
                     changed_map.set(e.id, e.email_flags);
             }
 
-            if (!cancellable.is_cancelled() && changed_map.size > 0)
-                notify_email_flags_changed(changed_map);
+            if (!cancellable.is_cancelled() && changed_map.size > 0) {
+                email_flags_changed(changed_map);
+            }
 
             chunk_size *= 2;
             if (chunk_size > FLAG_UPDATE_MAX_CHUNK) {
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 0d04eb475..48982ca14 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
@@ -214,8 +214,8 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
 
         // signal
         if (created_ids.size > 0) {
-            owner.replay_notify_email_inserted(created_ids);
-            owner.replay_notify_email_locally_inserted(created_ids);
+            this.owner.account.email_added(created_ids, this.owner);
+            this.owner.email_inserted(created_ids);
         }
     }
 
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 6c9f67112..11b0bc8ed 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
@@ -34,11 +34,12 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
         // if local folder is not empty, report all as being removed
         if (removed_ids != null) {
             if (removed_ids.size > 0)
-                engine.replay_notify_email_removed(removed_ids);
+                engine.email_removed(removed_ids);
 
             int new_count = Numeric.int_floor(original_count - removed_ids.size, 0);
-            if (new_count != original_count)
-                engine.replay_notify_email_count_changed(new_count, Geary.Folder.CountChangeReason.REMOVED);
+            if (new_count != original_count) {
+                engine.email_count_changed(new_count, REMOVED);
+            }
         }
 
         return ReplayOperation.Status.CONTINUE;
@@ -60,10 +61,10 @@ private class Geary.ImapEngine.EmptyFolder : Geary.ImapEngine.SendReplayOperatio
     public override async void backout_local_async() throws Error {
         if (removed_ids != null && removed_ids.size > 0) {
             yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
-            engine.replay_notify_email_inserted(removed_ids);
+            engine.email_inserted(removed_ids);
         }
 
-        engine.replay_notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
+        engine.email_count_changed(original_count, INSERTED);
     }
 
     public override string describe_state() {
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 50584097d..ed0c1284d 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
@@ -121,10 +121,11 @@ private class Geary.ImapEngine.FetchEmail : Geary.ImapEngine.SendReplayOperation
 
         Geary.Email email = list[0];
         if (created_or_merged.get(email)) {
-            Gee.Collection<Geary.EmailIdentifier> ids
-                = Geary.iterate<Geary.EmailIdentifier>(email.id).to_array_list();
-            engine.replay_notify_email_inserted(ids);
-            engine.replay_notify_email_locally_inserted(ids);
+            var ids = Geary.iterate<Geary.EmailIdentifier>(
+                email.id
+            ).to_array_list();
+            engine.account.email_added(ids, this.engine);
+            engine.email_inserted(ids);
         }
 
         // Finally, pull again from the local database, to get the
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 b48e8edbf..0f93592e0 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
@@ -52,7 +52,7 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
         Gee.Map<EmailIdentifier, Geary.EmailFlags>? map = yield engine.local_folder.get_email_flags_async(
             original_flags.keys, cancellable);
         if (map != null && map.size > 0)
-            engine.replay_notify_email_flags_changed(map);
+            engine.email_flags_changed(map);
 
         return ReplayOperation.Status.CONTINUE;
     }
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 5652fcd45..b8941b5f1 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
@@ -99,8 +99,8 @@ private class Geary.ImapEngine.MoveEmailCommit : Geary.ImapEngine.SendReplayOper
         if (count < 0) {
             count = 0;
         }
-        engine.replay_notify_email_inserted(to_move);
-        engine.replay_notify_email_count_changed(count + to_move.size, Folder.CountChangeReason.INSERTED);
+        engine.email_inserted(to_move);
+        engine.email_count_changed(count + to_move.size, INSERTED);
     }
 
     public override string describe_state() {
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 087dc2022..71930606d 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
@@ -47,13 +47,13 @@ private class Geary.ImapEngine.MoveEmailPrepare : Geary.ImapEngine.SendReplayOpe
         if (prepared_for_move == null || prepared_for_move.size == 0)
             return ReplayOperation.Status.COMPLETED;
 
-        engine.replay_notify_email_removed(prepared_for_move);
-
-        engine.replay_notify_email_count_changed(
+        engine.email_removed(prepared_for_move);
+        engine.email_count_changed(
             Numeric.int_floor(count - prepared_for_move.size, 0),
-            Folder.CountChangeReason.REMOVED);
+            REMOVED
+        );
 
-        return ReplayOperation.Status.COMPLETED;
+        return COMPLETED;
     }
 
     public override string describe_state() {
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 5cecd822f..df66828e7 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
@@ -41,11 +41,10 @@ private class Geary.ImapEngine.MoveEmailRevoke : Geary.ImapEngine.SendReplayOper
             count = 0;
         }
 
-        engine.replay_notify_email_inserted(revoked);
-        engine.replay_notify_email_count_changed(count + revoked.size,
-            Geary.Folder.CountChangeReason.INSERTED);
+        engine.email_inserted(revoked);
+        engine.email_count_changed(count + revoked.size, INSERTED);
 
-        return ReplayOperation.Status.COMPLETED;
+        return COMPLETED;
     }
 
     public override string describe_state() {
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 de86ab710..d20c76b48 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
@@ -41,12 +41,14 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
         if (removed_ids == null || removed_ids.size == 0)
             return ReplayOperation.Status.COMPLETED;
 
-        engine.replay_notify_email_removed(removed_ids);
+        engine.email_removed(removed_ids);
 
-        engine.replay_notify_email_count_changed(Numeric.int_floor(original_count - removed_ids.size, 0),
-            Geary.Folder.CountChangeReason.REMOVED);
+        engine.email_count_changed(
+            Numeric.int_floor(original_count - removed_ids.size, 0),
+            REMOVED
+        );
 
-        return ReplayOperation.Status.CONTINUE;
+        return CONTINUE;
     }
 
     public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
@@ -70,10 +72,9 @@ private class Geary.ImapEngine.RemoveEmail : Geary.ImapEngine.SendReplayOperatio
     public override async void backout_local_async() throws Error {
         if (removed_ids != null && removed_ids.size > 0) {
             yield engine.local_folder.mark_removed_async(removed_ids, false, cancellable);
-            engine.replay_notify_email_inserted(removed_ids);
+            engine.email_inserted(removed_ids);
         }
-
-        engine.replay_notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
+        engine.email_count_changed(original_count, INSERTED);
     }
 
     public override string describe_state() {
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 720ca0c0e..f79c91056 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
@@ -11,10 +11,6 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
     private Gee.List<Imap.SequenceNumber> positions;
     private Cancellable? cancellable;
 
-    public signal void email_appended(Gee.Collection<Geary.EmailIdentifier> ids);
-    public signal void email_locally_appended(Gee.Collection<Geary.EmailIdentifier> ids);
-    public signal void email_count_changed(int count, Folder.CountChangeReason reason);
-
 
     public ReplayAppend(MinimalFolder owner,
                         int remote_count,
@@ -122,13 +118,16 @@ private class Geary.ImapEngine.ReplayAppend : Geary.ImapEngine.ReplayOperation {
             this.remote_count, this.cancellable
         );
 
-        if (appended.size > 0)
-            email_appended(appended);
+        if (appended.size > 0) {
+            this.owner.email_appended(appended);
+        }
 
-        if (created.size > 0)
-            email_locally_appended(created);
+        if (created.size > 0) {
+            this.owner.account.email_added(created, this.owner);
+            this.owner.email_appended(created);
+        }
 
-        email_count_changed(this.remote_count, Folder.CountChangeReason.APPENDED);
+        this.owner.email_count_changed(this.remote_count, Folder.CountChangeReason.APPENDED);
 
         debug("%s do_replay_appended_message: completed, this.remote_count=%d",
               to_string(), this.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 60f186e1b..3f5efca6d 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,10 +10,6 @@ private class Geary.ImapEngine.ReplayRemoval : Geary.ImapEngine.ReplayOperation
     private int remote_count;
     private Imap.SequenceNumber position;
 
-    public signal void email_removed(Gee.Collection<Geary.EmailIdentifier> ids);
-    public signal void marked_email_removed(Gee.Collection<Geary.EmailIdentifier> ids);
-    public signal void email_count_changed(int count, Folder.CountChangeReason reason);
-
 
     public ReplayRemoval(MinimalFolder owner, int remote_count, Imap.SequenceNumber position) {
         // Although technically a local-only operation, must treat as
@@ -133,13 +129,13 @@ private class Geary.ImapEngine.ReplayRemoval : Geary.ImapEngine.ReplayOperation
         if (owned_id != null) {
             Gee.List<EmailIdentifier> removed = 
Geary.iterate<Geary.EmailIdentifier>(owned_id).to_array_list();
             if (!marked)
-                email_removed(removed);
+                this.owner.email_removed(removed);
             else
-                marked_email_removed(removed);
+                this.owner.marked_email_removed(removed);
         }
 
         if (!marked) {
-            this.owner.replay_notify_email_count_changed(
+            this.owner.email_count_changed(
                 this.remote_count, Folder.CountChangeReason.REMOVED
             );
         }
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 2a75f07ec..f6a58dee8 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
@@ -66,7 +66,7 @@ private class Geary.ImapEngine.ReplayUpdate : Geary.ImapEngine.ReplayOperation {
                     yield this.owner.local_folder.fetch_email_async(
                         id, NONE, NONE, null
                     );
-                    this.owner.replay_notify_email_flags_changed(changed_map);
+                    this.owner.email_flags_changed(changed_map);
                 } catch (EngineError.NOT_FOUND err) {
                     //fine
                 }
diff --git a/src/engine/outbox/outbox-folder.vala b/src/engine/outbox/outbox-folder.vala
index 51c3c7b2a..71b82ee3d 100644
--- a/src/engine/outbox/outbox-folder.vala
+++ b/src/engine/outbox/outbox-folder.vala
@@ -140,9 +140,9 @@ public class Geary.Outbox.Folder :
         Gee.List<EmailIdentifier> list = new Gee.ArrayList<EmailIdentifier>();
         list.add(row.outbox_id);
 
-        notify_email_appended(list);
-        notify_email_locally_appended(list);
-        notify_email_count_changed(email_count, CountChangeReason.APPENDED);
+        this.account.email_added(list, this);
+        email_inserted(list);
+        email_count_changed(email_count, CountChangeReason.APPENDED);
 
         return row.outbox_id;
     }
@@ -169,7 +169,7 @@ public class Geary.Outbox.Folder :
             }
         }
 
-        notify_email_flags_changed(changed);
+        email_flags_changed(changed);
     }
 
     public virtual async void
@@ -205,8 +205,9 @@ public class Geary.Outbox.Folder :
         if (removed.size >= 0) {
             _properties.set_total(final_count);
 
-            notify_email_removed(removed);
-            notify_email_count_changed(final_count, CountChangeReason.REMOVED);
+            email_removed(removed);
+            email_count_changed(final_count, REMOVED);
+            this.account.email_removed(removed);
         }
     }
 
diff --git a/test/engine/app/app-conversation-monitor-test.vala 
b/test/engine/app/app-conversation-monitor-test.vala
index ca8dd3477..22fd76285 100644
--- a/test/engine/app/app-conversation-monitor-test.vala
+++ b/test/engine/app/app-conversation-monitor-test.vala
@@ -373,13 +373,13 @@ class Geary.App.ConversationMonitorTest : TestCase {
             .returns_object(paths);
 
         // Should not be added, since it's actually in the base folder
-        this.account.email_appended(
+        this.account.email_appended_to_folder(
             this.base_folder,
             new Gee.ArrayList<EmailIdentifier>.wrap({e2.id})
         );
 
         // Should be added, since it's an external message
-        this.account.email_appended(
+        this.account.email_appended_to_folder(
             this.other_folder,
             new Gee.ArrayList<EmailIdentifier>.wrap({e3.id})
         );
@@ -411,7 +411,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
         Gee.HashMap<EmailIdentifier,EmailFlags> flags_changed =
             new Gee.HashMap<EmailIdentifier,EmailFlags>();
         flags_changed.set(e1.id, new EmailFlags.with(EmailFlags.DELETED));
-        this.account.email_flags_changed(this.base_folder, flags_changed);
+        this.account.email_flags_changed_in_folder(this.base_folder, flags_changed);
 
         this.base_folder.expect_call("list_email_by_sparse_id_async");
         this.base_folder.expect_call("list_email_by_id_async");


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