[geary/wip/721828-undo] Working on getting client notified of message undo before round-trip
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/721828-undo] Working on getting client notified of message undo before round-trip
- Date: Wed, 24 Dec 2014 01:53:25 +0000 (UTC)
commit babc2ef7f3342069269c7c12da35894cde09454f
Author: Jim Nelson <jim yorba org>
Date: Tue Dec 23 17:52:10 2014 -0800
Working on getting client notified of message undo before round-trip
Waiting to synchronize with server is too slow; this partial work is
to get the client notified that the message has returned to the folder
before we have actually synchronized with the server.
src/CMakeLists.txt | 1 +
src/engine/abstract/geary-abstract-account.vala | 10 +++++
src/engine/abstract/geary-abstract-folder.vala | 8 ++++
src/engine/api/geary-account.vala | 20 +++++++++++
src/engine/api/geary-folder.vala | 27 ++++++++++++++
src/engine/app/app-conversation-monitor.vala | 37 ++++++++++++++++++++
.../app-predict-insert-operation.vala | 19 ++++++++++
.../imap-engine/imap-engine-generic-account.vala | 4 ++
.../imap-engine/imap-engine-minimal-folder.vala | 2 +-
.../imap-engine/imap-engine-revokable-move.vala | 23 +++++++++---
.../replay-ops/imap-engine-move-email.vala | 18 ++++++++-
src/engine/imap/api/imap-folder.vala | 2 +-
12 files changed, 161 insertions(+), 10 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3ce824a..3b70b55 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -57,6 +57,7 @@ engine/app/conversation-monitor/app-external-append-operation.vala
engine/app/conversation-monitor/app-fill-window-operation.vala
engine/app/conversation-monitor/app-local-load-operation.vala
engine/app/conversation-monitor/app-local-search-operation.vala
+engine/app/conversation-monitor/app-predict-insert-operation.vala
engine/app/conversation-monitor/app-remove-operation.vala
engine/app/conversation-monitor/app-reseed-operation.vala
engine/app/conversation-monitor/app-terminate-operation.vala
diff --git a/src/engine/abstract/geary-abstract-account.vala b/src/engine/abstract/geary-abstract-account.vala
index 1d590bd..f861aba 100644
--- a/src/engine/abstract/geary-abstract-account.vala
+++ b/src/engine/abstract/geary-abstract-account.vala
@@ -63,6 +63,16 @@ public abstract class Geary.AbstractAccount : BaseObject, Geary.Account {
email_flags_changed(folder, flag_map);
}
+ protected virtual void notify_predict_email_inserted(Geary.Folder folder,
+ Gee.Collection<Geary.EmailIdentifier> ids) {
+ predict_email_inserted(folder, ids);
+ }
+
+ protected virtual void notify_unpredict_email_inserted(Geary.Folder folder,
+ Gee.Collection<Geary.EmailIdentifier> ids) {
+ unpredict_email_inserted(folder, ids);
+ }
+
protected virtual void notify_opened() {
opened();
}
diff --git a/src/engine/abstract/geary-abstract-folder.vala b/src/engine/abstract/geary-abstract-folder.vala
index aeed7cd..0080fc1 100644
--- a/src/engine/abstract/geary-abstract-folder.vala
+++ b/src/engine/abstract/geary-abstract-folder.vala
@@ -56,6 +56,14 @@ public abstract class Geary.AbstractFolder : BaseObject, Geary.Folder {
email_count_changed(new_count, reason);
}
+ internal virtual void notify_predict_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
+ predict_email_inserted(ids);
+ }
+
+ internal virtual void notify_unpredict_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids) {
+ unpredict_email_inserted(ids);
+ }
+
internal virtual void notify_email_flags_changed(Gee.Map<Geary.EmailIdentifier,
Geary.EmailFlags> flag_map) {
email_flags_changed(flag_map);
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 152508e..c15843e 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -109,6 +109,20 @@ public interface Geary.Account : BaseObject {
Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map);
/**
+ * Fired when email is predicted to be inserted into the { link Folder}.
+ *
+ * @see Folder.predict_email_inserted
+ */
+ public signal void predict_email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier>
ids);
+
+ /**
+ * Fired when email predicted to be inserted into the { link Folder} will not be arriving.
+ *
+ * @see Folder.unpredict_email_inserted
+ */
+ public signal void unpredict_email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier>
ids);
+
+ /**
* Signal notification method for subclasses to use.
*/
protected abstract void notify_opened();
@@ -178,6 +192,12 @@ public interface Geary.Account : BaseObject {
protected abstract void notify_email_flags_changed(Geary.Folder folder,
Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map);
+ protected abstract void notify_predict_email_inserted(Geary.Folder folder,
+ Gee.Collection<Geary.EmailIdentifier> ids);
+
+ protected abstract void notify_unpredict_email_inserted(Geary.Folder folder,
+ Gee.Collection<Geary.EmailIdentifier> ids);
+
/**
* A utility method to sort a Gee.Collection of { link Folder}s by their { link FolderPath}s
* to ensure they comport with { link folders_available_unavailable} and
diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala
index 69c6d30..3fb411f 100644
--- a/src/engine/api/geary-folder.vala
+++ b/src/engine/api/geary-folder.vala
@@ -259,6 +259,29 @@ public interface Geary.Folder : BaseObject {
public signal void email_count_changed(int new_count, CountChangeReason reason);
/**
+ * Fired when an operation on another { link Folder} predicts that email will be inserted
+ * into this Folder.
+ *
+ * It should ''not'' be assumed that the email identifiers can be loaded from this Folder,
+ * especially since this can be fired when the Folder is closed. Use
+ * { link Account.local_fetch_email_async} to fetch whatever portion of the email is locally
+ * available without regards to folder association.
+ *
+ * Since it's unknown what the predicted email's ordering will be on the remote folder, there
+ * is no predict_email_appended.
+ */
+ public signal void predict_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
+
+ /**
+ * Fired when an operation on another { link Folder} predicted that email will be inserted into
+ * this Folder, but that prediction is now incorrect.
+ *
+ * This most often occurs because the original prediction was made ''before'' the remote
+ * operation took place, and then the operation failed.
+ */
+ public signal void unpredict_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
+
+ /**
* Fired when the supplied email flags have changed, whether due to local action or reported by
* the server.
*/
@@ -304,6 +327,10 @@ public interface Geary.Folder : BaseObject {
protected abstract void notify_email_count_changed(int new_count, CountChangeReason reason);
+ protected abstract void notify_predict_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
+
+ protected abstract void notify_unpredict_email_inserted(Gee.Collection<Geary.EmailIdentifier> ids);
+
protected abstract void notify_email_flags_changed(Gee.Map<Geary.EmailIdentifier,
Geary.EmailFlags> flag_map);
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index 14bf509..bdd9da2 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -286,6 +286,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
folder.opened.connect(on_folder_opened);
folder.account.email_flags_changed.connect(on_account_email_flags_changed);
folder.account.email_locally_complete.connect(on_account_email_locally_complete);
+ folder.account.predict_email_inserted.connect(on_account_predict_email_inserted);
// TODO: handle removed email
try {
@@ -299,6 +300,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
folder.opened.disconnect(on_folder_opened);
folder.account.email_flags_changed.disconnect(on_account_email_flags_changed);
folder.account.email_locally_complete.disconnect(on_account_email_locally_complete);
+ folder.account.predict_email_inserted.disconnect(on_account_predict_email_inserted);
throw err;
}
@@ -348,6 +350,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
folder.opened.disconnect(on_folder_opened);
folder.account.email_flags_changed.disconnect(on_account_email_flags_changed);
folder.account.email_locally_complete.disconnect(on_account_email_locally_complete);
+ folder.account.predict_email_inserted.disconnect(on_account_predict_email_inserted);
Error? close_err = null;
if (close_folder) {
@@ -630,6 +633,18 @@ public class Geary.App.ConversationMonitor : BaseObject {
operation_queue.add(new ExternalAppendOperation(this, folder, complete_ids));
}
+ private void on_account_predict_email_inserted(Geary.Folder folder,
+ Gee.Collection<Geary.EmailIdentifier> complete_ids) {
+ debug("PREDICTED: %s %d", folder.to_string(), complete_ids.size);
+
+ // only load in predicted emails going to monitored folder; don't attempt to merge in
+ // all messages predicted anywhere in the account
+ if (this.folder != folder)
+ return;
+
+ operation_queue.add(new PredictInsertOperation(this, complete_ids));
+ }
+
internal async void append_emails_async(Gee.Collection<Geary.EmailIdentifier> appended_ids) {
debug("%d message(s) appended to %s, fetching to add to conversations...", appended_ids.size,
folder.to_string());
@@ -677,6 +692,28 @@ public class Geary.App.ConversationMonitor : BaseObject {
yield external_load_by_sparse_id(folder, appended_ids, Geary.Folder.ListFlags.NONE, null);
}
+ internal async void predict_emails_async(Gee.Collection<Geary.EmailIdentifier> ids) {
+ debug("Loading predicted local email: %d", ids.size);
+ Gee.ArrayList<Geary.Email> emails = new Gee.ArrayList<Geary.Email>();
+ foreach (Geary.EmailIdentifier id in ids) {
+ try {
+ emails.add(yield folder.account.local_fetch_email_async(id, required_fields));
+ } catch (Error err) {
+ // Ignore error; INCOMPLETE and NOT_FOUND are just part of the fuzziness of
+ // prediction
+ debug("Unable to locally fetch predicted email in %s: %s", folder.to_string(),
+ err.message);
+ }
+ }
+
+ debug("Loaded predicted local email: %d", emails.size);
+
+ if (emails.size == 0)
+ return;
+
+ yield process_email_async(emails, new ProcessJobContext(false));
+ }
+
private void on_account_email_flags_changed(Geary.Folder folder,
Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map) {
foreach (Geary.EmailIdentifier id in map.keys) {
diff --git a/src/engine/app/conversation-monitor/app-predict-insert-operation.vala
b/src/engine/app/conversation-monitor/app-predict-insert-operation.vala
new file mode 100644
index 0000000..4036a52
--- /dev/null
+++ b/src/engine/app/conversation-monitor/app-predict-insert-operation.vala
@@ -0,0 +1,19 @@
+/* Copyright 2014 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.App.PredictInsertOperation : ConversationOperation {
+ private Gee.Collection<Geary.EmailIdentifier> predicted_ids;
+
+ public PredictInsertOperation(ConversationMonitor monitor,
+ Gee.Collection<Geary.EmailIdentifier> predicted_ids) {
+ base(monitor);
+ this.predicted_ids = predicted_ids;
+ }
+
+ public override async void execute_async() {
+ yield monitor.predict_emails_async(predicted_ids);
+ }
+}
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 50f6001..20ccbe0 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -60,6 +60,8 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
folder.email_removed.connect(notify_email_removed);
folder.email_locally_complete.connect(notify_email_locally_complete);
folder.email_flags_changed.connect(notify_email_flags_changed);
+ folder.predict_email_inserted.connect(notify_predict_email_inserted);
+ folder.unpredict_email_inserted.connect(notify_unpredict_email_inserted);
}
}
if (unavailable != null) {
@@ -69,6 +71,8 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
folder.email_removed.disconnect(notify_email_removed);
folder.email_locally_complete.disconnect(notify_email_locally_complete);
folder.email_flags_changed.disconnect(notify_email_flags_changed);
+ folder.predict_email_inserted.disconnect(notify_predict_email_inserted);
+ folder.unpredict_email_inserted.disconnect(notify_unpredict_email_inserted);
}
}
}
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index 32e2936..c3956de 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -1271,7 +1271,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
replay_queue.schedule(move);
yield move.wait_for_ready_async(cancellable);
- return new RevokableMove(_account, path, destination, move.destination_uids);
+ return new RevokableMove(_account, path, destination, move.destination_ids);
}
private void on_email_flags_changed(Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> changed) {
diff --git a/src/engine/imap-engine/imap-engine-revokable-move.vala
b/src/engine/imap-engine/imap-engine-revokable-move.vala
index 646e4bf..f670116 100644
--- a/src/engine/imap-engine/imap-engine-revokable-move.vala
+++ b/src/engine/imap-engine/imap-engine-revokable-move.vala
@@ -8,14 +8,18 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
private GenericAccount account;
private FolderPath original_source;
private FolderPath original_dest;
- private Gee.Set<Imap.UID> uids;
+ private Gee.Set<ImapDB.EmailIdentifier> destination_ids;
+ /**
+ * Supplied EmailIdentifiers *must* be loaded with UIDs of the messages on the *destination*
+ * folder. Do *not* merely stuff in here the EmailIdentifier from the source folder.
+ */
public RevokableMove(GenericAccount account, FolderPath original_source, FolderPath original_dest,
- Gee.Set<Imap.UID> uids) {
+ Gee.Set<ImapDB.EmailIdentifier> destination_ids) {
this.account = account;
this.original_source = original_source;
this.original_dest = original_dest;
- this.uids = uids;
+ this.destination_ids = destination_ids;
account.folders_available_unavailable.connect(on_folders_available_unavailable);
account.email_removed.connect(on_folder_email_removed);
@@ -56,8 +60,10 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
yield dest_folder.open_async(Geary.Folder.OpenFlags.NO_DELAY, cancellable);
// watch out for messages detected as gone when folder is opened
- if (can_revoke && !yield dest_folder.revoke_move_async(uids, original_source, cancellable))
+ if (can_revoke && !yield dest_folder.revoke_move_async(destination_ids, original_source,
+ cancellable)) {
can_revoke = false;
+ }
} finally {
// note that the Cancellable is not used
try {
@@ -99,8 +105,13 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
.to_hash_set();
// otherwise, ability to revoke is best-effort
- uids.remove_all(removed_uids);
- can_revoke = uids.size > 0;
+ Gee.Iterator<ImapDB.EmailIdentifier> iter = destination_ids.iterator();
+ while (iter.next()) {
+ if (removed_uids.contains(iter.get().uid))
+ iter.remove();
+ }
+
+ can_revoke = destination_ids.size > 0;
}
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
index a93a742..30b19a4 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email.vala
@@ -5,7 +5,7 @@
*/
private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation {
- public Gee.Set<Imap.UID>? destination_uids { get; private set; default = null; }
+ public Gee.Set<ImapDb.EmailIdentifier>? destination_ids { get; private set; default = null; }
private MinimalFolder engine;
private Gee.List<ImapDB.EmailIdentifier> to_move = new Gee.ArrayList<ImapDB.EmailIdentifier>();
@@ -52,6 +52,13 @@ private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation
engine.notify_email_count_changed(Numeric.int_floor(original_count - to_move.size, 0),
Geary.Folder.CountChangeReason.REMOVED);
+ MinimalFolder? dest_folder = (yield engine.account.fetch_folder_async(destination, cancellable))
+ as MinimalFolder;
+ if (dest_folder != null) {
+ debug("NOTIFY PREDICTED: %d", moved_ids.size);
+ dest_folder.notify_predict_email_inserted(moved_ids);
+ }
+
return ReplayOperation.Status.CONTINUE;
}
@@ -69,7 +76,9 @@ private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation
if (cancellable != null && cancellable.is_cancelled())
throw new IOError.CANCELLED("Move email to %s cancelled", engine.remote_folder.to_string());
- Gee.Set<Imap.UID> acc_uids = new Gee.HashSet<Imap.UID>();
+ // TODO: Need to generate fully-valid ImapDB.EmailIdentifiers for the destination folder
+ // (with message_ids and UIDs properly associated)
+ Gee.Set<ImapDB.EmailIdentifier> acc_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
Gee.List<Imap.MessageSet> msg_sets = Imap.MessageSet.uid_sparse(
ImapDB.EmailIdentifier.to_uids(moved_ids));
@@ -93,6 +102,11 @@ private class Geary.ImapEngine.MoveEmail : Geary.ImapEngine.SendReplayOperation
engine.notify_email_inserted(moved_ids);
engine.notify_email_count_changed(original_count, Geary.Folder.CountChangeReason.INSERTED);
+
+ MinimalFolder? dest_folder = (yield engine.account.fetch_folder_async(destination, cancellable))
+ as MinimalFolder;
+ if (dest_folder != null)
+ dest_folder.notify_unpredict_email_inserted(moved_ids);
}
public override string describe_state() {
diff --git a/src/engine/imap/api/imap-folder.vala b/src/engine/imap/api/imap-folder.vala
index f40ae46..d5bde97 100644
--- a/src/engine/imap/api/imap-folder.vala
+++ b/src/engine/imap/api/imap-folder.vala
@@ -675,7 +675,7 @@ private class Geary.Imap.Folder : BaseObject {
yield exec_commands_async(cmds, null, null, cancellable);
}
- public async Gee.Set<UID>? copy_email_async(MessageSet msg_set, Geary.FolderPath destination,
+ public async Gee.List<UID>? copy_email_async(MessageSet msg_set, Geary.FolderPath destination,
Cancellable? cancellable) throws Error {
check_open();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]