[geary/mjog/email-templates: 59/72] Application.Controller, Composer.Widget: Clean up composer construction



commit 53de3b86b848d9d514f29569ecdc5f07af649cc8
Author: Michael Gratton <mike vee net>
Date:   Sun Apr 19 15:51:03 2020 +1000

    Application.Controller, Composer.Widget: Clean up composer construction
    
    Clean up and simplify how composers are constructed. Ensure all
    composers are constructed via Application.Composer. Provide a set of
    APIs for constructing different kinds of composers with minimal
    parameters, rather than having one method with eleventy different
    parameters.
    
    Mirror API changes in Composer.Widget by splitting `load` method up
    into a method for each different composer type. Clean up internals a
    bit as a result. Rename `ComposeType` enum and its values to
    `ContextType` to better reflect what it does.

 src/client/application/application-client.vala     |  16 +-
 src/client/application/application-controller.vala | 412 +++++++++------------
 .../application/application-main-window.vala       |  90 +----
 .../application/application-plugin-manager.vala    |   7 +-
 src/client/composer/composer-embed.vala            |   2 +-
 src/client/composer/composer-web-view.vala         |   6 +-
 src/client/composer/composer-widget.vala           | 355 ++++++++++--------
 .../conversation-contact-popover.vala              |   2 +-
 .../conversation-viewer/conversation-list-box.vala |   4 +-
 .../conversation-viewer/conversation-viewer.vala   |   2 +-
 10 files changed, 415 insertions(+), 481 deletions(-)
---
diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala
index 7a784c90..a5dc995b 100644
--- a/src/client/application/application-client.vala
+++ b/src/client/application/application-client.vala
@@ -513,7 +513,7 @@ public class Application.Client : Gtk.Application {
                         mailto.substring(B0RKED_GLIB_MAILTO_PREFIX.length)
                     );
                 }
-                this.new_composer.begin(mailto);
+                this.new_composer_mailto.begin(mailto);
             }
         }
     }
@@ -725,10 +725,14 @@ public class Application.Client : Gtk.Application {
         prefs.show();
     }
 
