[geary/mjog/mail-merge-plugin: 50/72] Geary.Account: Add {de}register_local_folder methods




commit df7a30cbe15e5c6474f3da3cb403d09ff81bb310
Author: Michael Gratton <mike vee net>
Date:   Wed Aug 5 14:10:11 2020 +1000

    Geary.Account: Add {de}register_local_folder methods
    
    Support API clients registering their own local folder implementations.
    Use this (and the last commit) to generalise handling of the outbox by
    GenericAccount by registering it when the outbox postie is started, and
    when creating a map of folders that contain specific ids.
    
    This also ensures API clients are informed of the outbox becoming
    available, allowing some special case code to be removed from the app
    controller.

 src/client/application/application-controller.vala |   2 -
 src/engine/api/geary-account.vala                  |  36 +++++++
 .../imap-engine/imap-engine-generic-account.vala   | 116 +++++++++++++++++----
 src/engine/outbox/outbox-folder.vala               |  23 ----
 test/mock/mock-account.vala                        |  12 +++
 5 files changed, 142 insertions(+), 47 deletions(-)
---
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index 473f18f71..38b1bfeae 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -976,8 +976,6 @@ internal class Application.Controller :
             smtp.email_sent.connect(on_sent);
             smtp.sending_monitor.start.connect(on_sending_started);
             smtp.sending_monitor.finish.connect(on_sending_finished);
-            var outbox_context = new FolderContext(smtp.outbox);
-            context.add_folders(Geary.Collection.single(outbox_context));
         }
 
         // Notify before opening so that listeners have a chance to
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 4a4850a57..d81661cae 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -434,6 +434,42 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error;
 
