[geary/mjog/account-command-stacks: 41/77] Implement conversation flag updating as undoable commands
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/mjog/account-command-stacks: 41/77] Implement conversation flag updating as undoable commands
- Date: Tue, 5 Nov 2019 00:36:32 +0000 (UTC)
commit d8a864019d6d55e6a6d6f10ea8a8f5c2e8dec19f
Author: Michael Gratton <mike vee net>
Date: Sat Oct 5 19:50:50 2019 +1000
Implement conversation flag updating as undoable commands
This lets us undo marking as read/unread/starred/etc.
src/client/application/application-controller.vala | 174 +++++++++++++++++++++
src/client/components/main-window.vala | 103 +++++++++++-
.../conversation-list/conversation-list-view.vala | 31 ++--
3 files changed, 285 insertions(+), 23 deletions(-)
---
diff --git a/src/client/application/application-controller.vala
b/src/client/application/application-controller.vala
index 8dc62119..a03c3428 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -525,6 +525,137 @@ public class Application.Controller : Geary.BaseObject {
return (context != null) ? context.contacts : null;
}
+ /**
+ * Updates flags for a collection of conversations.
+ *
+ * If `prefer_adding` is true, this will add the flag if not set
+ * on all conversations or else will remove it. If false, this
+ * will remove the flag if not set on all conversations or else
+ * add it.
+ */
+ public async void mark_conversations(Geary.Account target,
+ Gee.Collection<Geary.App.Conversation> conversations,
+ Geary.NamedFlag flag,
+ bool prefer_adding)
+ throws GLib.Error {
+ Geary.Iterable<Geary.App.Conversation> selecting =
+ Geary.traverse(conversations);
+ Geary.EmailFlags flags = new Geary.EmailFlags();
+
+ if (flag.equal_to(Geary.EmailFlags.UNREAD)) {
+ selecting = selecting.filter(c => prefer_adding ^ c.is_unread());
+ flags.add(Geary.EmailFlags.UNREAD);
+ } else if (flag.equal_to(Geary.EmailFlags.FLAGGED)) {
+ selecting = selecting.filter(c => prefer_adding ^ c.is_flagged());
+ flags.add(Geary.EmailFlags.FLAGGED);
+ } else {
+ throw new Geary.EngineError.UNSUPPORTED(
+ "Marking as %s is not supported", flag.to_string()
+ );
+ }
+
+ Gee.Collection<Geary.EmailIdentifier>? messages = null;
+ Gee.Collection<Geary.App.Conversation> selected =
+ selecting.to_linked_list();
+
+ bool do_add = prefer_adding ^ selected.is_empty;
+ if (selected.is_empty) {
+ selected = conversations;
+ }
+
+ if (do_add) {
+ // Only apply to the latest in-folder message in
+ // conversations that don't already have the flag, since
+ // we don't want to flag every message in the conversation
+ messages = Geary.traverse(selected).map<Geary.EmailIdentifier>(
+ c => c.get_latest_recv_email(IN_FOLDER_OUT_OF_FOLDER).id
+ ).to_linked_list();
+ } else {
+ // Remove the flag from those that have it
+ messages = new Gee.LinkedList<Geary.EmailIdentifier>();
+ foreach (Geary.App.Conversation convo in selected) {
+ foreach (Geary.Email email in
+ convo.get_emails(RECV_DATE_DESCENDING)) {
+ if (email.email_flags != null &&
+ email.email_flags.contains(flag)) {
+ messages.add(email.id);
+ }
+ }
+ }
+ }
+
+ AccountContext? context = this.accounts.get(target.information);
+ if (context != null) {
+ yield this.commands.execute(
+ new MarkEmailCommand(
+ context.emails,
+ messages,
+ do_add ? flags : null,
+ do_add ? null : flags,
+ /// Translators: Label for in-app undo notification
+ ngettext(
+ "Conversation marked",
+ "Conversations marked",
+ selected.size
+ ),
+ /// Translators: Label for in-app undo notification
+ ngettext(
+ "Conversation un-marked",
+ "Conversations un-marked",
+ selected.size
+ )
+
+ ),
+ context.cancellable
+ );
+ }
+ }
+
+ /**
+ * Updates flags for a collection of email.
+ *
+ * This should only be used when working with specific messages
+ * (for example, marking a specific message in a conversation)
+ * rather than when working with whole conversations. In that
+ * case, use {@link mark_conversations}.
+ */
+ public async void mark_messages(Geary.Account target,
+ Gee.Collection<Geary.EmailIdentifier> messages,
+ Geary.EmailFlags? to_add,
+ Geary.EmailFlags? to_remove)
+ throws GLib.Error {
+ AccountContext? context = this.accounts.get(target.information);
+ if (context != null) {
+ yield this.commands.execute(
+ new MarkEmailCommand(
+ context.emails,
+ messages,
+ to_add,
+ to_remove,
+ /// Translators: Label for in-app undo notification
+ ngettext(
+ "Message marked",
+ "Messages marked",
+ messages.size
+ ),
+ /// Translators: Label for in-app undo notification
+ ngettext(
+ "Message un-marked",
+ "Messages un-marked",
+ messages.size
+ )
+ ),
+ context.cancellable
+ );
+ }
+ }
+
+ ),
+ context.cancellable
+ );
+ }
+ }
+
/** Expunges removed accounts while the controller remains open. */
internal async void expunge_accounts() {
try {
@@ -1958,3 +2089,46 @@ public class Application.Controller : Geary.BaseObject {
}
}
+
+
+private class Application.MarkEmailCommand : Command {
+
+
+ private Geary.App.EmailStore store;
+ private Gee.Collection<Geary.EmailIdentifier> messages;
+ private Geary.EmailFlags? to_add;
+ private Geary.EmailFlags? to_remove;
+
+
+ public MarkEmailCommand(Geary.App.EmailStore store,
+ Gee.Collection<Geary.EmailIdentifier> messages,
+ Geary.EmailFlags? to_add,
+ Geary.EmailFlags? to_remove,
+ string? executed_label = null,
+ string? undone_label = null) {
+ this.store = store;
+ this.messages = messages;
+ this.to_add = to_add;
+ this.to_remove = to_remove;
+
+ this.executed_label = executed_label;
+ this.undone_label = undone_label;
+ }
+
+ public override async void execute(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ yield this.store.mark_email_async(
+ this.messages, this.to_add, this.to_remove, cancellable
+ );
+ }
+
+ public override async void undo(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ yield this.store.mark_email_async(
+ this.messages, this.to_remove, this.to_add, cancellable
+ );
+ }
+
+}
+
+
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 1a446c34..cf30b1b5 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -544,6 +544,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
private void setup_layout(Configuration config) {
this.conversation_list_view = new ConversationListView(this);
this.conversation_list_view.load_more.connect(on_load_more);
+ this.conversation_list_view.mark_conversations.connect(on_mark_conversations);
this.conversation_list_view.conversations_selected.connect(on_conversations_selected);
this.conversation_viewer = new ConversationViewer(
@@ -1401,16 +1402,100 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
current_folder.special_folder_type != Geary.SpecialFolderType.OUTBOX);
}
+ private void on_mark_conversations(Gee.Collection<Geary.App.Conversation> conversations,
+ Geary.NamedFlag flag) {
+ Geary.Account? target = this.current_account;
+ if (target != null) {
+ this.application.controller.mark_conversations.begin(
+ target,
+ conversations,
+ flag,
+ true,
+ (obj, res) => {
+ try {
+ this.application.controller.mark_conversations.end(res);
+ } catch (GLib.Error err) {
+ handle_error(target.information, err);
+ }
+ }
+ );
+ }
+ }
+
private void on_mark_as_read() {
+ Geary.Account? target = this.current_account;
+ if (target != null) {
+ this.application.controller.mark_conversations.begin(
+ target,
+ this.conversation_list_view.get_selected_conversations(),
+ Geary.EmailFlags.UNREAD,
+ false,
+ (obj, res) => {
+ try {
+ this.application.controller.mark_conversations.end(res);
+ } catch (GLib.Error err) {
+ handle_error(target.information, err);
+ }
+ }
+ );
+ }
}
private void on_mark_as_unread() {
+ Geary.Account? target = this.current_account;
+ if (target != null) {
+ this.application.controller.mark_conversations.begin(
+ target,
+ this.conversation_list_view.get_selected_conversations(),
+ Geary.EmailFlags.UNREAD,
+ true,
+ (obj, res) => {
+ try {
+ this.application.controller.mark_conversations.end(res);
+ } catch (GLib.Error err) {
+ handle_error(target.information, err);
+ }
+ }
+ );
+ }
}
private void on_mark_as_starred() {
+ Geary.Account? target = this.current_account;
+ if (target != null) {
+ this.application.controller.mark_conversations.begin(
+ target,
+ this.conversation_list_view.get_selected_conversations(),
+ Geary.EmailFlags.FLAGGED,
+ true,
+ (obj, res) => {
+ try {
+ this.application.controller.mark_conversations.end(res);
+ } catch (GLib.Error err) {
+ handle_error(target.information, err);
+ }
+ }
+ );
+ }
}
private void on_mark_as_unstarred() {
+ Geary.Account? target = this.current_account;
+ if (target != null) {
+ this.application.controller.mark_conversations.begin(
+ target,
+ this.conversation_list_view.get_selected_conversations(),
+ Geary.EmailFlags.FLAGGED,
+ false,
+ (obj, res) => {
+ try {
+ this.application.controller.mark_conversations.end(res);
+ } catch (GLib.Error err) {
+ handle_error(target.information, err);
+ }
+ }
+ );
+ }
}
private void on_mark_as_spam_toggle() {
@@ -1437,9 +1522,25 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
private void on_empty_trash() {
// Individual message view action callbacks
- private void on_conversation_viewer_mark_emails(Gee.Collection<Geary.EmailIdentifier> email,
+ private void on_conversation_viewer_mark_emails(Gee.Collection<Geary.EmailIdentifier> messages,
Geary.EmailFlags? to_add,
Geary.EmailFlags? to_remove) {
+ Geary.Account? target = this.current_account;
+ if (target != null) {
+ this.application.controller.mark_messages.begin(
+ target,
+ messages,
+ to_add,
+ to_remove,
+ (obj, res) => {
+ try {
+ this.application.controller.mark_messages.end(res);
+ } catch (GLib.Error err) {
+ handle_error(target.information, err);
+ }
+ }
+ );
+ }
}
private void on_email_load_error(ConversationEmail view, GLib.Error err) {
diff --git a/src/client/conversation-list/conversation-list-view.vala
b/src/client/conversation-list/conversation-list-view.vala
index 39a503d9..3050935d 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -7,6 +7,7 @@
public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
const int LOAD_MORE_HEIGHT = 100;
+
// Used to be able to refer to the action names of the MainWindow
private weak MainWindow main_window;
@@ -29,7 +30,7 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
}
public signal void mark_conversations(Gee.Collection<Geary.App.Conversation> conversations,
- Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, bool only_mark_preview);
+ Geary.NamedFlag flag);
public signal void visible_conversations_changed(Gee.Set<Geary.App.Conversation> visible);
@@ -289,32 +290,18 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
// all selected conversations; otherwise, it just applies to this one.
Geary.App.Conversation conversation = get_model().get_conversation_at_path(path);
Gee.Collection<Geary.App.Conversation> to_mark;
- if (GearyApplication.instance.controller.get_selected_conversations().contains(conversation))
- to_mark = GearyApplication.instance.controller.get_selected_conversations();
+ if (this.selected.contains(conversation))
+ // take a copy of currently selected for handling to
+ // the signal
+ to_mark = get_selected_conversations();
else
- to_mark = Geary.iterate<Geary.App.Conversation>(conversation).to_array_list();
+ to_mark = Geary.Collection.single(conversation);
if (read_clicked) {
- // Read/unread.
- Geary.EmailFlags flags = new Geary.EmailFlags();
- flags.add(Geary.EmailFlags.UNREAD);
-
- if (conversation.is_unread())
- mark_conversations(to_mark, null, flags, false);
- else
- mark_conversations(to_mark, flags, null, true);
-
+ mark_conversations(to_mark, Geary.EmailFlags.UNREAD);
return true;
} else if (star_clicked) {
- // Starred/unstarred.
- Geary.EmailFlags flags = new Geary.EmailFlags();
- flags.add(Geary.EmailFlags.FLAGGED);
-
- if (conversation.is_flagged())
- mark_conversations(to_mark, null, flags, false);
- else
- mark_conversations(to_mark, flags, null, true);
-
+ mark_conversations(to_mark, Geary.EmailFlags.FLAGGED);
return true;
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]