Implement undoing saved composers

Add Application::save_composed_email method and SaveComposerEmail
command, execute the latter when the former is called. Update
Composer.Widget::save_and_exit_async to call this as needed instead
of manually disposing of the widget.
This commit is contained in:
Michael Gratton 2019-11-12 15:21:26 +11:00 committed by Michael James Gratton
parent 373504025f
commit de57355a1e
2 changed files with 101 additions and 9 deletions

View file

@ -464,6 +464,27 @@ public class Application.Controller : Geary.BaseObject {
}
}
/** 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));
}
}
}
/** Queues a composer to be discarded. */
public async void discard_composed_email(Composer.Widget composer) {
AccountContext? context = this.accounts.get(
@ -2739,6 +2760,64 @@ private class Application.SendComposerCommand : ComposerCommand {
}
private class Application.SaveComposerCommand : ComposerCommand {
private const int DESTROY_TIMEOUT_SEC = 30 * 60;
public override bool can_redo {
get { return false; }
}
private Controller controller;
private Geary.TimeoutManager destroy_timer;
public SaveComposerCommand(Controller controller,
Composer.Widget composer) {
base(composer);
this.controller = controller;
this.destroy_timer = new Geary.TimeoutManager.seconds(
DESTROY_TIMEOUT_SEC,
on_destroy_timeout
);
}
public override async void execute(GLib.Cancellable? cancellable)
throws GLib.Error {
Geary.ComposedEmail email = yield this.composer.get_composed_email();
/// Translators: The label for an in-app notification. The
/// string substitution is a list of recipients of the email.
this.executed_label = _(
"Email to %s saved"
).printf(Util.Email.to_short_recipient_display(email));
this.destroy_timer.start();
}
public override async void undo(GLib.Cancellable? cancellable)
throws GLib.Error {
if (this.composer != null) {
this.destroy_timer.reset();
this.composer.set_enabled(true);
this.controller.show_composer(this.composer, null);
clear_composer();
} else {
/// Translators: A label for an in-app notification.
this.undone_label = _(
"Composer could not be restored"
);
}
}
private void on_destroy_timeout() {
close_composer();
}
}
private class Application.DiscardComposerCommand : ComposerCommand {
@ -2783,6 +2862,7 @@ private class Application.DiscardComposerCommand : ComposerCommand {
this.controller.show_composer(this.composer, null);
clear_composer();
} else {
/// Translators: A label for an in-app notification.
this.undone_label = _(
"Composer could not be restored"
);

View file

@ -1621,16 +1621,28 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
private async void save_and_exit_async() {
this.is_closing = true;
set_enabled(false);
try {
yield save_draft();
} catch (GLib.Error error) {
this.application.controller.report_problem(
new Geary.AccountProblemReport(
this.account.information, error
)
);
if (!is_blank) {
try {
yield save_draft();
} catch (GLib.Error error) {
this.application.controller.report_problem(
new Geary.AccountProblemReport(
this.account.information, error
)
);
}
// Pass on to the controller so the draft can be re-opened
// on undo
if (this.container != null) {
this.container.close();
}
yield this.application.controller.save_composed_email(this);
} else {
// The composer is blank, so drop the mic and walk away
yield close();
}
yield close();
}
private async void discard_and_exit_async() {