+    /**
+     * Registers a local folder with the account.
+     *
+     * The registering a local folder will cause the account will hook
+     * to the folder's signals such as {@link Folder.email_appended}
+     * and forward them on to the account-wide equivalents, include
+     * the list folder in the folder list, allow email in the folder
+     * to be found by account-wide operations, and so on. The folder
+     * will then be signalled as being available via {@link
+     * folders_available_unavailable}.
+     *
+     * A {@link EngineError.ALREADY_EXISTS} exception will be thrown
+     * if the given folder is already registered, or {@link
+     * EngineError.NOT_FOUND} if its path does not have {@link
+     * local_folder_root} as its root.
+     *
+     * @see deregister_local_folder
+     */
+    public abstract void register_local_folder(Folder local)
+        throws GLib.Error;
+
+    /**
+     * De-registers a local folder with the account.
+     *
+     * De-registering a previously registered local folder will signal
+     * it as being unavailable via {@link
+     * folders_available_unavailable} and unhook it from the account.
+     *
+     * A {@link local_folder_root} error will be thrown if the given
+     * folder is not already registered.
+     *
+     * @see register_local_folder
+     */
+    public abstract void deregister_local_folder(Folder local)
+        throws GLib.Error;
+
     /**
      * Search the local account for emails referencing a Message-ID value
      * (which can appear in the Message-ID header itself, as well as the
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 3b8536c37..8285af57a 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -48,8 +48,10 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
     private Cancellable? open_cancellable = null;
     private Nonblocking.Semaphore? remote_ready_lock = null;
 
-    private Gee.Map<FolderPath,MinimalFolder> folder_map =
+    private Gee.Map<FolderPath,MinimalFolder> remote_folders =
         new Gee.HashMap<FolderPath,MinimalFolder>();
+    private Gee.Map<FolderPath,Folder> local_folders =
+        new Gee.HashMap<FolderPath,Folder>();
 
     private AccountProcessor? processor;
     private AccountSynchronizer sync;
@@ -152,7 +154,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         // outgoing so local folders can be loaded first in case
         // queued mail gets sent and needs to get saved somewhere.
         yield this.imap.start(cancellable);
-        this.queue_operation(new StartPostie(this));
+        this.queue_operation(new StartPostie(this, this.smtp.outbox));
 
         // Kick off a background update of the search table.
         //
@@ -185,13 +187,16 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         this.imap.discard_returned_sessions = true;
         this.remote_ready_lock.reset();
 
-        // Close folders and ensure they do in fact close
+        // Notify folders are going away and wait for remotes to close
 
-        Gee.BidirSortedSet<Folder> remotes = sort_by_path(this.folder_map.values);
-        this.folder_map.clear();
-        notify_folders_available_unavailable(null, remotes);
+        var locals = sort_by_path(this.local_folders.values);
+        this.local_folders.clear();
+        notify_folders_available_unavailable(null, locals);
 
-        foreach (Geary.Folder folder in remotes) {
+        var remotes = sort_by_path(this.remote_folders.values);
+        this.remote_folders.clear();
+        notify_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();
         }
@@ -402,7 +407,12 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
     /** {@inheritDoc} */
     public override Folder get_folder(FolderPath path)
         throws EngineError.NOT_FOUND {
-        Folder? folder = this.folder_map.get(path);
+        Folder? folder = null;
+        if (this.local.imap_folder_root.is_descendant(path)) {
+            folder = this.remote_folders.get(path);
+        } else if (this.local_folder_root.is_descendant(path)) {
+            folder = this.local_folders.get(path);
+        }
         if (folder == null) {
             throw new EngineError.NOT_FOUND(
                 "Folder not found: %s", path.to_string()
@@ -414,21 +424,37 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
     /** {@inheritDoc} */
     public override Gee.Collection<Folder> list_folders() {
         var all = new Gee.HashSet<Folder>();
-        all.add_all(this.folder_map.values);
+        all.add_all(this.remote_folders.values);
+        all.add_all(this.local_folders.values);
         return all;
     }
 
     /** {@inheritDoc} */
     public override Gee.Collection<Folder> list_matching_folders(FolderPath? parent)
         throws EngineError.NOT_FOUND {
-        return traverse<FolderPath>(folder_map.keys)
+        Gee.Map<FolderPath,Folder>? folders = null;
+        if (this.local.imap_folder_root.is_descendant(parent)) {
+            folders = this.remote_folders;
+        } else if (this.local_folder_root.is_descendant(parent)) {
+            folders = this.local_folders;
+        } else {
+            throw new EngineError.NOT_FOUND(
+                "Unknown folder root: %s", parent.to_string()
+            );
+        }
+        if (!folders.has_key(parent)) {
+            throw new EngineError.NOT_FOUND(
+                "Unknown parent: %s", parent.to_string()
+            );
+        }
+        return traverse<FolderPath>(folders.keys)
             .filter(p => {
                 FolderPath? path_parent = p.parent;
                 return ((parent == null && path_parent == null) ||
                     (parent != null && path_parent != null &&
                      path_parent.equal_to(parent)));
             })
-            .map<Geary.Folder>(p => folder_map.get(p))
+            .map<Geary.Folder>(p => folders.get(p))
             .to_array_list();
     }
 
@@ -466,7 +492,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         FolderPath root =
             yield remote.get_default_personal_namespace(cancellable);
         FolderPath path = root.get_child(name);
-        if (this.folder_map.has_key(path)) {
+        if (this.remote_folders.has_key(path)) {
             throw new EngineError.ALREADY_EXISTS(
                 "Folder already exists: %s", path.to_string()
             );
@@ -481,7 +507,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             remote_folder, cancellable
         );
         add_folders(Collection.single(local_folder), false);
-        var folder = this.folder_map.get(path);
+        var folder = this.remote_folders.get(path);
         if (use != NONE) {
             promote_folders(
                 Collection.single_map<Folder.SpecialUse,Folder>(use, folder)
@@ -490,6 +516,41 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         return folder;
     }
 
+    /** {@inheritDoc} */
+    public override void register_local_folder(Folder local)
+        throws GLib.Error {
+            var path = local.path;
+        if (this.local_folders.has_key(path)) {
+            throw new EngineError.ALREADY_EXISTS(
+                "Folder already exists: %s", path.to_string()
+            );
+        }
+        if (!this.local_folder_root.is_descendant(path)) {
+            throw new EngineError.NOT_FOUND(
+                "Not a desendant of the local folder root: %s", path.to_string()
+            );
+        }
+        this.local_folders.set(path, local);
+        notify_folders_available_unavailable(
+            sort_by_path(Collection.single(local)), null
+        );
+    }
+
+    /** {@inheritDoc} */
+    public override void deregister_local_folder(Folder local)
+        throws GLib.Error {
+        var path = local.path;
+        if (!this.local_folders.has_key(path)) {
+            throw new EngineError.NOT_FOUND(
+                "Unknown folder: %s", path.to_string()
+            );
+        }
+        notify_folders_available_unavailable(
+            null, sort_by_path(Collection.single(local))
+        );
+        this.local_folders.unset(path);
+    }
+
     private ImapDB.EmailIdentifier check_id(Geary.EmailIdentifier id) throws EngineError {
         ImapDB.EmailIdentifier? imapdb_id = id as ImapDB.EmailIdentifier;
         if (imapdb_id == null)
@@ -563,7 +624,13 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         Gee.MultiMap<EmailIdentifier,FolderPath> map =
             new Gee.HashMultiMap<EmailIdentifier,FolderPath>();
         yield this.local.get_containing_folders_async(ids, map, cancellable);
-        yield this.smtp.outbox.add_to_containing_folders_async(ids, map, cancellable);
+        foreach (var folder in this.local_folders.values) {
+            var path = folder.path;
+            var matching = yield folder.contains_identifiers(ids, cancellable);
+            foreach (var id in matching) {
+                map.set(id, path);
+            }
+        }
         return (map.size == 0) ? null : map;
     }
 
@@ -608,7 +675,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         );
         foreach(ImapDB.Folder db_folder in db_folders) {
             FolderPath path = db_folder.get_path();
-            if (!this.folder_map.has_key(path)) {
+            if (!this.remote_folders.has_key(path)) {
                 MinimalFolder folder = new_folder(db_folder);
                 folder.report_problem.connect(notify_report_problem);
                 if (folder.used_as == NONE) {
@@ -618,7 +685,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
                     }
                 }
                 built_folders.add(folder);
-                this.folder_map.set(folder.path, folder);
+                this.remote_folders.set(folder.path, folder);
             }
         }
 
@@ -699,9 +766,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             Account.folder_path_comparator
         );
         foreach(Geary.Folder folder in folders) {
-            MinimalFolder? impl = this.folder_map.get(folder.path);
+            MinimalFolder? impl = this.remote_folders.get(folder.path);
             if (impl != null) {
-                this.folder_map.unset(folder.path);
+                this.remote_folders.unset(folder.path);
                 removed.add(impl);
             }
         }
@@ -742,7 +809,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
                 Gee.List<string> search_names = special_search_names.get(use);
                 foreach (string search_name in search_names) {
                     FolderPath search_path = root.get_child(search_name);
-                    foreach (FolderPath test_path in folder_map.keys) {
+                    foreach (FolderPath test_path in this.remote_folders.keys) {
                         if (test_path.compare_normalized_ci(search_path) == 0) {
                             path = search_path;
                             break;
@@ -764,8 +831,8 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
                 );
             }
 
-            if (this.folder_map.has_key(path)) {
-                special = this.folder_map.get(path);
+            if (this.remote_folders.has_key(path)) {
+                special = this.remote_folders.get(path);
                 promote_folders(
                     Collection.single_map<Folder.SpecialUse,Folder>(use, special)
                 );
@@ -1077,12 +1144,17 @@ internal class Geary.ImapEngine.LoadFolders : AccountOperation {
 internal class Geary.ImapEngine.StartPostie : AccountOperation {
 
 
-    internal StartPostie(Account account) {
+    private Outbox.Folder outbox;
+
+
+    internal StartPostie(Account account, Outbox.Folder outbox) {
         base(account);
+        this.outbox = outbox;
     }
 
     public override async void execute(GLib.Cancellable cancellable)
         throws GLib.Error {
+        this.account.register_local_folder(this.outbox);
         yield this.account.outgoing.start(cancellable);
     }
 
diff --git a/src/engine/outbox/outbox-folder.vala b/src/engine/outbox/outbox-folder.vala
index 9fb84c2b8..51c3c7b2a 100644
--- a/src/engine/outbox/outbox-folder.vala
+++ b/src/engine/outbox/outbox-folder.vala
@@ -383,29 +383,6 @@ public class Geary.Outbox.Folder :
         throw new EngineError.UNSUPPORTED("Folder special use cannot be changed");
     }
 
-    internal async void
-        add_to_containing_folders_async(Gee.Collection<Geary.EmailIdentifier> ids,
-                                        Gee.MultiMap<Geary.EmailIdentifier,FolderPath> map,
-                                        GLib.Cancellable? cancellable)
-        throws GLib.Error {
-        check_open();
-        yield db.exec_transaction_async(Db.TransactionType.RO, (cx, cancellable) => {
-            foreach (Geary.EmailIdentifier id in ids) {
-                EmailIdentifier? outbox_id = id as EmailIdentifier;
-                if (outbox_id == null)
-                    continue;
-
-                OutboxRow? row = do_fetch_row_by_ordering(cx, outbox_id.ordering, cancellable);
-                if (row == null)
-                    continue;
-
-                map.set(id, path);
-            }
-
-            return Db.TransactionOutcome.DONE;
-        }, cancellable);
-    }
-
     // Utility for getting an email object back from an outbox row.
     private Geary.Email row_to_email(OutboxRow row) throws Error {
         Geary.Email? email = null;
diff --git a/test/mock/mock-account.vala b/test/mock/mock-account.vala
index 7d598a0aa..173b2ecae 100644
--- a/test/mock/mock-account.vala
+++ b/test/mock/mock-account.vala
@@ -88,6 +88,18 @@ public class Mock.Account : Geary.Account,
         );
     }
 
+    /** {@inheritDoc} */
+    public override void register_local_folder(Geary.Folder local)
+        throws GLib.Error {
+        void_call("register_local_folder", { local });
+    }
+
+    /** {@inheritDoc} */
+    public override void deregister_local_folder(Geary.Folder local)
+        throws GLib.Error {
+        void_call("deregister_local_folder", { local });
+    }
+
     public override Geary.EmailIdentifier to_email_identifier(GLib.Variant serialised)
         throws Geary.EngineError.BAD_PARAMETERS {
         try {


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