[geary/wip/135-contact-popovers: 44/56] Update converation message details when a client contact changes



commit 6da9c38980386873bca7da96d25e8f90f4893b11
Author: Michael Gratton <mike vee net>
Date:   Sat Mar 16 17:43:51 2019 +1100

    Update converation message details when a client contact changes
    
    Add Application.Contact.changed signal, hook up to that in
    ContactFlowBoxChild and ContactPopover.

 .../application/application-contact-store.vala     |   2 +-
 src/client/application/application-contact.vala    |  77 ++++++++++++++--
 .../conversation-contact-popover.vala              |  30 ++++--
 .../conversation-viewer/conversation-message.vala  | 101 ++++++++++++++-------
 4 files changed, 157 insertions(+), 53 deletions(-)
---
diff --git a/src/client/application/application-contact-store.vala 
b/src/client/application/application-contact-store.vala
index 44753ea3..59ae9108 100644
--- a/src/client/application/application-contact-store.vala
+++ b/src/client/application/application-contact-store.vala
@@ -17,7 +17,7 @@ public class Application.ContactStore : Geary.BaseObject {
     /** The account this store aggregates data for. */
     public Geary.Account account { get; private set; }
 
-    private Folks.IndividualAggregator individuals;
+    internal Folks.IndividualAggregator individuals;
 
 
     /** Constructs a new contact store for an account. */
diff --git a/src/client/application/application-contact.vala b/src/client/application/application-contact.vala
index 516a8234..5ff5d3fa 100644
--- a/src/client/application/application-contact.vala
+++ b/src/client/application/application-contact.vala
@@ -41,6 +41,11 @@ public class Application.Contact : Geary.BaseObject {
         }
     }
 
+
+    /** Fired when the contact has changed in some way. */
+    public signal void changed();
+
+
     private weak ContactStore store;
     private Folks.Individual? individual;
     private Geary.Contact? contact;
@@ -51,15 +56,11 @@ public class Application.Contact : Geary.BaseObject {
                      Geary.Contact? contact,
                      Geary.RFC822.MailboxAddress source) {
         this.store = store;
-        this.individual = individual;
         this.contact = contact;
+        update_individual(individual);
 
-        if (individual != null) {
-            this.display_name = individual.display_name;
-            this.is_desktop_contact = true;
-        } else if (contact != null) {
-            this.display_name = contact.real_name;
-        } else {
+        update();
+        if (Geary.String.is_empty_or_whitespace(this.display_name)) {
             this.display_name = source.name;
         }
 
@@ -74,6 +75,11 @@ public class Application.Contact : Geary.BaseObject {
         }
     }
 
+    ~Contact() {
+        // Disconnect from signals if any
+        update_individual(null);
+    }
+
     /** Sets remote resource loading for this contact. */
     public async void set_remote_resource_loading(bool enabled,
                                                   GLib.Cancellable? cancellable)
@@ -90,6 +96,8 @@ public class Application.Contact : Geary.BaseObject {
                 // XXX cancellable
             );
         }
+
+        changed();
     }
 
     /** Returns a string representation for debugging */
@@ -97,4 +105,59 @@ public class Application.Contact : Geary.BaseObject {
         return "Contact(\"%s\")".printf(this.display_name);
     }
 
+    private void update_individual(Folks.Individual? replacement) {
+        if (this.individual != null) {
+            this.individual.notify.disconnect(this.on_individual_notify);
+            this.individual.removed.disconnect(this.on_individual_removed);
+        }
+
+        this.individual = replacement;
+
+        if (this.individual != null) {
+            this.individual.notify.connect(this.on_individual_notify);
+            this.individual.removed.connect(this.on_individual_removed);
+        }
+    }
+
+    private void update() {
+        if (this.individual != null) {
+            this.display_name = this.individual.display_name;
+            this.is_desktop_contact = true;
+        } else {
+            if (this.contact != null) {
+                this.display_name = this.contact.real_name;
+            }
+            this.is_desktop_contact = false;
+        }
+    }
+
+    private async void update_replacement(Folks.Individual? replacement) {
+        if (replacement == null) {
+            ContactStore? store = this.store;
+            if (store != null) {
+                try {
+                    replacement = yield store.individuals.look_up_individual(
+                        this.individual.id
+                    );
+                } catch (GLib.Error err) {
+                    debug("Error loading replacement for Folks %s: %s",
+                          this.individual.id, err.message);
+                }
+            }
+        }
+
+        update_individual(replacement);
+        update();
+        changed();
+    }
+
+    private void on_individual_notify() {
+        update();
+        changed();
+    }
+
+    private void on_individual_removed(Folks.Individual? replacement) {
+        this.update_replacement.begin(replacement);
+    }
+
 }