-    public async void new_composer(string? mailto) {
+    public async void new_composer(Geary.RFC822.MailboxAddress? to = null) {
         yield this.present();
+        yield this.controller.compose_new_email(to);
+    }
 
-        this.controller.compose(mailto);
+    public async void new_composer_mailto(string? mailto) {
+        yield this.present();
+        yield this.controller.compose_mailto(mailto);
     }
 
     public async void new_window(Geary.Folder? select_folder,
@@ -824,7 +828,7 @@ public class Application.Client : Gtk.Application {
         yield create_controller();
 
         if (uri.down().has_prefix(MAILTO_URI_SCHEME_PREFIX)) {
-            yield this.new_composer(uri);
+            yield this.new_composer_mailto(uri);
         } else {
             string uri_ = uri;
             // Support web URLs that omit the protocol.
@@ -1146,7 +1150,7 @@ public class Application.Client : Gtk.Application {
     }
 
     private void on_activate_compose() {
-        this.new_composer.begin(null);
+        this.new_composer.begin();
     }
 
     private void on_activate_inspect() {
@@ -1155,7 +1159,7 @@ public class Application.Client : Gtk.Application {
 
     private void on_activate_mailto(SimpleAction action, Variant? param) {
         if (param != null) {
-            this.new_composer.begin(param.get_string());
+            this.new_composer_mailto.begin(param.get_string());
         }
     }
 
diff --git a/src/client/application/application-controller.vala 
b/src/client/application/application-controller.vala
index 6f534d47..530dec32 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -212,16 +212,6 @@ internal class Application.Controller : Geary.BaseObject {
         this.expunge_accounts.begin();
     }
 
-    /** Returns a context for an account, if any. */
-    internal AccountContext? get_context_for_account(Geary.AccountInformation account) {
-        return this.accounts.get(account);
-    }
-
-    /** Returns a read-only collection of contexts each active account. */
-    internal Gee.Collection<AccountContext> get_account_contexts() {
-        return this.accounts.values.read_only_view;
-    }
-
     /** Closes all windows and accounts, releasing held resources. */
     public async void close() {
         // Stop listening for account changes up front so we don't
@@ -333,20 +323,48 @@ internal class Application.Controller : Geary.BaseObject {
     }
 
     /**
-     * Opens or queues a new composer addressed to a specific email address.
+     * Opens a composer for writing a new, blank message.
      */
-    public void compose(string? mailto = null) {
+    public async void compose_new_email(Geary.RFC822.MailboxAddress? to = null) {
+        // If there's already an empty composer open, just use that
+        foreach (Composer.Widget existing in this.composer_widgets) {
+            if (existing != null &&
+                existing.current_mode == PANED &&
+                existing.is_blank) {
+                existing.present();
+            }
+        }
+
+        var composer = new Composer.Widget(
+            this.application,
+            this.application.get_active_main_window().selected_account
+        );
+        register_composer(composer);
+        show_composer(composer, null);
+        try {
+            yield composer.load_empty_body(to);
+        } catch (GLib.Error err) {
+            report_problem(new Geary.ProblemReport(err));
+        }
+    }
+
+    /**
+     * Opens a composer with the given `mailto:` URL.
+     */
+    public async void compose_mailto(string mailto) {
         MainWindow? window = this.application.last_active_main_window;
         if (window != null && window.selected_account != null) {
-            create_compose_widget(
-                window,
-                window.selected_account,
-                NEW_MESSAGE,
-                mailto,
-                null,
-                null,
-                false
+            var composer = new Composer.Widget(
+                this.application, window.selected_account
             );
+            register_composer(composer);
+            show_composer(composer, null);
+
+            try {
+                yield composer.load_mailto(mailto);
+            } catch (GLib.Error err) {
+                report_problem(new Geary.ProblemReport(err));
+            }
         } else {
             // Schedule the send for after we have an account open.
             this.pending_mailtos.add(mailto);
@@ -355,90 +373,67 @@ internal class Application.Controller : Geary.BaseObject {
 
     /**
      * Opens new composer with an existing message as context.
+     *
+     * If the given type is {@link Composer.Widget.ContextType.EDIT},
+     * the context is loaded to be edited (e.g. for drafts, templates,
+     * sending again. Otherwise the context is treated as the email to
+     * be replied to, etc.
      */
-    public void compose_with_context_email(MainWindow to_show,
-                                           Geary.Account account,
-                                           Composer.Widget.ComposeType type,
-                                           Geary.Email context,
-                                           string? quote,
-                                           bool is_draft) {
-        create_compose_widget(
-            to_show, account, type, null, context, quote, is_draft
-        );
-    }
-
-    /** Adds a new composer to be kept track of. */
-    public void add_composer(Composer.Widget widget) {
-        debug(@"Added composer of type $(widget.compose_type); $(this.composer_widgets.size) composers 
total");
-        widget.destroy.connect_after(this.on_composer_widget_destroy);
-        this.composer_widgets.add(widget);
-    }
-
-    /** Returns a read-only collection of currently open composers .*/
-    public Gee.Collection<Composer.Widget> get_composers() {
-        return this.composer_widgets.read_only_view;
-    }
-
-    /** Opens any pending composers. */
-    public void process_pending_composers() {
-        foreach (string? mailto in this.pending_mailtos) {
-            compose(mailto);
-        }
-        this.pending_mailtos.clear();
-    }
-
-    /** Queues the email in a composer for delivery. */
-    public async void send_composed_email(Composer.Widget composer) {
-        AccountContext? context = this.accounts.get(
-            composer.account.information
-        );
-        if (context != null) {
-            try {
-                yield context.commands.execute(
-                    new SendComposerCommand(this.application, context, composer),
-                    context.cancellable
-                );
-            } catch (GLib.Error err) {
-                report_problem(new Geary.ProblemReport(err));
+    public async void compose_with_context_email(Composer.Widget.ContextType type,
+                                                 Geary.Email context,
+                                                 string? quote) {
+        MainWindow show_on = this.application.get_active_main_window();
+        if (type == EDIT) {
+            // Check all known composers since the context may be open
+            // an existing composer already.
+            foreach (Composer.Widget composer in this.composer_widgets) {
+                if (composer.saved_id != null &&
+                    composer.saved_id.equal_to(context.id)) {
+                    composer.present();
+                    composer.set_focus();
+                    return;
+                }
+            }
+        } else {
+            // See whether there is already an inline message in the
+            // current window that is either a reply/forward for that
+            // message, or there is a quote to insert into it.
+            foreach (Composer.Widget existing in this.composer_widgets) {
+                if (existing.get_toplevel() == show_on &&
+                    (existing.current_mode == INLINE ||
+                     existing.current_mode == INLINE_COMPACT) &&
+                    (context.id in existing.get_referred_ids() ||
+                     quote != null)) {
+                    try {
+                        existing.append_to_email(context, quote, type);
+                        existing.present();
+                        return;
+                    } catch (Geary.EngineError error) {
+                        report_problem(new Geary.ProblemReport(error));
+                    }
+                }
             }
-        }
-    }
 
-    /** Saves the email in a composer as a draft on the server. */
-    public async void save_composed_email(Composer.Widget composer) {
-        // XXX this doesn't actually do what it says on the tin, since
-        // the composer's draft manager is already saving drafts on
-        // the server. Until we get that saving local-only, this will
-        // only be around for pushing the composer onto the undo stack
-        AccountContext? context = this.accounts.get(
-            composer.account.information
-        );
-        if (context != null) {
-            try {
-                yield context.commands.execute(
-                    new SaveComposerCommand(this, composer),
-                    context.cancellable
-                );
-            } catch (GLib.Error err) {
-                report_problem(new Geary.ProblemReport(err));
+            // Can't re-use an existing composer, so need to create a
+            // new one. Replies must open inline in the main window,
+            // so we need to ensure there are no composers open there
+            // first.
+            if (!show_on.close_composer(true)) {
+                return;
             }
         }
-    }
 
-    /** Queues a composer to be discarded. */
-    public async void discard_composed_email(Composer.Widget composer) {
-        AccountContext? context = this.accounts.get(
-            composer.account.information
+        var composer = new Composer.Widget(
+            this.application,
+            this.application.get_active_main_window().selected_account
         );
-        if (context != null) {
-            try {
-                yield context.commands.execute(
-                    new DiscardComposerCommand(this, composer),
-                    context.cancellable
-                );
-            } catch (GLib.Error err) {
-                report_problem(new Geary.ProblemReport(err));
-            }
+        register_composer(composer);
+        show_composer(composer, Geary.Collection.single(context.id));
+
+        try {
+            yield composer.load_context(type, context, quote);
+        } catch (GLib.Error err) {
+            report_problem(new Geary.ProblemReport(err));
         }
     }
 
@@ -858,6 +853,16 @@ internal class Application.Controller : Geary.BaseObject {
         }
     }
 
+    /** Returns a context for an account, if any. */
+    internal AccountContext? get_context_for_account(Geary.AccountInformation account) {
+        return this.accounts.get(account);
+    }
+
+    /** Returns a read-only collection of contexts each active account. */
+    internal Gee.Collection<AccountContext> get_account_contexts() {
+        return this.accounts.values.read_only_view;
+    }
+
     internal void register_window(MainWindow window) {
         window.retry_service_problem.connect(on_retry_service_problem);
     }
@@ -866,6 +871,69 @@ internal class Application.Controller : Geary.BaseObject {
         window.retry_service_problem.disconnect(on_retry_service_problem);
     }
 
+    /** Opens any pending composers. */
+    internal async void process_pending_composers() {
+        foreach (string? mailto in this.pending_mailtos) {
+            yield compose_mailto(mailto);
+        }
+        this.pending_mailtos.clear();
+    }
+
+    /** Queues the email in a composer for delivery. */
+    internal async void send_composed_email(Composer.Widget composer) {
+        AccountContext? context = this.accounts.get(
+            composer.account.information
+        );
+        if (context != null) {
+            try {
+                yield context.commands.execute(
+                    new SendComposerCommand(this.application, context, composer),
+                    context.cancellable
+                );
+            } catch (GLib.Error err) {
+                report_problem(new Geary.ProblemReport(err));
+            }
+        }
+    }
+
+    /** Saves the email in a composer as a draft on the server. */
+    internal async void save_composed_email(Composer.Widget composer) {
+        // XXX this doesn't actually do what it says on the tin, since
+        // the composer's draft manager is already saving drafts on
+        // the server. Until we get that saving local-only, this will
+        // only be around for pushing the composer onto the undo stack
+        AccountContext? context = this.accounts.get(
+            composer.account.information
+        );
+        if (context != null) {
+            try {
+                yield context.commands.execute(
+                    new SaveComposerCommand(this, composer),
+                    context.cancellable
+                );
+            } catch (GLib.Error err) {
+                report_problem(new Geary.ProblemReport(err));
+            }
+        }
+    }
+
+    /** Queues a composer to be discarded. */
+    internal async void discard_composed_email(Composer.Widget composer) {
+        AccountContext? context = this.accounts.get(
+            composer.account.information
+        );
+        if (context != null) {
+            try {
+                yield context.commands.execute(
+                    new DiscardComposerCommand(this, composer),
+                    context.cancellable
+                );
+            } catch (GLib.Error err) {
+                report_problem(new Geary.ProblemReport(err));
+            }
+        }
+    }
+
     /** Expunges removed accounts while the controller remains open. */
     internal async void expunge_accounts() {
         try {
@@ -1330,13 +1398,8 @@ internal class Application.Controller : Geary.BaseObject {
 
     /** Displays a composer on the last active main window. */
     internal void show_composer(Composer.Widget composer,
-                                Gee.Collection<Geary.EmailIdentifier>? refers_to,
-                                MainWindow? show_on) {
-        var target = show_on;
-        if (target == null) {
-            target = this.application.get_active_main_window();
-        }
-
+                                Gee.Collection<Geary.EmailIdentifier>? refers_to) {
+        var target = this.application.get_active_main_window();
         target.show_composer(composer, refers_to);
         composer.set_focus();
     }
@@ -1352,135 +1415,17 @@ internal class Application.Controller : Geary.BaseObject {
         return do_quit;
     }
 
-    /**
-     * Creates a composer widget.
-     *
-     * Depending on the arguments, this can be inline in the
-     * conversation or as a new window.
-     *
-     * @param compose_type - Whether it's a new message, a reply, a
-     * forwarded mail, ...
-     * @param referred - The mail of which we should copy the from/to/...
-     * addresses
-     * @param quote - The quote after the mail body
-     * @param mailto - A "mailto:"-link
-     * @param is_draft - Whether we're starting from a draft (true) or
-     * a new mail (false)
-     */
-    private void create_compose_widget(MainWindow show_on,
-                                       Geary.Account account,
-                                       Composer.Widget.ComposeType compose_type,
-                                       string? mailto,
-                                       Geary.Email? referred,
-                                       string? quote,
-                                       bool is_draft) {
-        // There's a few situations where we can re-use an existing
-        // composer, check for these first.
-        if (compose_type == NEW_MESSAGE && !is_draft) {
-            // We're creating a new message that isn't a draft, if
-            // there's already an empty composer open, just use
-            // that
-            foreach (Composer.Widget existing in this.composer_widgets) {
-                if (existing != null &&
-                    existing.current_mode == PANED &&
-                    existing.is_blank) {
-                    existing.present();
-                    return;
-                }
-            }
-        } else if (compose_type != NEW_MESSAGE && referred != null) {
-            // A reply/forward was requested, see whether there is
-            // already an inline message in the target window that is
-            // either a reply/forward for that message, or there is a
-            // quote to insert into it.
-            foreach (Composer.Widget existing in this.composer_widgets) {
-                if (existing.get_toplevel() == show_on &&
-                    (existing.current_mode == INLINE ||
-                     existing.current_mode == INLINE_COMPACT) &&
-                    (referred.id in existing.get_referred_ids() ||
-                     quote != null)) {
-                    try {
-                        existing.append_to_email(referred, quote, compose_type);
-                        existing.present();
-                        return;
-                    } catch (Geary.EngineError error) {
-                        report_problem(new Geary.ProblemReport(error));
-                    }
-                }
-            }
-
-            // Can't re-use an existing composer, so need to create a
-            // new one. Replies must open inline in the main window,
-            // so we need to ensure there are no composers open there
-            // first.
-            if (!show_on.close_composer(true)) {
-                return;
-            }
-        }
-
-        Composer.Widget widget;
-        if (mailto != null) {
-            widget = new Composer.Widget.from_mailto(
-                this.application, account, mailto
-            );
-        } else {
-            widget = new Composer.Widget(
-                this.application, account, compose_type
-            );
-        }
-
-        add_composer(widget);
-        show_composer(
-            widget,
-            referred != null ? Geary.Collection.single(referred.id) : null,
-            show_on
-        );
-
-        this.load_composer.begin(
-            account,
-            widget,
-            referred,
-            is_draft,
-            quote
-        );
-    }
-
-    private async void load_composer(Geary.Account account,
-                                     Composer.Widget widget,
-                                     Geary.Email? referred = null,
-                                     bool is_draft,
-                                     string? quote = null) {
-        Geary.Email? full = null;
-        GLib.Cancellable? cancellable = null;
-        if (referred != null) {
-            AccountContext? context = this.accounts.get(account.information);
-            if (context != null) {
-                cancellable = context.cancellable;
-                try {
-                    full = yield context.emails.fetch_email_async(
-                        referred.id,
-                        Geary.ComposedEmail.REQUIRED_REPLY_FIELDS |
-                        Composer.Widget.REQUIRED_FIELDS,
-                        NONE,
-                        cancellable
-                    );
-                } catch (Error e) {
-                    message("Could not load full message: %s", e.message);
-                }
-            }
-        }
-        try {
-            yield widget.load(full, is_draft, quote, cancellable);
-        } catch (GLib.Error err) {
-            report_problem(new Geary.ProblemReport(err));
-        }
+    internal void register_composer(Composer.Widget widget) {
+        debug(@"Registered composer of type $(widget.context_type); $(this.composer_widgets.size) composers 
total");
+        widget.destroy.connect_after(this.on_composer_widget_destroy);
+        this.composer_widgets.add(widget);
     }
 
     private void on_composer_widget_destroy(Gtk.Widget sender) {
         Composer.Widget? composer = sender as Composer.Widget;
         if (composer != null) {
             composer_widgets.remove((Composer.Widget) sender);
-            debug(@"Composer type $(composer.compose_type) destroyed; " +
+            debug(@"Composer type $(composer.context_type) destroyed; " +
                   @"$(this.composer_widgets.size) composers remaining");
         }
     }
@@ -2485,7 +2430,6 @@ private class Application.SendComposerCommand : ComposerCommand {
     public override async void execute(GLib.Cancellable? cancellable)
         throws GLib.Error {
         Geary.ComposedEmail email = yield this.composer.get_composed_email();
-        
         if (this.can_undo) {
             /// Translators: The label for an in-app notification. The
             /// string substitution is a list of recipients of the email.
@@ -2510,7 +2454,9 @@ private class Application.SendComposerCommand : ComposerCommand {
         this.saved = null;
 
         this.composer.set_enabled(true);
-        this.application.controller.show_composer(this.composer, null, null);
+        this.application.controller.show_composer(
+            this.composer, this.composer.get_referred_ids()
+        );
         clear_composer();
     }
 
@@ -2564,7 +2510,9 @@ private class Application.SaveComposerCommand : ComposerCommand {
         if (this.composer != null) {
             this.destroy_timer.reset();
             this.composer.set_enabled(true);
-            this.controller.show_composer(this.composer, null, null);
+            this.controller.show_composer(
+                this.composer, this.composer.get_referred_ids()
+            );
             clear_composer();
         } else {
             /// Translators: A label for an in-app notification.
@@ -2622,7 +2570,9 @@ private class Application.DiscardComposerCommand : ComposerCommand {
         if (this.composer != null) {
             this.destroy_timer.reset();
             this.composer.set_enabled(true);
-            this.controller.show_composer(this.composer, null, null);
+            this.controller.show_composer(
+                this.composer, this.composer.get_referred_ids()
+            );
             clear_composer();
         } else {
             /// Translators: A label for an in-app notification.
diff --git a/src/client/application/application-main-window.vala 
b/src/client/application/application-main-window.vala
index 9fe6ab19..1a3fc269 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -766,7 +766,7 @@ public class Application.MainWindow :
                 );
 
                 yield open_conversation_monitor(this.conversations, cancellable);
-                this.controller.process_pending_composers();
+                yield this.controller.process_pending_composers();
             }
         }
 
@@ -864,16 +864,6 @@ public class Application.MainWindow :
         }
     }
 
-    /** Displays a composer addressed to a specific email address. */
-    public void open_composer_for_mailbox(Geary.RFC822.MailboxAddress to) {
-        var composer = new Composer.Widget.from_mailbox(
-            this.application, this.selected_folder.account, to
-        );
-        this.controller.add_composer(composer);
-        show_composer(composer, null);
-        composer.load.begin(null, false, null, null);
-    }
-
     /**
      * Displays a composer in the window if possible, else in a new window.
      *
@@ -883,8 +873,8 @@ public class Application.MainWindow :
      * the composer's {@link Composer.Widget.get_referred_ids} will be
      * used.
      */
-    public void show_composer(Composer.Widget composer,
-                              Gee.Collection<Geary.EmailIdentifier>? refers_to) {
+    internal void show_composer(Composer.Widget composer,
+                                Gee.Collection<Geary.EmailIdentifier>? refers_to) {
         if (this.has_composer) {
             composer.detach();
         } else {
@@ -923,7 +913,7 @@ public class Application.MainWindow :
      * Returns true if none were open or the user approved closing
      * them.
      */
-    public bool close_composer(bool should_prompt, bool is_shutdown = false) {
+    internal bool close_composer(bool should_prompt, bool is_shutdown = false) {
         bool closed = true;
         Composer.Widget? composer = this.conversation_viewer.current_composer;
         if (composer != null &&
@@ -1547,7 +1537,7 @@ public class Application.MainWindow :
         );
     }
 
-    private void create_composer_from_viewer(Composer.Widget.ComposeType compose_type) {
+    private void create_composer_from_viewer(Composer.Widget.ContextType type) {
         Geary.Account? account = this.selected_account;
         ConversationEmail? email_view = null;
         ConversationListBox? list_view = this.conversation_viewer.current_list;
@@ -1557,13 +1547,8 @@ public class Application.MainWindow :
         if (account != null && email_view != null) {
             email_view.get_selection_for_quoting.begin((obj, res) => {
                     string? quote = email_view.get_selection_for_quoting.end(res);
-                    this.controller.compose_with_context_email(
-                        this,
-                        account,
-                        compose_type,
-                        email_view.email,
-                        quote,
-                        false
+                    this.controller.compose_with_context_email.begin(
+                        type, email_view.email, quote ?? ""
                     );
                 });
         }
@@ -2043,7 +2028,6 @@ public class Application.MainWindow :
         list.reply_to_all_email.connect(on_email_reply_to_all);
         list.reply_to_sender_email.connect(on_email_reply_to_sender);
         list.forward_email.connect(on_email_forward);
-        list.edit_email.connect(on_email_edit);
         list.trash_email.connect(on_email_trash);
         list.delete_email.connect(on_email_delete);
     }
@@ -2102,31 +2086,9 @@ public class Application.MainWindow :
                 // TODO: Determine how to map between conversations
                 // and drafts correctly.
                 Geary.Email draft = activated.get_latest_recv_email(IN_FOLDER);
-
-                // Check all known composers since the draft may be
-                // open in a detached composer
-                bool already_open = false;
-                foreach (Composer.Widget composer
-                         in this.controller.get_composers()) {
-                    if (composer.current_draft_id != null &&
-                        composer.current_draft_id.equal_to(draft.id)) {
-                        already_open = true;
-                        composer.present();
-                        composer.set_focus();
-                        break;
-                    }
-                }
-
-                if (!already_open) {
-                    this.controller.compose_with_context_email(
-                        this,
-                        activated.base_folder.account,
-                        NEW_MESSAGE,
-                        draft,
-                        null,
-                        true
-                    );
-                }
+                this.controller.compose_with_context_email.begin(
+                    EDIT, draft, null
+                );
             }
         }
     }
@@ -2153,7 +2115,7 @@ public class Application.MainWindow :
     }
 
     private void on_reply_conversation() {
-        create_composer_from_viewer(REPLY);
+        create_composer_from_viewer(REPLY_SENDER);
     }
 
     private void on_reply_all_conversation() {
@@ -2476,37 +2438,25 @@ public class Application.MainWindow :
     }
 
     private void on_email_reply_to_sender(Geary.Email target, string? quote) {
-        Geary.Account? account = this.selected_account;
-        if (account != null) {
-            this.controller.compose_with_context_email(
-                this, account, REPLY, target, quote, false
+        if (this.selected_account != null) {
+            this.controller.compose_with_context_email.begin(
+                REPLY_SENDER, target, quote
             );
         }
     }
 
     private void on_email_reply_to_all(Geary.Email target, string? quote) {
-        Geary.Account? account = this.selected_account;
-        if (account != null) {
-            this.controller.compose_with_context_email(
-                this, account, REPLY_ALL, target, quote, false
+        if (this.selected_account != null) {
+            this.controller.compose_with_context_email.begin(
+                REPLY_ALL, target, quote
             );
         }
     }
 
     private void on_email_forward(Geary.Email target, string? quote) {
-        Geary.Account? account = this.selected_account;
-        if (account != null) {
-            this.controller.compose_with_context_email(
-                this, account, FORWARD, target, quote, false
-            );
-        }
-    }
-
-    private void on_email_edit(Geary.Email target) {
-        Geary.Account? account = this.selected_account;
-        if (account != null) {
-            this.controller.compose_with_context_email(
-                this, account, NEW_MESSAGE, target, null, true
+        if (this.selected_account != null) {
+            this.controller.compose_with_context_email.begin(
+                FORWARD, target, quote
             );
         }
     }
diff --git a/src/client/application/application-plugin-manager.vala 
b/src/client/application/application-plugin-manager.vala
index 378e65f4..b36e9e1a 100644
--- a/src/client/application/application-plugin-manager.vala
+++ b/src/client/application/application-plugin-manager.vala
@@ -183,12 +183,7 @@ public class Application.PluginManager : GLib.Object {
         }
 
         public void show() {
-            var composer = new Composer.Widget(
-                this.application, this.account.account, NEW_MESSAGE
-            );
-            var main_window = this.application.get_active_main_window();
-            main_window.show_composer(composer, null);
-            composer.load.begin(null, false, null, null);
+            this.application.controller.compose_new_email.begin();
         }
 
     }
diff --git a/src/client/composer/composer-embed.vala b/src/client/composer/composer-embed.vala
index d92aca6d..26f6a336 100644
--- a/src/client/composer/composer-embed.vala
+++ b/src/client/composer/composer-embed.vala
@@ -43,7 +43,7 @@ public class Composer.Embed : Gtk.EventBox, Container {
         this.composer.embed_header();
 
         Widget.PresentationMode mode = INLINE_COMPACT;
-        if (composer.compose_type == FORWARD ||
+        if (composer.context_type == FORWARD ||
             composer.has_multiple_from_addresses) {
             mode = INLINE;
         }
diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala
index 9c78ec75..d187759c 100644
--- a/src/client/composer/composer-web-view.vala
+++ b/src/client/composer/composer-web-view.vala
@@ -148,7 +148,7 @@ public class Composer.WebView : ClientWebView {
     public new void load_html(string body,
                               string quote,
                               bool top_posting,
-                              bool is_draft) {
+                              bool body_complete) {
         const string HTML_PRE = """<html><body class="%s">""";
         const string HTML_POST = """</body></html>""";
         const string BODY_PRE = """
@@ -165,7 +165,7 @@ public class Composer.WebView : ClientWebView {
         StringBuilder html = new StringBuilder();
         string body_class = (this.is_rich_text) ? "" : "plain";
         html.append(HTML_PRE.printf(body_class));
-        if (!is_draft) {
+        if (!body_complete) {
             html.append(BODY_PRE);
             bool have_body = !Geary.String.is_empty(body);
             if (have_body) {
@@ -185,7 +185,7 @@ public class Composer.WebView : ClientWebView {
                 html.append_printf(QUOTE, quote);
             }
         } else {
-            html.append(quote);
+            html.append(body);
         }
         html.append(HTML_POST);
         base.load_html((string) html.data);
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 6cceec72..8b62eb92 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -1,6 +1,6 @@
 /*
- * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2017-2019 Michael Gratton <mike vee net>
+ * Copyright © 2016 Software Freedom Conservancy Inc.
+ * Copyright © 2017-2020 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later). See the COPYING file in this distribution.
@@ -25,15 +25,31 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
 
     /** The email fields the composer requires for referred email. */
-    public const Geary.Email.Field REQUIRED_FIELDS = ENVELOPE | BODY;
+    public const Geary.Email.Field REQUIRED_FIELDS = ENVELOPE | HEADER | BODY;
 
     /// Translators: Title for an empty composer window
     private const string DEFAULT_TITLE = _("New Message");
+    
+    /**
+     * Determines the type of the context email passed to the composer
+     *
+     * @see context_type
+     * @see load_context
+     */
+    public enum ContextType {
+        /** No context mail was provided. */
+        NONE,
 
-    public enum ComposeType {
-        NEW_MESSAGE,
-        REPLY,
+        /** Context is an email to edited, for example a draft or template. */
+        EDIT,
+
+        /** Context is an email being replied to the sender only. */
+        REPLY_SENDER,
+
+        /** Context is an email being replied to all recipients. */
         REPLY_ALL,
+
+        /** Context is an email being forwarded. */
         FORWARD
     }
 
@@ -242,17 +258,17 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
     /** The account the email is being sent from. */
     public Geary.Account account { get; private set; }
 
-    /** The identifier of the draft this composer holds, if any. */
-    public Geary.EmailIdentifier? current_draft_id {
+    /** The identifier of the saved email this composer holds, if any. */
+    public Geary.EmailIdentifier? saved_id {
         get; private set; default = null;
     }
 
+    /** Determines the type of the context email. */
+    public ContextType context_type { get; private set; default = NONE; }
+
     /** Determines the composer's current presentation mode. */
     public PresentationMode current_mode { get; set; default = NONE; }
 
-    /** Determines the type of email being composed. */
-    public ComposeType compose_type { get; private set; default = ComposeType.NEW_MESSAGE; }
-
     /** Determines if the composer is completely empty. */
     public bool is_blank {
         get {
@@ -431,17 +447,12 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
     private Gee.Collection<Geary.Account> accounts;
 
-    private string body_html = "";
-
     private string? pointer_url = null;
     private string? cursor_url = null;
     private bool is_attachment_overlay_visible = false;
     private Geary.RFC822.MailboxAddresses reply_to_addresses;
     private Geary.RFC822.MailboxAddresses reply_cc_addresses;
-    private string reply_subject = "";
-    private string forward_subject = "";
     private bool top_posting = true;
-    private string? last_quote = null;
 
     // The message(s) this email is in reply to/forwarded from
     private Gee.Set<Geary.EmailIdentifier> referred_ids =
@@ -491,8 +502,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
 
     public Widget(Application.Client application,
-                          Geary.Account initial_account,
-                          ComposeType compose_type) {
+                  Geary.Account initial_account) {
         components_reflow_box_get_type();
         base_ref();
         this.application = application;
@@ -504,8 +514,6 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
             warning("Could not fetch account info: %s", e.message);
         }
 
-        this.compose_type = compose_type;
-
         this.header = new Headerbar(application.config);
         this.header.expand_composer.connect(on_expand_compact_headers);
 
@@ -642,18 +650,20 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
         update_color_icon.begin(Util.Gtk.rgba(0, 0, 0, 0));
     }
 
-    public Widget.from_mailbox(Application.Client application,
-                               Geary.Account initial_account,
-                               Geary.RFC822.MailboxAddress to) {
-        this(application, initial_account, ComposeType.NEW_MESSAGE);
-        this.to = to.to_full_display();
+    ~Widget() {
+        base_unref();
     }
 
-    public Widget.from_mailto(Application.Client application,
-                              Geary.Account initial_account,
-                              string mailto) {
-        this(application, initial_account, ComposeType.NEW_MESSAGE);
+    public async void load_empty_body(Geary.RFC822.MailboxAddress? to = null)
+        throws GLib.Error {
+        if (to != null) {
+            this.to = to.to_full_display();
+        }
+        yield finish_loading("", "", false, null);
+    }
 
+    public async void load_mailto(string mailto)
+        throws GLib.Error {
         Gee.HashMultiMap<string, string> headers = new Gee.HashMultiMap<string, string>();
         if (mailto.has_prefix(MAILTO_URI_PREFIX)) {
             // Parse the mailto link.
@@ -687,9 +697,14 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
             if (headers.contains("subject"))
                 this.subject = Geary.Collection.first(headers.get("subject"));
 
-            if (headers.contains("body"))
-                this.body_html = Geary.HTML.preserve_whitespace(Geary.HTML.escape_markup(
-                    Geary.Collection.first(headers.get("body"))));
+            var body = "";
+            if (headers.contains("body")) {
+                body = Geary.HTML.preserve_whitespace(
+                    Geary.HTML.escape_markup(
+                        Geary.Collection.first(headers.get("body"))
+                    )
+                );
+            }
 
             Gee.List<string> attachments = new Gee.LinkedList<string>();
             attachments.add_all(headers.get("attach"));
@@ -701,67 +716,86 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
                     attachment_failed(err.message);
                 }
             }
+            yield finish_loading(body, "", false, null);
         }
     }
 
-    ~Widget() {
-        base_unref();
-    }
-
-    /**
-     * Returns the emails referred to by the composed email.
-     *
-     * A referred email is the email this composer is a reply to, or
-     * forwarded from. There may be multiple if a composer was already
-     * open and another email was replied to.
-     */
-    public Gee.Set<Geary.EmailIdentifier> get_referred_ids() {
-        return this.referred_ids.read_only_view;
-    }
-
     /**
      * Loads the message into the composer editor.
      */
-    public async void load(Geary.Email? referred = null,
-                           bool is_draft,
-                           string? quote = null,
-                           GLib.Cancellable? cancellable)
+    public async void load_context(ContextType type,
+                                   Geary.Email context,
+                                   string? quote)
         throws GLib.Error {
-        if (referred != null &&
-            !referred.fields.is_all_set(REQUIRED_FIELDS)) {
+        if (type == NONE) {
+            throw new Geary.EngineError.BAD_PARAMETERS(
+                "Invalid context type: %s", type.to_string()
+            );
+        }
+
+        if (!context.fields.is_all_set(REQUIRED_FIELDS)) {
             throw new Geary.EngineError.INCOMPLETE_MESSAGE(
-                "Required fields not met: %s", referred.fields.to_string()
+                "Required fields not met: %s", context.fields.to_string()
             );
         }
-        string referred_quote = "";
-        this.last_quote = quote;
-        if (referred != null) {
-            referred_quote = fill_in_from_referred(referred, quote);
-            if (is_draft ||
-                compose_type == ComposeType.NEW_MESSAGE ||
-                compose_type == ComposeType.FORWARD) {
-                this.pending_include = AttachPending.ALL;
-            }
-            if (is_draft) {
-                yield restore_reply_to_state();
-            }
+
+        this.context_type = type;
+
+        if (type == EDIT ||
+            type == FORWARD) {
+            this.pending_include = AttachPending.ALL;
         }
 
-        update_attachments_view();
-        update_pending_attachments(this.pending_include, true);
+        var body = "";
+        var complete_quote = "";
+        switch (type) {
+        case EDIT:
+            this.saved_id = context.id;
+            yield restore_reply_to_state();
+            fill_in_from_context(context);
+            Geary.RFC822.Message message = context.get_message();
+            body = (
+                message.has_html_body()
+                ? message.get_html_body(null)
+                : message.get_plain_body(true, null)
+            );
+            break;
 
-        this.editor.load_html(
-            this.body_html,
-            referred_quote,
-            this.top_posting,
-            is_draft
-        );
+        case REPLY_SENDER:
+        case REPLY_ALL:
+            add_recipients_and_ids(this.context_type, context);
+            fill_in_from_context(context);
+            complete_quote = Util.Email.quote_email_for_reply(
+                context, quote, this.application.config.clock_format, HTML
+            );
+            if (!Geary.String.is_empty(quote)) {
+                this.top_posting = false;
+            } else {
+                this.can_delete_quote = true;
+            }
+            break;
 
-        try {
-            yield open_draft_manager(is_draft ? referred.id : null, cancellable);
-        } catch (Error e) {
-            debug("Could not open draft manager: %s", e.message);
+        case FORWARD:
+            add_recipients_and_ids(this.context_type, context);
+            fill_in_from_context(context);
+            complete_quote = Util.Email.quote_email_for_forward(
+                context, quote, HTML
+            );
+            break;
         }
+
+        yield finish_loading(body, complete_quote, (type == EDIT), null);
+    }
+
+    /**
+     * Returns the emails referred to by the composed email.
+     *
+     * A referred email is the email this composer is a reply to, or
+     * forwarded from. There may be multiple if a composer was already
+     * open and another email was replied to.
+     */
+    public Gee.Set<Geary.EmailIdentifier> get_referred_ids() {
+        return this.referred_ids.read_only_view;
     }
 
     /** Detaches the composer and opens it in a new window. */
@@ -954,7 +988,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
         this.header.set_sensitive(enabled);
 
         if (enabled) {
-            this.open_draft_manager.begin(this.current_draft_id, null);
+            this.open_draft_manager.begin(this.saved_id, null);
         } else {
             if (this.container != null) {
                 this.container.close();
@@ -1006,25 +1040,18 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
             if (email == null)
                 continue;
 
-            // XXX pretty sure we are calling this only to update the
-            // composer's internal set of ids - we really shouldn't be
-            // messing around with the draft's recipients since the
-            // user may have already updated them.
-            add_recipients_and_ids(this.compose_type, email, false);
+            add_recipients_and_ids(this.context_type, email, false);
 
-            if (first_email) {
-                this.reply_subject = Geary.RFC822.Utils.create_subject_for_reply(email);
-                this.forward_subject = Geary.RFC822.Utils.create_subject_for_forward(email);
-                first_email = false;
-            }
+            first_email = false;
         }
         if (first_email)  // Either no referenced emails, or we don't have them.  Treat as new.
             return;
 
-        if (this.cc == "")
-            this.compose_type = ComposeType.REPLY;
-        else
-            this.compose_type = ComposeType.REPLY_ALL;
+        if (this.cc == "") {
+            this.context_type = REPLY_SENDER;
+        } else {
+            this.context_type = REPLY_ALL;
+        }
 
         if (!to_entry.addresses.equal_to(reply_to_addresses))
             this.to_entry.set_modified();
@@ -1054,18 +1081,12 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
         }
     }
 
-    // Copies the addresses (e.g. From/To/CC) and content from referred into this one
-    private string fill_in_from_referred(Geary.Email referred, string? quote) {
-        string referred_quote = "";
-        if (this.compose_type != ComposeType.NEW_MESSAGE) {
-            add_recipients_and_ids(this.compose_type, referred);
-            this.reply_subject = Geary.RFC822.Utils.create_subject_for_reply(referred);
-            this.forward_subject = Geary.RFC822.Utils.create_subject_for_forward(referred);
-        }
+    // Copies the addresses (e.g. From/To/CC) and content from
+    // referred into this one
+    private void fill_in_from_context(Geary.Email referred) {
         this.pending_attachments = referred.attachments;
-        switch (this.compose_type) {
-            // Restoring a draft
-            case ComposeType.NEW_MESSAGE:
+        switch (this.context_type) {
+            case EDIT:
                 if (referred.from != null)
                     this.from = referred.from;
                 if (referred.to != null)
@@ -1084,41 +1105,26 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
                     this.references = referred.references.to_rfc822_string();
                 if (referred.subject != null)
                     this.subject = referred.subject.value ?? "";
-                try {
-                    Geary.RFC822.Message message = referred.get_message();
-                    if (message.has_html_body()) {
-                        referred_quote = message.get_html_body(null);
-                    } else {
-                        referred_quote = message.get_plain_body(true, null);
-                    }
-                } catch (Error error) {
-                    debug("Error getting draft message body: %s", error.message);
-                }
             break;
 
-            case ComposeType.REPLY:
-            case ComposeType.REPLY_ALL:
-                this.subject = reply_subject;
-                this.references = Geary.RFC822.Utils.reply_references(referred);
-                referred_quote = Util.Email.quote_email_for_reply(referred, quote,
-                    this.application.config.clock_format,
-                    Geary.RFC822.TextFormat.HTML);
-                if (!Geary.String.is_empty(quote)) {
-                    this.top_posting = false;
-                } else {
-                    this.can_delete_quote = true;
-                }
+            case REPLY_SENDER:
+            case REPLY_ALL:
+                this.subject = Geary.RFC822.Utils.create_subject_for_reply(
+                    referred
+                );
+                this.references = Geary.RFC822.Utils.reply_references(
+                    referred
+                );
             break;
 
-            case ComposeType.FORWARD:
-                this.subject = forward_subject;
-                referred_quote = Util.Email.quote_email_for_forward(referred, quote,
-                    Geary.RFC822.TextFormat.HTML);
+            case FORWARD:
+                this.subject = Geary.RFC822.Utils.create_subject_for_forward(
+                    referred
+                );
             break;
         }
 
         update_extended_headers();
-        return referred_quote;
     }
 
     public void present() {
@@ -1319,7 +1325,8 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
             this.subject
         );
 
-        if ((this.compose_type == ComposeType.REPLY || this.compose_type == ComposeType.REPLY_ALL) &&
+        if ((this.context_type == REPLY_SENDER ||
+             this.context_type == REPLY_ALL) &&
             !this.in_reply_to.is_empty)
             email.set_in_reply_to(
                 new Geary.RFC822.MessageIDList.from_collection(this.in_reply_to)
@@ -1359,7 +1366,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
     /** Appends an email or fragment quoted into the composer. */
     public void append_to_email(Geary.Email referred,
                                 string? to_quote,
-                                ComposeType type)
+                                ContextType type)
         throws Geary.EngineError {
         if (!referred.fields.is_all_set(REQUIRED_FIELDS)) {
             throw new Geary.EngineError.INCOMPLETE_MESSAGE(
@@ -1371,30 +1378,29 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
             add_recipients_and_ids(type, referred);
         }
 
-        if (this.last_quote != to_quote) {
-            this.last_quote = to_quote;
-            // Always use reply styling, since forward styling doesn't
-            // work for inline quotes
-            this.editor.insert_html(
-                Util.Email.quote_email_for_reply(
-                    referred,
-                    to_quote,
-                    this.application.config.clock_format,
-                    Geary.RFC822.TextFormat.HTML
-                )
-            );
-        }
+        // Always use reply styling, since forward styling doesn't
+        // work for inline quotes
+        this.editor.insert_html(
+            Util.Email.quote_email_for_reply(
+                referred,
+                to_quote,
+                this.application.config.clock_format,
+                Geary.RFC822.TextFormat.HTML
+            )
+        );
     }
 
-    private void add_recipients_and_ids(ComposeType type, Geary.Email referred,
-        bool modify_headers = true) {
+    private void add_recipients_and_ids(ContextType type,
+                                        Geary.Email referred,
+                                        bool modify_headers = true) {
         Gee.List<Geary.RFC822.MailboxAddress> sender_addresses =
             account.information.sender_mailboxes;
 
         // Set the preferred from address. New messages should retain
         // the account default and drafts should retain the draft's
         // from addresses, so don't update them here
-        if (this.compose_type != ComposeType.NEW_MESSAGE) {
+        if (this.context_type != NONE &&
+            this.context_type != EDIT) {
             if (!check_preferred_from_address(sender_addresses, referred.to)) {
                 if (!check_preferred_from_address(sender_addresses, referred.cc))
                     if (!check_preferred_from_address(sender_addresses, referred.bcc))
@@ -1417,16 +1423,25 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
         bool recipients_modified = this.to_entry.is_modified || this.cc_entry.is_modified || 
this.bcc_entry.is_modified;
         if (!recipients_modified) {
-            if (type == ComposeType.REPLY || type == ComposeType.REPLY_ALL)
-                this.to_entry.addresses = Geary.RFC822.Utils.merge_addresses(to_entry.addresses,
-                    to_addresses);
-            if (type == ComposeType.REPLY_ALL)
+            if (type == REPLY_SENDER || type == REPLY_ALL) {
+                this.to_entry.addresses = Geary.RFC822.Utils.merge_addresses(
+                    to_entry.addresses,
+                    to_addresses
+                );
+            }
+            if (type == REPLY_ALL) {
                 this.cc_entry.addresses = Geary.RFC822.Utils.remove_addresses(
-                    Geary.RFC822.Utils.merge_addresses(this.cc_entry.addresses, cc_addresses),
-                    this.to_entry.addresses);
-            else
-                this.cc_entry.addresses = Geary.RFC822.Utils.remove_addresses(this.cc_entry.addresses,
-                    this.to_entry.addresses);
+                    Geary.RFC822.Utils.merge_addresses(
+                        this.cc_entry.addresses, cc_addresses
+                    ),
+                    this.to_entry.addresses
+                );
+            } else {
+                this.cc_entry.addresses = Geary.RFC822.Utils.remove_addresses(
+                    this.cc_entry.addresses,
+                    this.to_entry.addresses
+                );
+            }
         }
 
         if (referred.message_id != null) {
@@ -1493,6 +1508,27 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
         }
     }
 
+    private async void finish_loading(string body,
+                                      string quote,
+                                      bool is_body_complete,
+                                      GLib.Cancellable? cancellable) {
+        update_attachments_view();
+        update_pending_attachments(this.pending_include, true);
+
+        this.editor.load_html(
+            body,
+            quote,
+            this.top_posting,
+            is_body_complete
+        );
+
+        try {
+            yield open_draft_manager(this.saved_id, cancellable);
+        } catch (Error e) {
+            debug("Could not open draft manager: %s", e.message);
+        }
+    }
+
     private async bool should_send() {
         bool has_subject = !Geary.String.is_empty(subject.strip());
         bool has_attachment = this.attached_files.size > 0;
@@ -1627,7 +1663,6 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
             this.draft_manager = null;
             this.draft_status_text = "";
-            this.current_draft_id = null;
 
             old_manager.notify[Geary.App.DraftManager.PROP_DRAFT_STATE]
                 .disconnect(on_draft_state_changed);
@@ -2666,7 +2701,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
     }
 
     private void on_draft_id_changed() {
-        this.current_draft_id = this.draft_manager.current_draft_id;
+        this.saved_id = this.draft_manager.current_draft_id;
     }
 
     private void on_draft_manager_fatal(Error err) {
diff --git a/src/client/conversation-viewer/conversation-contact-popover.vala 
b/src/client/conversation-viewer/conversation-contact-popover.vala
index 9d58193c..0aa329a9 100644
--- a/src/client/conversation-viewer/conversation-contact-popover.vala
+++ b/src/client/conversation-viewer/conversation-contact-popover.vala
@@ -240,7 +240,7 @@ public class Conversation.ContactPopover : Gtk.Popover {
     private void on_new_conversation() {
         var main = this.get_toplevel() as Application.MainWindow;
         if (main != null) {
-            main.open_composer_for_mailbox(this.mailbox);
+            main.application.new_composer.begin(this.mailbox);
         }
     }
 
diff --git a/src/client/conversation-viewer/conversation-list-box.vala 
b/src/client/conversation-viewer/conversation-list-box.vala
index 81e22d86..e727a3cb 100644
--- a/src/client/conversation-viewer/conversation-list-box.vala
+++ b/src/client/conversation-viewer/conversation-list-box.vala
@@ -891,8 +891,8 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
         add(row);
         this.current_composer = row;
 
-        embed.composer.notify["current-draft-id"].connect(
-            (id) => { this.draft_id = embed.composer.current_draft_id; }
+        embed.composer.notify["saved-id"].connect(
+            (id) => { this.draft_id = embed.composer.saved_id; }
         );
         embed.vanished.connect(() => {
                 this.current_composer = null;
diff --git a/src/client/conversation-viewer/conversation-viewer.vala 
b/src/client/conversation-viewer/conversation-viewer.vala
index 461c7c94..dbb50c4d 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -187,7 +187,7 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
         if (this.current_list != null) {
             this.current_list.add_embedded_composer(
                 embed,
-                composer.current_draft_id != null
+                composer.saved_id != null
             );
             composer.update_window_title();
         }


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