[geary/wip/135-contact-popovers: 20/20] Separate and implement contact popover action from conversation message



commit 99a20970a7fc43e58c481c55b4524b0a62559fcf
Author: Michael Gratton <mike vee net>
Date:   Sat Mar 16 19:56:33 2019 +1100

    Separate and implement contact popover action from conversation message
    
    This leads to a minor bit of UI string and code duplication, but
    substantially reduces the complexity of implementing the popover's
    actions. Hook up all remaining actions except open and save.

 .../conversation-contact-popover.vala              | 146 ++++++++++++++++-----
 .../conversation-viewer/conversation-message.vala  |  75 ++---------
 ui/conversation-contact-popover.ui                 | 105 ++++++++++++++-
 ui/conversation-message-menus.ui                   |  24 ----
 4 files changed, 228 insertions(+), 122 deletions(-)
---
diff --git a/src/client/conversation-viewer/conversation-contact-popover.vala 
b/src/client/conversation-viewer/conversation-contact-popover.vala
index 640bb015..75386fa3 100644
--- a/src/client/conversation-viewer/conversation-contact-popover.vala
+++ b/src/client/conversation-viewer/conversation-contact-popover.vala
@@ -12,15 +12,35 @@
 public class Conversation.ContactPopover : Gtk.Popover {
 
 
+    private const string ACTION_COPY_EMAIL= "copy-email";
+    private const string ACTION_LOAD_REMOTE = "load-remote";
+    private const string ACTION_NEW_CONVERSATION = "new-conversation";
+    private const string ACTION_OPEN = "open";
+    private const string ACTION_SAVE = "save";
+    private const string ACTION_SHOW_CONVERSATIONS = "show-conversations";
+    private const string ACTION_STAR = "star";
+    private const string ACTION_UNSTAR = "unstar";
+
+    private const string ACTION_GROUP = "con";
+
+    private const GLib.ActionEntry[] ACTION_ENTRIES = {
+        {ACTION_COPY_EMAIL,         on_copy_email,                },
+        {ACTION_LOAD_REMOTE,        on_load_remote, null, "false" },
+        {ACTION_NEW_CONVERSATION,   on_new_conversation           },
+        {ACTION_OPEN,               on_open                       },
+        {ACTION_SAVE,               on_save                       },
+        {ACTION_SHOW_CONVERSATIONS, on_show_conversations         },
+        {ACTION_STAR,               on_star,                      },
+        {ACTION_UNSTAR,             on_unstar,                    },
+    };
+
+
     public Application.Contact contact { get; private set; }
 
     public Geary.RFC822.MailboxAddress mailbox { get; private set; }
 
     private GLib.Cancellable load_cancellable = new GLib.Cancellable();
 
-    [GtkChild]
-    private Gtk.Grid container;
-
     [GtkChild]
     private Gtk.Image avatar;
 
@@ -36,45 +56,35 @@ public class Conversation.ContactPopover : Gtk.Popover {
     [GtkChild]
     private Gtk.Button unstarred_button;
 
+    [GtkChild]
+    private Gtk.ModelButton open_button;
+
+    [GtkChild]
+    private Gtk.ModelButton save_button;
+
+    [GtkChild]
+    private Gtk.ModelButton load_remote_button;
+
+    private GLib.SimpleActionGroup actions = new GLib.SimpleActionGroup();
+
 
     public ContactPopover(Gtk.Widget relative_to,
                           Application.Contact contact,
                           Geary.RFC822.MailboxAddress mailbox) {
+
         this.relative_to = relative_to;
         this.contact = contact;
         this.mailbox = mailbox;
 
+        this.load_remote_button.role = CHECK;
+
+        this.actions.add_action_entries(ACTION_ENTRIES, this);
+        insert_action_group(ACTION_GROUP, this.actions);
+
         contact.changed.connect(this.on_contact_changed);
         update();
     }
 
-    public void add_section(GLib.MenuModel section,
-                            Gee.Map<string,GLib.Variant> values) {
-        Gtk.Separator separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
-        separator.show();
-        this.container.add(separator);
-        for (int i = 0; i < section.get_n_items(); i++) {
-            GLib.MenuItem item = new MenuItem.from_model(section, i);
-            string action_fq = (string) item.get_attribute_value(
-                Menu.ATTRIBUTE_ACTION, VariantType.STRING
-            );
-
-            string action_name = action_fq.substring(action_fq.index_of(".") + 1);
-
-            Gtk.ModelButton button = new Gtk.ModelButton();
-            button.text = (string) item.get_attribute_value(
-                Menu.ATTRIBUTE_LABEL, VariantType.STRING
-            );
-            button.action_name = (string) item.get_attribute_value(
-                Menu.ATTRIBUTE_ACTION, VariantType.STRING
-            );
-            button.action_target = values[action_name];
-            button.show();
-
-            this.container.add(button);
-        }
-    }
-
     /**
      * Starts loading the avatar for the message's sender.
      */
@@ -121,20 +131,94 @@ public class Conversation.ContactPopover : Gtk.Popover {
             this.contact_address.hide();
         }
 
+        bool is_desktop = this.contact.is_desktop_contact;
+
         bool starred = false;
         bool unstarred = false;
-        if (this.contact.is_desktop_contact) {
+        if (is_desktop) {
             starred = this.contact.is_favourite;
             unstarred = !this.contact.is_favourite;
         }
         this.starred_button.set_visible(starred);
         this.unstarred_button.set_visible(unstarred);
+
+
+        this.open_button.set_visible(is_desktop);
+        this.save_button.set_visible(!is_desktop);
+        this.load_remote_button.set_visible(!is_desktop);
+
+        GLib.SimpleAction load_remote = (GLib.SimpleAction)
+            actions.lookup_action(ACTION_LOAD_REMOTE);
+        load_remote.set_state(
+            new GLib.Variant.boolean(
+                is_desktop || this.contact.load_remote_resources
+            )
+        );
+    }
+
+    private async void set_load_remote_resources(bool enabled) {
+        try {
+            yield this.contact.set_remote_resource_loading(enabled, null);
+        } catch (GLib.Error err) {
+            debug("Failed to set load remote resources for contact %s:, %s",
+                  this.contact.to_string(), err.message);
+        }
+    }
+
+    private async void set_favourite(bool enabled) {
+        try {
+            yield this.contact.set_favourite(enabled, null);
+        } catch (GLib.Error err) {
+            debug("Failed to set enabled state for contact %s:, %s",
+                  this.contact.to_string(), err.message);
+        }
     }
 
     private void on_contact_changed() {
         update();
     }
 
+    private void on_copy_email() {
+        Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
+        clipboard.set_text(this.mailbox.to_full_display(), -1);
+        clipboard.store();
+    }
+
+    private void on_load_remote(GLib.SimpleAction action) {
+        bool state = !action.get_state().get_boolean();
+        this.set_load_remote_resources.begin(state);
+    }
+
+    private void on_new_conversation() {
+        MainWindow? main = this.get_toplevel() as MainWindow;
+        if (main != null) {
+            main.open_composer_for_mailbox(this.mailbox);
+        }
+    }
+
+    private void on_open() {
+
+    }
+
+    private void on_save() {
+
+    }
+
+    private void on_show_conversations() {
+        MainWindow? main = this.get_toplevel() as MainWindow;
+        if (main != null) {
+            main.show_search_bar("from:%s".printf(this.mailbox.address));
+        }
+    }
+
+    private void on_star() {
+        this.set_favourite.begin(true);
+    }
+
+    private void on_unstar() {
+        this.set_favourite.begin(false);
+    }
+
     [GtkCallback]
     private void after_closed() {
         GLib.Idle.add(() => {
diff --git a/src/client/conversation-viewer/conversation-message.vala 
b/src/client/conversation-viewer/conversation-message.vala
index 5336e435..92cc38a1 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -32,11 +32,6 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
 
     private const int MAX_INLINE_IMAGE_MAJOR_DIM = 1024;
 
-    private const string ACTION_CONTACT_LOAD_IMAGES = "contact-load-images";
-    private const string ACTION_CONTACT_OPEN = "contact-open";
-    private const string ACTION_CONTACT_SAVE = "contact-save";
-    private const string ACTION_CONTACT_SHOW_CONVERSATIONS =
-        "contact-show-conversations";
     private const string ACTION_CONVERSATION_NEW = "conversation-new";
     private const string ACTION_COPY_EMAIL = "copy-email";
     private const string ACTION_COPY_LINK = "copy-link";
@@ -203,9 +198,6 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
     }
 
 
-    private static GLib.VariantType MAILBOX_TYPE = new GLib.VariantType("(ss)");
-
-
     /** Contact for the primary originator, if any. */
     internal Application.Contact? primary_contact {
         get; private set;
@@ -302,8 +294,6 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
     private MenuModel context_menu_email;
     private MenuModel context_menu_image;
     private MenuModel context_menu_main;
-    private MenuModel context_menu_known_contact;
-    private MenuModel context_menu_unknown_contact;
     private MenuModel? context_menu_inspector = null;
 
     // Address fields that can be search through
@@ -402,18 +392,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
 
         // Actions
 
-        add_action(ACTION_CONTACT_LOAD_IMAGES, true, VariantType.BOOLEAN)
-            // XXX
-            ;
-        add_action(ACTION_CONTACT_OPEN, true, VariantType.STRING)
-            // XXX
-            ;
-        add_action(ACTION_CONTACT_SAVE, true, ConversationMessage.MAILBOX_TYPE)
-            // XXX
-            ;
-        add_action(ACTION_CONTACT_SHOW_CONVERSATIONS, true, VariantType.STRING)
-            .activate.connect(on_contact_show_conversations);
-        add_action(ACTION_CONVERSATION_NEW, true, ConversationMessage.MAILBOX_TYPE)
+        add_action(ACTION_CONVERSATION_NEW, true, VariantType.STRING)
             .activate.connect(on_new_conversation);
         add_action(ACTION_COPY_EMAIL, true, VariantType.STRING)
             .activate.connect(on_copy_email_address);
@@ -443,12 +422,6 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
         context_menu_email = (MenuModel) builder.get_object("context_menu_email");
         context_menu_image = (MenuModel) builder.get_object("context_menu_image");
         context_menu_main = (MenuModel) builder.get_object("context_menu_main");
-        this.context_menu_known_contact = (MenuModel) builder.get_object(
-            "context_menu_known_contact"
-        );
-        this.context_menu_unknown_contact = (MenuModel) builder.get_object(
-            "context_menu_unknown_contact"
-        );
         if (Args.inspector) {
             context_menu_inspector =
                 (MenuModel) builder.get_object("context_menu_inspector");
@@ -1067,17 +1040,6 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
 
             Gee.Map<string,GLib.Variant> values =
                 new Gee.HashMap<string,GLib.Variant>();
-
-            GLib.Variant mailbox_var = new GLib.Variant.tuple(
-                new GLib.Variant[] {
-                    address.name ?? "",
-                    address.address
-                });
-            values[ACTION_CONTACT_OPEN] = "not yet defined";
-            values[ACTION_CONTACT_SAVE] = mailbox_var;
-            values[ACTION_CONTACT_SHOW_CONVERSATIONS] = address.address;
-            values[ACTION_CONTACT_LOAD_IMAGES] = false;
-            values[ACTION_CONVERSATION_NEW] = mailbox_var;
             values[ACTION_COPY_EMAIL] = address.to_full_display();
 
             Conversation.ContactPopover popover = new Conversation.ContactPopover(
@@ -1086,13 +1048,6 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
                 address
             );
             popover.load_avatar.begin();
-            popover.add_section(this.context_menu_email, values);
-            popover.add_section(
-                address_child.contact.is_desktop_contact
-                    ? this.context_menu_known_contact
-                    : this.context_menu_unknown_contact,
-                values
-            );
             popover.set_position(Gtk.PositionType.BOTTOM);
             popover.closed.connect(() => {
                     address_child.unset_state_flags(Gtk.StateFlags.ACTIVE);
@@ -1232,34 +1187,28 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
         remote_images_infobar.hide();
     }
 
-    private void on_contact_show_conversations(Variant? param) {
-        string email = param as string;
-        MainWindow? main = this.get_toplevel() as MainWindow;
-        if (main != null && email != null) {
-            main.show_search_bar("from:%s".printf(email));
-        }
+    private void on_copy_link(Variant? param) {
+        Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
+        clipboard.set_text(param.get_string(), -1);
+        clipboard.store();
     }
 
     private void on_new_conversation(Variant? param) {
+        string value = param.get_string();
+        if (value.has_prefix(Geary.ComposedEmail.MAILTO_SCHEME)) {
+            value = value.substring(Geary.ComposedEmail.MAILTO_SCHEME.length, -1);
+        }
+
         MainWindow? main = this.get_toplevel() as MainWindow;
         if (main != null &&
-            param.get_type().equal(ConversationMessage.MAILBOX_TYPE)) {
-            string? name = (string) param.get_child_value(0);
+            Geary.RFC822.MailboxAddress.is_valid_address(value)) {
             Geary.RFC822.MailboxAddress mailbox = new Geary.RFC822.MailboxAddress(
-                Geary.String.is_empty_or_whitespace(name) ? null : name,
-                (string) param.get_child_value(1)
+                null, value
             );
-
             main.open_composer_for_mailbox(mailbox);
         }
     }
 
-    private void on_copy_link(Variant? param) {
-        Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
-        clipboard.set_text(param.get_string(), -1);
-        clipboard.store();
-    }
-
     private void on_copy_email_address(Variant? param) {
         string value = param.get_string();
         if (value.has_prefix(Geary.ComposedEmail.MAILTO_SCHEME)) {
diff --git a/ui/conversation-contact-popover.ui b/ui/conversation-contact-popover.ui
index b533f3d0..ca98a8fc 100644
--- a/ui/conversation-contact-popover.ui
+++ b/ui/conversation-contact-popover.ui
@@ -6,7 +6,7 @@
     <property name="can_focus">False</property>
     <signal name="closed" handler="after_closed" after="yes" swapped="no"/>
     <child>
-      <object class="GtkGrid" id="container">
+      <object class="GtkGrid">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="margin_left">10</property>
@@ -14,7 +14,6 @@
         <property name="margin_top">10</property>
         <property name="margin_bottom">10</property>
         <property name="orientation">vertical</property>
-        <property name="row_spacing">2</property>
         <child>
           <object class="GtkGrid">
             <property name="visible">True</property>
@@ -27,7 +26,7 @@
                 <property name="receives_default">True</property>
                 <property name="halign">end</property>
                 <property name="valign">center</property>
-                <property name="action_name">msg.contact-star</property>
+                <property name="action_name">con.star</property>
                 <property name="relief">none</property>
                 <child>
                   <object class="GtkImage">
@@ -48,7 +47,7 @@
                 <property name="receives_default">True</property>
                 <property name="halign">end</property>
                 <property name="valign">center</property>
-                <property name="action_name">msg.contact-star</property>
+                <property name="action_name">con.unstar</property>
                 <property name="relief">none</property>
                 <child>
                   <object class="GtkImage">
@@ -123,6 +122,104 @@
             <property name="top_attach">0</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkSeparator">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">con.new-conversation</property>
+            <property name="text" translatable="yes">_New Conversation…</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">con.copy-email</property>
+            <property name="text" translatable="yes">Copy Email _Address</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">3</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkSeparator">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">4</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton" id="save_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">con.save</property>
+            <property name="text" translatable="yes">Save in Contacts…</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">7</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">con.show-conversations</property>
+            <property name="text" translatable="yes">Show Conversations</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">5</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton" id="open_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">con.open</property>
+            <property name="text" translatable="yes">Open in Contacts</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">6</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkModelButton" id="load_remote_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">con.load-remote</property>
+            <property name="text" translatable="yes">Always Load Remote Images</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">8</property>
+          </packing>
+        </child>
       </object>
     </child>
     <style>
diff --git a/ui/conversation-message-menus.ui b/ui/conversation-message-menus.ui
index 27d70c6d..32d0be1c 100644
--- a/ui/conversation-message-menus.ui
+++ b/ui/conversation-message-menus.ui
@@ -38,30 +38,6 @@
         <attribute name="action">msg.copy-selection</attribute>
       </item>
     </section>
-    <section id="context_menu_known_contact">
-      <item>
-        <attribute name="label" translatable="yes">Sho_w Conversations</attribute>
-        <attribute name="action">msg.contact-show-conversations</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">_Open in Contacts…</attribute>
-        <attribute name="action">msg.contact-save</attribute>
-      </item>
-    </section>
-    <section id="context_menu_unknown_contact">
-      <item>
-        <attribute name="label" translatable="yes">Sho_w Conversations</attribute>
-        <attribute name="action">msg.contact-show-conversations</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">Save in Con_tacts…</attribute>
-        <attribute name="action">msg.contact-save</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">Always Load Remote Images</attribute>
-        <attribute name="action">msg.contact-load-images</attribute>
-      </item>
-    </section>
     <section id="context_menu_inspector">
       <item>
         <attribute name="label" translatable="yes">_Inspect…</attribute>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]