[geary/mjog/account-command-stacks: 11/27] Implement moving conversations as undoable commands
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/mjog/account-command-stacks: 11/27] Implement moving conversations as undoable commands
- Date: Sat, 26 Oct 2019 05:35:09 +0000 (UTC)
commit 667710b50b76b131ba7ae715bc16bfeec77e5914
Author: Michael Gratton <mike vee net>
Date: Sun Oct 6 10:30:19 2019 +1100
Implement moving conversations as undoable commands
src/client/application/application-controller.vala | 223 +++++++++++++++++++++
src/client/components/main-window.vala | 85 ++++++++
2 files changed, 308 insertions(+)
---
diff --git a/src/client/application/application-controller.vala
b/src/client/application/application-controller.vala
index c3726e56..a3f8c5c1 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -650,6 +650,113 @@ public class Application.Controller : Geary.BaseObject {
}
}
+ public async void move_conversations(Geary.FolderSupport.Move source,
+ Geary.Folder destination,
+ Gee.Collection<Geary.App.Conversation> conversations)
+ throws GLib.Error {
+ AccountContext? context = this.accounts.get(source.account.information);
+ if (context != null) {
+ yield this.commands.execute(
+ new MoveEmailCommand(
+ source,
+ destination,
+ to_in_folder_email_ids(conversations),
+ /// Translators: Label for in-app undo
+ /// notification. String substitution is the name
+ /// of the destination folder.
+ ngettext(
+ "Conversation moved to %s",
+ "Conversations moved to %s",
+ conversations.size
+ ).printf(destination.get_display_name()),
+ /// Translators: Label for in-app undo
+ /// notification. String substitution is the name
+ /// of the source folder.
+ ngettext(
+ "Conversation restored to %s",
+ "Conversations restored to %s",
+ conversations.size
+ ).printf(source.get_display_name())
+ ),
+ context.cancellable
+ );
+ }
+ }
+
+ public async void move_conversations_special(Geary.FolderSupport.Move source,
+ Geary.SpecialFolderType destination,
+ Gee.Collection<Geary.App.Conversation> conversations)
+ throws GLib.Error {
+ AccountContext? context = this.accounts.get(source.account.information);
+ if (context != null) {
+ Geary.Folder? dest = source.account.get_special_folder(destination);
+ if (dest == null) {
+ throw new Geary.EngineError.NOT_FOUND(
+ "No folder found for: %s", destination.to_string()
+ );
+ }
+
+ yield this.commands.execute(
+ new MoveEmailCommand(
+ source,
+ dest,
+ to_in_folder_email_ids(conversations),
+ /// Translators: Label for in-app undo
+ /// notification. String substitution is the name
+ /// of the destination folder.
+ ngettext(
+ "Conversation moved to %s",
+ "Conversations moved to %s",
+ conversations.size
+ ).printf(dest.get_display_name()),
+ /// Translators: Label for in-app undo
+ /// notification. String substitution is the name
+ /// of the source folder.
+ ngettext(
+ "Conversation restored to %s",
+ "Conversations restored to %s",
+ conversations.size
+ ).printf(source.get_display_name())
+ ),
+ context.cancellable
+ );
+ }
+ }
+
+ public async void move_messages_special(Geary.FolderSupport.Move source,
+ Geary.SpecialFolderType destination,
+ Gee.Collection<Geary.EmailIdentifier> messages)
+ throws GLib.Error {
+ AccountContext? context = this.accounts.get(source.account.information);
+ if (context != null) {
+ Geary.Folder? dest = source.account.get_special_folder(destination);
+ if (dest == null) {
+ throw new Geary.EngineError.NOT_FOUND(
+ "No folder found for: %s", destination.to_string()
+ );
+ }
+
+ yield this.commands.execute(
+ new MoveEmailCommand(
+ source,
+ dest,
+ messages,
+ /// Translators: Label for in-app undo
+ /// notification. String substitution is the name
+ /// of the destination folder.
+ ngettext(
+ "Message moved to %s",
+ "Messages moved to %s",
+ messages.size
+ ).printf(destination.get_display_name()),
+ /// Translators: Label for in-app undo
+ /// notification. String substitution is the name
+ /// of the source folder.
+ ngettext(
+ "Message restored to %s",
+ "Messages restored to %s",
+ messages.size
+ ).printf(source.get_display_name())
),
context.cancellable
);
@@ -1912,6 +2019,19 @@ public class Application.Controller : Geary.BaseObject {
return false;
}
+ private Gee.Collection<Geary.EmailIdentifier>
+ to_in_folder_email_ids(Gee.Collection<Geary.App.Conversation> conversations) {
+ Gee.Collection<Geary.EmailIdentifier> messages =
+ new Gee.LinkedList<Geary.EmailIdentifier>();
+ foreach (Geary.App.Conversation conversation in conversations) {
+ foreach (Geary.Email email in
+ conversation.get_emails(RECV_DATE_ASCENDING, IN_FOLDER)) {
+ messages.add(email.id);
+ }
+ }
+ return messages;
+ }
+
private void on_account_available(Geary.AccountInformation info) {
Geary.Account? account = null;
try {
@@ -2127,3 +2247,106 @@ private class Application.MarkEmailCommand : Command {
}
+private abstract class Application.RevokableCommand : Command {
+
+
+ public override bool can_undo {
+ get { return this.revokable != null && this.revokable.valid; }
+ }
+
+ private Geary.Revokable? revokable = null;
+
+
+ public override async void execute(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ set_revokable(yield execute_impl(cancellable));
+ if (this.revokable != null && this.revokable.valid) {
+ yield this.revokable.commit_async(cancellable);
+ }
+ }
+
+ public override async void undo(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ if (this.revokable == null) {
+ throw new Geary.EngineError.UNSUPPORTED(
+ "Cannot undo command, no revokable available"
+ );
+ }
+
+ yield this.revokable.revoke_async(cancellable);
+ set_revokable(null);
+ }
+
+ protected abstract async Geary.Revokable
+ execute_impl(GLib.Cancellable cancellable)
+ throws GLib.Error;
+
+ private void set_revokable(Geary.Revokable? updated) {
+ if (this.revokable != null) {
+ this.revokable.committed.disconnect(on_revokable_committed);
+ }
+
+ this.revokable = updated;
+
+ if (this.revokable != null) {
+ this.revokable.committed.connect(on_revokable_committed);
+ }
+ }
+
+ private void on_revokable_committed(Geary.Revokable? updated) {
+ set_revokable(updated);
+ }
+
+}
+
+
+private class Application.MoveEmailCommand : RevokableCommand {
+
+
+ private Geary.FolderSupport.Move source;
+ private Gee.Collection<Geary.EmailIdentifier> source_messages;
+
+ private Geary.Folder destination;
+
+
+ public MoveEmailCommand(Geary.FolderSupport.Move source,
+ Geary.Folder destination,
+ Gee.Collection<Geary.EmailIdentifier> messages,
+ string? executed_label = null,
+ string? undone_label = null) {
+ this.source = source;
+ this.source_messages = messages;
+ this.destination = destination;
+
+ this.executed_label = executed_label;
+ this.undone_label = undone_label;
+ }
+
+ protected override async Geary.Revokable
+ execute_impl(GLib.Cancellable cancellable)
+ throws GLib.Error {
+ bool open = false;
+ try {
+ yield this.source.open_async(
+ Geary.Folder.OpenFlags.NO_DELAY, cancellable
+ );
+ open = true;
+ return yield this.source.move_email_async(
+ this.source_messages,
+ this.destination.path,
+ cancellable
+ );
+ } finally {
+ if (open) {
+ try {
+ yield this.source.close_async(null);
+ } catch (GLib.Error err) {
+ // ignored
+ }
+ }
+ }
+ }
+
+}
+
+
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index cf30b1b5..eb441611 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -1499,18 +1499,87 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
}
private void on_mark_as_spam_toggle() {
+ Geary.FolderSupport.Move? source =
+ this.current_folder as Geary.FolderSupport.Move;
+ if (source != null) {
+ Geary.SpecialFolderType destination =
+ (source.special_folder_type != SPAM)
+ ? Geary.SpecialFolderType.SPAM
+ : Geary.SpecialFolderType.INBOX;
+ this.application.controller.move_conversations_special.begin(
+ source,
+ destination,
+ this.conversation_list_view.get_selected_conversations(),
+ (obj, res) => {
+ try {
+ this.application.controller.move_conversations_special.end(res);
+ } catch (GLib.Error err) {
+ handle_error(source.account.information, err);
+ }
+ }
+ );
+ }
}
private void on_move_conversation(Geary.Folder destination) {
+ Geary.FolderSupport.Move source =
+ this.current_folder as Geary.FolderSupport.Move;
+ if (source != null) {
+ this.application.controller.move_conversations.begin(
+ source,
+ destination,
+ this.conversation_list_view.get_selected_conversations(),
+ (obj, res) => {
+ try {
+ this.application.controller.move_conversations.end(res);
+ } catch (GLib.Error err) {
+ handle_error(source.account.information, err);
+ }
+ }
+ );
+
+ }
}
private void on_copy_conversation(Geary.Folder destination) {
}
private void on_archive_conversation() {
+ Geary.FolderSupport.Move source =
+ this.current_folder as Geary.FolderSupport.Move;
+ if (source != null) {
+ this.application.controller.move_conversations_special.begin(
+ source,
+ Geary.SpecialFolderType.ARCHIVE,
+ this.conversation_list_view.get_selected_conversations(),
+ (obj, res) => {
+ try {
+ this.application.controller.move_conversations_special.end(res);
+ } catch (GLib.Error err) {
+ handle_error(source.account.information, err);
+ }
+ }
+ );
+ }
}
private void on_trash_conversation() {
+ Geary.FolderSupport.Move source =
+ this.current_folder as Geary.FolderSupport.Move;
+ if (source != null) {
+ this.application.controller.move_conversations_special.begin(
+ source,
+ Geary.SpecialFolderType.TRASH,
+ this.conversation_list_view.get_selected_conversations(),
+ (obj, res) => {
+ try {
+ this.application.controller.move_conversations_special.end(res);
+ } catch (GLib.Error err) {
+ handle_error(source.account.information, err);
+ }
+ }
+ );
+ }
}
private void on_delete_conversation() {
@@ -1662,6 +1731,22 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
}
private void on_trash_message(ConversationEmail target_view) {
+ Geary.FolderSupport.Move? source =
+ this.current_folder as Geary.FolderSupport.Move;
+ if (source != null) {
+ this.application.controller.move_messages_special.begin(
+ source,
+ TRASH,
+ Geary.Collection.single(target_view.email.id),
+ (obj, res) => {
+ try {
+ this.application.controller.move_messages_special.end(res);
+ } catch (GLib.Error err) {
+ handle_error(source.account.information, err);
+ }
+ }
+ );
+ }
}
private void on_delete_message(ConversationEmail target_view) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]