Closes #7374 Ensure draft is deleted on send

This commit is contained in:
Eric Gregory 2013-09-09 17:51:47 -07:00
parent 735b093b0d
commit afe33527ef
5 changed files with 103 additions and 21 deletions

View file

@ -203,6 +203,7 @@ engine/imap-engine/other/imap-engine-other-account.vala
engine/imap-engine/other/imap-engine-other-folder.vala
engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
engine/imap-engine/replay-ops/imap-engine-copy-email.vala
engine/imap-engine/replay-ops/imap-engine-create-email.vala
engine/imap-engine/replay-ops/imap-engine-expunge-email.vala
engine/imap-engine/replay-ops/imap-engine-fetch-email.vala
engine/imap-engine/replay-ops/imap-engine-list-email-by-id.vala

View file

@ -182,6 +182,7 @@ public class ComposerWindow : Gtk.Window {
private Geary.EmailIdentifier? draft_id = null;
private uint draft_save_timeout_id = 0;
private Cancellable cancellable_drafts = new Cancellable();
private Cancellable cancellable_save_draft = new Cancellable();
private WebKit.WebView editor;
// We need to keep a reference to the edit-fixer in composer-window, so it doesn't get
@ -764,6 +765,8 @@ public class ComposerWindow : Gtk.Window {
// Used internally by on_send()
private async void on_send_async() {
cancellable_save_draft.cancel();
linkify_document(editor.get_dom_document());
// Perform send.
@ -804,12 +807,12 @@ public class ComposerWindow : Gtk.Window {
// Save to the draft folder, if available.
// Note that drafts are NOT "linkified."
private bool save_draft() {
save_async.begin();
save_async.begin(cancellable_save_draft);
return false;
}
private async void save_async() {
private async void save_async(Cancellable? cancellable) {
if (drafts_folder == null)
return;
@ -818,7 +821,7 @@ public class ComposerWindow : Gtk.Window {
try {
draft_id = yield drafts_folder.create_email_async(new Geary.RFC822.Message.from_composed_email(
get_composed_email()), new Geary.EmailFlags(), null, draft_id, null);
get_composed_email()), new Geary.EmailFlags(), null, draft_id, cancellable);
draft_save_label.label = DRAFT_SAVED_TEXT;
} catch (Error e) {
@ -852,7 +855,7 @@ public class ComposerWindow : Gtk.Window {
make_gui_insensitive();
// Do the save.
yield save_async();
yield save_async(null);
destroy();
}

View file

@ -1156,30 +1156,32 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
if (id != null)
check_id("create_email_async", id);
// TODO: Move this into a ReplayQueue operation
yield wait_for_open_async(cancellable);
// use IMAP APPEND command on remote folders, which doesn't require opening a folder
Geary.EmailIdentifier? ret = yield remote_folder.create_email_async(rfc822, flags,
date_received, cancellable);
if (ret != null) {
// TODO: need to prevent gaps that may occur here
Geary.Email created = new Geary.Email(ret);
Gee.Map<Geary.Email, bool> results = yield local_folder.create_or_merge_email_async(
new Collection.SingleItem<Geary.Email>(created), cancellable);
if (results.size > 0)
ret = Collection.get_first<Geary.Email>(results.keys).id;
Error? cancel_error = null;
Geary.EmailIdentifier? ret = null;
try {
CreateEmail create = new CreateEmail(this, rfc822, flags, date_received, cancellable);
replay_queue.schedule(create);
yield create.wait_for_ready_async(cancellable);
ret = create.created_id;
} catch (Error e) {
if (e is IOError.CANCELLED)
cancel_error = e;
else
ret = null;
throw e;
}
// Remove old message.
if (id != null) {
Geary.FolderSupport.Remove? remove_folder = this as Geary.FolderSupport.Remove;
if (remove_folder != null)
yield remove_folder.remove_single_email_async(id, cancellable);
yield remove_folder.remove_single_email_async(id, null);
}
// If the user cancelled the operation, throw the error here.
if (cancel_error != null)
throw cancel_error;
return ret;
}
}

View file

@ -0,0 +1,73 @@
/* Copyright 2013 Yorba Foundation
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
private class Geary.ImapEngine.CreateEmail : Geary.ImapEngine.SendReplayOperation {
public Geary.EmailIdentifier? created_id { get; private set; default = null; }
private GenericFolder engine;
private RFC822.Message rfc822;
private Geary.EmailFlags? flags;
private DateTime? date_received;
private Cancellable? cancellable;
public CreateEmail(GenericFolder engine, RFC822.Message rfc822, Geary.EmailFlags? flags,
DateTime? date_received, Cancellable? cancellable) {
base.only_remote("CreateEmail");
this.engine = engine;
this.rfc822 = rfc822;
this.flags = flags;
this.date_received = date_received;
this.cancellable = cancellable;
}
public override async ReplayOperation.Status replay_local_async() throws Error {
return ReplayOperation.Status.CONTINUE;
}
public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
}
public override async void backout_local_async() throws Error {
}
public override string describe_state() {
return "";
}
public override async ReplayOperation.Status replay_remote_async() throws Error {
// Deal with cancellable manually since create_email_async cannot be cancelled.
if (cancellable.is_cancelled())
throw new IOError.CANCELLED("CreateEmail op cancelled immediately");
// use IMAP APPEND command on remote folders, which doesn't require opening a folder
created_id = yield engine.remote_folder.create_email_async(rfc822, flags, date_received);
if (created_id == null)
return ReplayOperation.Status.FAILED;
// If the user cancelled the operation, we need to wipe the new message to keep this
// operation atomic.
if (cancellable.is_cancelled()) {
yield engine.remote_folder.remove_email_async(
new Imap.MessageSet.uid(((ImapDB.EmailIdentifier) created_id).uid), null);
throw new IOError.CANCELLED("CreateEmail op cancelled after create");
}
// TODO: need to prevent gaps that may occur here
Geary.Email created = new Geary.Email(created_id);
Gee.Map<Geary.Email, bool> results = yield engine.local_folder.create_or_merge_email_async(
new Collection.SingleItem<Geary.Email>(created), cancellable);
if (results.size > 0)
created_id = Collection.get_first<Geary.Email>(results.keys).id;
else
created_id = null;
return ReplayOperation.Status.COMPLETED;
}
}

View file

@ -862,8 +862,11 @@ private class Geary.Imap.Folder : BaseObject {
}
// Returns a no-message-id ImapDB.EmailIdentifier with the UID stored in it.
// This method does not take a cancellable; there is currently no way to tell if an email was
// created or not if exec_commands_async() is cancelled during the append. For atomicity's sake,
// callers need to remove the returned email ID if a cancel occurred.
public async Geary.EmailIdentifier? create_email_async(RFC822.Message message, Geary.EmailFlags? flags,
DateTime? date_received, Cancellable? cancellable) throws Error {
DateTime? date_received) throws Error {
check_open();
MessageFlags? msg_flags = null;
@ -880,7 +883,7 @@ private class Geary.Imap.Folder : BaseObject {
msg_flags, internaldate, message.get_network_buffer(false));
Gee.Map<Command, StatusResponse> responses = yield exec_commands_async(
new Collection.SingleItem<AppendCommand>(cmd), null, null, cancellable);
new Collection.SingleItem<AppendCommand>(cmd), null, null, null);
// Grab the response and parse out the UID, if available.
StatusResponse response = responses.get(cmd);