[geary/mjog/493-undo-send: 15/20] Update composer draft lifecycle management



commit c3446e73ec895f8c4616aad546836bcbdcf35f08
Author: Michael Gratton <mike vee net>
Date:   Tue Nov 12 14:21:37 2019 +1100

    Update composer draft lifecycle management
    
    Make Geary.App.DraftManager's ::update and ::discard methods async and
    drop the ::discard_on_close property. Update all Composer.Widget draft
    methods to be aync, drop the `_async` prefix, take a cancellable arg and
    throw their errors rather than logging them. Update call sites to take
    this into account and report errors rather than logging them.

 src/client/composer/composer-widget.vala | 154 +++++++++++++++++++------------
 src/engine/app/app-draft-manager.vala    |  47 ++--------
 2 files changed, 104 insertions(+), 97 deletions(-)
---
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index d038c45f..93b6a799 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -556,7 +556,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
         this.from = new Geary.RFC822.MailboxAddresses.single(account.information.primary_mailbox);
 
         this.draft_timer = new Geary.TimeoutManager.seconds(
-            10, () => { this.save_draft.begin(); }
+            10, on_draft_timeout
         );
 
         // Add actions once every element has been initialized and added
@@ -697,10 +697,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
         );
 
         try {
-            yield open_draft_manager_async(
-                is_draft ? referred.id : null,
-                cancellable
-            );
+            yield open_draft_manager(is_draft ? referred.id : null, cancellable);
         } catch (Error e) {
             debug("Could not open draft manager: %s", e.message);
         }
