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




commit f24b65c333246391cebedb495b277816e2d990a9
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   | 120 +++++++++++++++++----
 src/engine/outbox/outbox-folder.vala               |  23 ----
 test/engine/api/geary-account-mock.vala            |  12 +++
 5 files changed, 146 insertions(+), 47 deletions(-)
---
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index 28f70c740..f784dbd1c 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -996,8 +996,6 @@ internal class Application.Controller : Geary.BaseObject {
             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 ad1f87a44..6363c2d98 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)
                 );
@@ -979,6 +1046,10 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             unloc_names.add("Archive | Archives");
 
             break;
+
+        default:
+            // noop
+            break;
         }
 
         loc_names.add_all(unloc_names);
@@ -1073,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/engine/api/geary-account-mock.vala b/test/engine/api/geary-account-mock.vala
index d7fb167e7..c22be6185 100644
--- a/test/engine/api/geary-account-mock.vala
+++ b/test/engine/api/geary-account-mock.vala
@@ -121,6 +121,18 @@ public class Geary.MockAccount : Account,
         );
     }
 
+    /** {@inheritDoc} */
+    public override void register_local_folder(Folder local)
+        throws GLib.Error {
+        void_call("register_local_folder", { local });
+    }
+
+    /** {@inheritDoc} */
+    public override void deregister_local_folder(Folder local)
+        throws GLib.Error {
+        void_call("deregister_local_folder", { local });
+    }
+
     public override EmailIdentifier to_email_identifier(GLib.Variant serialised)
         throws EngineError.BAD_PARAMETERS {
         try {


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