diff --git a/src/client/conversation-viewer/conversation-contact-popover.vala 
b/src/client/conversation-viewer/conversation-contact-popover.vala
index 24f87002..033c59e1 100644
--- a/src/client/conversation-viewer/conversation-contact-popover.vala
+++ b/src/client/conversation-viewer/conversation-contact-popover.vala
@@ -38,16 +38,8 @@ public class Conversation.ContactPopover : Gtk.Popover {
         this.contact = contact;
         this.mailbox = mailbox;
 
-        string? display_name = contact.display_name;
-        this.contact_name.set_text(display_name);
-
-        if (!contact.display_name_is_email) {
-            this.contact_address.set_text(mailbox.address);
-        } else {
-            this.contact_name.vexpand = true;
-            this.contact_name.valign = FILL;
-            this.contact_address.hide();
-        }
+        contact.changed.connect(this.on_contact_changed);
+        update();
     }
 
     public void add_section(GLib.MenuModel section,
@@ -106,10 +98,28 @@ public class Conversation.ContactPopover : Gtk.Popover {
     }
 
     public override void destroy() {
+        this.contact.changed.disconnect(this.on_contact_changed);
         this.load_cancellable.cancel();
         base.destroy();
     }
 
+    private void update() {
+        string display_name = this.contact.display_name;
+        this.contact_name.set_text(display_name);
+
+        if (!this.contact.display_name_is_email) {
+            this.contact_address.set_text(this.mailbox.address);
+        } else {
+            this.contact_name.vexpand = true;
+            this.contact_name.valign = FILL;
+            this.contact_address.hide();
+        }
+    }
+
+    private void on_contact_changed() {
+        update();
+    }
+
     [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 e3fbde3a..ccc90448 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -46,14 +46,20 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
     private const string ACTION_SAVE_IMAGE = "save-image";
     private const string ACTION_SELECT_ALL = "select-all";
 
+
     // Widget used to display sender/recipient email addresses in
     // message header Gtk.FlowBox instances.
     private class ContactFlowBoxChild : Gtk.FlowBoxChild {
 
+
         private const string PRIMARY_CLASS = "geary-primary";
 
+
         public enum Type { FROM, OTHER; }
 
+
+        public Type address_type { get; private set; }
+
         public Application.Contact contact { get; private set; }
 
         public Geary.RFC822.MailboxAddress displayed { get; private set; }
@@ -61,13 +67,55 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
 
         private string search_value;
 
+        private Gtk.Bin container;
+
+
         public ContactFlowBoxChild(Application.Contact contact,
                                    Geary.RFC822.MailboxAddress source,
-                                   Type type = Type.OTHER) {
+                                   Type address_type = Type.OTHER) {
             this.contact = contact;
             this.source = source;
+            this.address_type = address_type;
             this.search_value = source.to_searchable_string().casefold();
 
+            // Update prelight state when mouse-overed.
+            Gtk.EventBox events = new Gtk.EventBox();
+            events.add_events(
+                Gdk.EventMask.ENTER_NOTIFY_MASK |
+                Gdk.EventMask.LEAVE_NOTIFY_MASK
+            );
+            events.set_visible_window(false);
+            events.enter_notify_event.connect(on_prelight_in_event);
+            events.leave_notify_event.connect(on_prelight_out_event);
+
+            add(events);
+            this.container = events;
+            set_halign(Gtk.Align.START);
+
+            this.contact.changed.connect(on_contact_changed);
+            update();
+        }
+
+        public override void destroy() {
+            this.contact.changed.disconnect(on_contact_changed);
+            base.destroy();
+        }
+
+        public bool highlight_search_term(string term) {
+            bool found = term in this.search_value;
+            if (found) {
+                get_style_context().add_class(MATCH_CLASS);
+            } else {
+                get_style_context().remove_class(MATCH_CLASS);
+            }
+            return found;
+        }
+
+        public void unmark_search_terms() {
+            get_style_context().remove_class(MATCH_CLASS);
+        }
+
+        private void update() {
             // We use two GTK.Label instances here when address has
             // distinct parts so we can dim the secondary part, if
             // any. Ideally, it would be just one label instance in
@@ -76,7 +124,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
 
             Gtk.Grid address_parts = new Gtk.Grid();
 
-            bool is_spoofed = source.is_spoofed();
+            bool is_spoofed = this.source.is_spoofed();
             if (is_spoofed) {
                 Gtk.Image spoof_img = new Gtk.Image.from_icon_name(
                     "dialog-warning-symbolic", Gtk.IconSize.SMALL_TOOLBAR
@@ -91,34 +139,34 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
             primary.ellipsize = Pango.EllipsizeMode.END;
             primary.set_halign(Gtk.Align.START);
             primary.get_style_context().add_class(PRIMARY_CLASS);
-            if (type == Type.FROM) {
+            if (this.address_type == Type.FROM) {
                 primary.get_style_context().add_class(FROM_CLASS);
             }
             address_parts.add(primary);
 
-            string display_address = source.to_address_display("", "");
+            string display_address = this.source.to_address_display("", "");
 
-            if (is_spoofed || contact.display_name_is_email) {
+            if (is_spoofed || this.contact.display_name_is_email) {
                 // Don't display the name to avoid duplication and/or
                 // reduce the chance of the user of being tricked by
                 // malware.
                 primary.set_text(display_address);
                 this.displayed = new Geary.RFC822.MailboxAddress(
-                    null, source.address
+                    null, this.source.address
                 );
-            } else if (contact.is_desktop_contact) {
+            } else if (this.contact.is_desktop_contact) {
                 // The contact's name can be trusted, so no need to
                 // display the email address
-                primary.set_text(contact.display_name);
+                primary.set_text(this.contact.display_name);
                 this.displayed = new Geary.RFC822.MailboxAddress(
-                    contact.display_name, source.address
+                    this.contact.display_name, this.source.address
                 );
             } else {
                 // Display both the display name and the email address
                 // so that the user has the full information at hand
-                primary.set_text(contact.display_name);
+                primary.set_text(this.contact.display_name);
                 this.displayed = new Geary.RFC822.MailboxAddress(
-                    contact.display_name, source.address
+                    this.contact.display_name, this.source.address
                 );
 
                 Gtk.Label secondary = new Gtk.Label(null);
@@ -129,34 +177,17 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
                 address_parts.add(secondary);
             }
 
-            // Update prelight state when mouse-overed.
-            Gtk.EventBox events = new Gtk.EventBox();
-            events.add_events(
-                Gdk.EventMask.ENTER_NOTIFY_MASK |
-                Gdk.EventMask.LEAVE_NOTIFY_MASK
-            );
-            events.set_visible_window(false);
-            events.enter_notify_event.connect(on_prelight_in_event);
-            events.leave_notify_event.connect(on_prelight_out_event);
-            events.add(address_parts);
+            Gtk.Widget? existing_ui = this.container.get_child();
+            if (existing_ui != null) {
+                this.container.remove(existing_ui);
+            }
 
-            add(events);
-            set_halign(Gtk.Align.START);
+            this.container.add(address_parts);
             show_all();
         }
 
-        public bool highlight_search_term(string term) {
-            bool found = term in this.search_value;
-            if (found) {
-                get_style_context().add_class(MATCH_CLASS);
-            } else {
-                get_style_context().remove_class(MATCH_CLASS);
-            }
-            return found;
-        }
-
-        public void unmark_search_terms() {
-            get_style_context().remove_class(MATCH_CLASS);
+        private void on_contact_changed() {
+            update();
         }
 
         private bool on_prelight_in_event(Gdk.Event event) {


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