@@ -827,7 +824,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
         if (this.draft_manager != null) {
             try {
-                yield close_draft_manager_async(null);
+                yield close_draft_manager(null);
             } catch (Error err) {
                 debug("Error closing draft manager on composer close");
             }
@@ -867,7 +864,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
         if (enabled) {
             this.is_closing = false;
-            this.open_draft_manager_async.begin(null, null);
+            this.open_draft_manager.begin(null, null);
         } else {
             if (this.container != null) {
                 this.container.close();
@@ -1447,33 +1444,32 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
     private async void on_send_async() {
         set_enabled(false);
 
-        // Perform send.
         try {
             yield this.editor.clean_content();
             yield this.application.controller.send_email(this);
-        } catch (Error e) {
-            GLib.message("Error sending email: %s", e.message);
-        }
 
-        Geary.Nonblocking.Semaphore? semaphore = discard_draft();
-        if (semaphore != null) {
-            try {
-                yield semaphore.wait_async();
-            } catch (Error err) {
-                // ignored
+            if (this.draft_manager != null) {
+                yield discard_draft();
+                yield close_draft_manager(null);
             }
-        }
 
-        if (this.container != null) {
-            this.container.close();
+            if (this.container != null) {
+                this.container.close();
+            }
+        } catch (GLib.Error error) {
+            this.application.controller.report_problem(
+                new Geary.AccountProblemReport(
+                    this.account.information, error
+                )
+            );
         }
     }
 
     /**
      * Creates and opens the composer's draft manager.
      */
-    private async void open_draft_manager_async(Geary.EmailIdentifier? editing_draft_id,
-                                                GLib.Cancellable? cancellable)
+    private async void open_draft_manager(Geary.EmailIdentifier? editing_draft_id,
+                                          GLib.Cancellable? cancellable)
         throws GLib.Error {
         if (!this.account.information.save_drafts) {
             this.header.show_save_and_close = false;
@@ -1520,19 +1516,19 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
     /**
      * Closes current draft manager, if any, then opens a new one.
      */
-    private async void reopen_draft_manager_async()
-    throws Error {
+    private async void reopen_draft_manager(GLib.Cancellable? cancellable)
+        throws GLib.Error {
         if (this.draft_manager != null) {
             // Discard the draft, if any, since it may be on a
             // different account
-            discard_draft();
-            this.draft_manager.discard_on_close = true;
-            yield close_draft_manager_async(null);
+            yield discard_draft();
+            yield close_draft_manager(cancellable);
         }
-        yield open_draft_manager_async(null, null);
+        yield open_draft_manager(null, cancellable);
+        yield save_draft();
     }
 
-    private async void close_draft_manager_async(Cancellable? cancellable)
+    private async void close_draft_manager(GLib.Cancellable? cancellable)
         throws GLib.Error {
         this.draft_status_text = "";
 
@@ -1547,12 +1543,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
             .disconnect(on_draft_id_changed);
         old_manager.fatal.disconnect(on_draft_manager_fatal);
 
-        // drop ref even if close failed
-        try {
-            yield old_manager.close_async(cancellable);
-        } catch (Error err) {
-            debug("Error closing draft manager: %s", err.message);
-        }
+        yield old_manager.close_async(cancellable);
         debug("Draft manager closed");
     }
 
@@ -1594,51 +1585,62 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
     }
 
     // Note that drafts are NOT "linkified."
-    private async void save_draft() {
+    private async void save_draft() throws GLib.Error {
+        debug("Saving draft");
+
         // cancel timer in favor of just doing it now
         this.draft_timer.reset();
 
         if (this.draft_manager != null) {
-            try {
-                Geary.ComposedEmail draft = yield get_composed_email(null, true);
-                this.draft_manager.update(
-                    yield draft.to_rfc822_message(null, null),
-                    this.draft_flags,
-                    null
-                );
-            } catch (Error err) {
-                GLib.message("Unable to save draft: %s", err.message);
-            }
+            Geary.ComposedEmail draft = yield get_composed_email(null, true);
+            yield this.draft_manager.update(
+                yield draft.to_rfc822_message(null, null),
+                this.draft_flags,
+                null,
+                null
+            );
         }
     }
 
-    private Geary.Nonblocking.Semaphore? discard_draft() {
+    private async void discard_draft() throws GLib.Error {
+        debug("Discarding draft");
+
         // cancel timer in favor of this operation
         this.draft_timer.reset();
-
-        try {
-            if (this.draft_manager != null)
-                return this.draft_manager.discard();
-        } catch (Error err) {
-            GLib.message("Unable to discard draft: %s", err.message);
-        }
-
-        return null;
+        yield this.draft_manager.discard(null);
     }
 
     private async void save_and_exit_async() {
         this.is_closing = true;
         set_enabled(false);
-        yield save_draft();
+        try {
+            yield save_draft();
+        } catch (GLib.Error error) {
+            this.application.controller.report_problem(
+                new Geary.AccountProblemReport(
+                    this.account.information, error
+                )
+            );
+        }
         yield close();
     }
 
     private async void discard_and_exit_async() {
         this.is_closing = true;
         set_enabled(false);
+
         if (this.draft_manager != null) {
-            discard_draft();
-            draft_manager.discard_on_close = true;
+            try {
+                yield discard_draft();
+                yield close_draft_manager(null);
+            } catch (GLib.Error error) {
+                this.application.controller.report_problem(
+                    new Geary.AccountProblemReport(
+                        this.account.information, error
+                    )
+                );
+            }
+        }
         }
         yield close();
     }
@@ -2297,7 +2299,22 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
                 this.account = selected.account;
                 this.update_signature.begin(null);
                 load_entry_completions();
-                this.reopen_draft_manager_async.begin();
+
+                var current_account = this.account;
+                this.reopen_draft_manager.begin(
+                    null,
+                    (obj, res) => {
+                        try {
+                            this.reopen_draft_manager.end(res);
+                        } catch (GLib.Error error) {
+                            this.application.controller.report_problem(
+                                new Geary.AccountProblemReport(
+                                    current_account.information, error
+                                )
+                            );
+                        }
+                    }
+                );
             }
         }
     }
@@ -2618,6 +2635,23 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
         discard_and_exit_async.begin();
     }
 
+    private void on_draft_timeout() {
+        var current_account = this.account;
+        this.save_draft.begin(
+            (obj, res) => {
+                try {
+                    this.save_draft.end(res);
+                } catch (GLib.Error error) {
+                    this.application.controller.report_problem(
+                        new Geary.AccountProblemReport(
+                            current_account.information, error
+                        )
+                    );
+                }
+            }
+        );
+    }
+
     private void on_account_available() {
         update_from_field();
     }
diff --git a/src/engine/app/app-draft-manager.vala b/src/engine/app/app-draft-manager.vala
index 112a17d0..23434b7b 100644
--- a/src/engine/app/app-draft-manager.vala
+++ b/src/engine/app/app-draft-manager.vala
@@ -31,7 +31,6 @@ public class Geary.App.DraftManager : BaseObject {
     public const string PROP_CURRENT_DRAFT_ID = "current-draft-id";
     public const string PROP_VERSIONS_SAVED = "versions-saved";
     public const string PROP_VERSIONS_DROPPED = "versions-dropped";
-    public const string PROP_DISCARD_ON_CLOSE = "discard-on-close";
 
     /**
      * Current saved state of the draft.
@@ -114,13 +113,6 @@ public class Geary.App.DraftManager : BaseObject {
      */
     public int versions_dropped { get; private set; default = 0; }
 
-    /**
-     * When set, the draft will be discarded when {@link close_async} is called.
-     *
-     * In addition, when set all future {@link update}s will result in the draft being dropped.
-     */
-    public bool discard_on_close { get; set; default = false; }
-
     private Account account;
     private Folder? drafts_folder = null;
     private FolderSupport.Create? create_support = null;
@@ -130,6 +122,7 @@ public class Geary.App.DraftManager : BaseObject {
     private bool was_opened = false;
     private Error? fatal_err = null;
 
+
     /**
      * Fired when a draft is successfully saved.
      */
@@ -272,13 +265,6 @@ public class Geary.App.DraftManager : BaseObject {
 
         // don't flush a CLOSE down the pipe if failed, the operation loop is closed for business
         if (fatal_err == null) {
-            // if discarding on close, do so now
-            if (discard_on_close) {
-                // don't use discard(), which checks if open, but submit_push() directly,
-                // which doesn't
-                submit_push(null, null, null);
-            }
-
             // flush pending I/O
             Nonblocking.Semaphore semaphore = new Nonblocking.Semaphore(cancellable);
             mailbox.send(new Operation(OperationType.CLOSE, null, null, null, semaphore));
@@ -316,15 +302,13 @@ public class Geary.App.DraftManager : BaseObject {
      *
      * See {@link FolderSupport.Create.create_email_async} for more information on the flags and
      * date_received arguments.
-     *
-     * @return A {@link Nonblocking.Semaphore} that is notified when the operation completes (with
-     * or without error)
      */
-    public Geary.Nonblocking.Semaphore? update(Geary.RFC822.Message draft, Geary.EmailFlags? flags,
-        DateTime? date_received) throws Error {
+    public async void update(Geary.RFC822.Message draft,
+                             Geary.EmailFlags? flags,
+                             DateTime? date_received,
+                             GLib.Cancellable? cancellable) throws GLib.Error {
         check_open();
-
-        return submit_push(draft, flags, date_received);
+        yield submit_push(draft, flags, date_received).wait_async(cancellable);
     }
 
     /**
@@ -335,27 +319,16 @@ public class Geary.App.DraftManager : BaseObject {
      *
      * Note: Replaced drafts are deleted, but on some services (i.e. Gmail) those deleted messages
      * are actually moved to the Trash.  This call does not currently solve that problem.
-     *
-     * @return A {@link Nonblocking.Semaphore} that is notified when the operation completes (with
-     * or without error)
      */
-    public Geary.Nonblocking.Semaphore? discard() throws Error {
+    public async void discard(GLib.Cancellable? cancellable)
+        throws GLib.Error {
         check_open();
-
-        return submit_push(null, null, null);
+        yield submit_push(null, null, null).wait_async(cancellable);
     }
 
     // Note that this call doesn't check_open(), important when used within close_async()
-    private Nonblocking.Semaphore? submit_push(RFC822.Message? draft, EmailFlags? flags,
+    private Nonblocking.Semaphore submit_push(RFC822.Message? draft, EmailFlags? flags,
         DateTime? date_received) {
-        // no drafts are pushed when discarding on close
-        if (draft != null && discard_on_close) {
-            versions_dropped++;
-            dropped(draft);
-
-            return null;
-        }
-
         // clear out pending pushes (which can be updates or discards)
         mailbox.revoke_matching((op) => {
             // count and notify of dropped drafts


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