[geary/wip/135-contact-popovers: 8/13] Add client Contact and ContactStore classes



commit 2b424cea8521f7995d31c0c50afce347fc338560
Author: Michael Gratton <mike vee net>
Date:   Thu Mar 14 21:41:57 2019 +1100

    Add client Contact and ContactStore classes
    
    These simply wrap the Engine equivalents for the moment, but will also
    incorporate Folks data as well. Construct a per-account store and add it
    to the controller's account context object.

 po/POTFILES.in                                     |  2 +
 .../application/application-contact-store.vala     | 40 ++++++++++
 src/client/application/application-contact.vala    | 88 ++++++++++++++++++++++
 src/client/application/geary-controller.vala       | 37 +++++----
 src/client/meson.build                             |  2 +
 5 files changed, 155 insertions(+), 14 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 58fd048c..0bf3df22 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -18,6 +18,8 @@ src/client/accounts/accounts-signature-web-view.vala
 src/client/application/application-avatar-store.vala
 src/client/application/application-certificate-manager.vala
 src/client/application/application-command.vala
+src/client/application/application-contact-store.vala
+src/client/application/application-contact.vala
 src/client/application/autostart-manager.vala
 src/client/application/geary-application.vala
 src/client/application/geary-args.vala
diff --git a/src/client/application/application-contact-store.vala 
b/src/client/application/application-contact-store.vala
new file mode 100644
index 00000000..c6df7b76
--- /dev/null
+++ b/src/client/application/application-contact-store.vala
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * A source of contacts for an account.
+ *
+ * This class aggregates data from for both the Engine and Folks,
+ * allowing contacts for a specific account to be queried.
+ */
+public class Application.ContactStore {
+
+
+    /** The account this store aggregates data for. */
+    public Geary.Account account { get; private set; }
+
+
+    /** Constructs a new contact store for an account. */
+    public ContactStore(Geary.Account account) {
+        this.account = account;
+    }
+
+    /**
+     * Returns a contact for a specific mailbox.
+     *
+     * Returns a contact that has the given mailbox address listed as
+     * a primary or secondary email. A contact will always be
+     * returned, so if no matching contact already exists a new,
+     * non-persistent contact will be returned.
+     */
+    public Contact get(Geary.RFC822.MailboxAddress mailbox) {
+        Geary.Contact? contact =
+            this.account.get_contact_store().get_by_rfc822(mailbox);
+        return new Contact(this, contact, mailbox);
+    }
+
+}
diff --git a/src/client/application/application-contact.vala b/src/client/application/application-contact.vala
new file mode 100644
index 00000000..c325a062
--- /dev/null
+++ b/src/client/application/application-contact.vala
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * Contact information for an individual.
+ *
+ * This class aggregates data from for both the Engine and Folks,
+ * allowing contacts information for a specific mailbox to be
+ * queried. Contacts are obtained from the {@link ContactStore} for an
+ * account.
+ */
+public class Application.Contact {
+
+
+    /** The human-readable name of the contact. */
+    public string display_name { get; private set; }
+
+    /** Determines if {@link display_name} is trusted by the user. */
+    public bool display_name_is_trusted { get; private set; default = false; }
+
+    /** Determines if {@link display_name} the same as its email address. */
+    public bool display_name_is_email { get; private set; default = false; }
+
+    /** Determines if email from this contact should load remote resources. */
+    public bool load_remote_resources {
+        get {
+            return (
+                this.contact != null &&
+                this.contact.contact_flags.always_load_remote_images()
+            );
+        }
+    }
+
+    private weak ContactStore store;
+    private Geary.Contact? contact;
+
+
+    internal Contact(ContactStore store,
+                     Geary.Contact? contact,
+                     Geary.RFC822.MailboxAddress source) {
+        this.store = store;
+        this.contact = contact;
+
+        if (contact != null) {
+            this.display_name = contact.real_name;
+        } else {
+            this.display_name = source.name;
+        }
+
+        // Use the email address as the display name if the existing
+        // display name looks in any way sketchy, regardless of where
+        // it came from
+        if (source.is_spoofed() ||
+            Geary.String.is_empty_or_whitespace(this.display_name) ||
+            Geary.RFC822.MailboxAddress.is_valid_address(this.display_name)) {
+            this.display_name = source.address;
+            this.display_name_is_email = true;
+        }
+    }
+
+    /** Sets remote resource loading for this contact. */
+    public async void set_remote_resource_loading(bool enabled,
+                                                  GLib.Cancellable? cancellable)
+        throws GLib.Error {
+        ContactStore? store = this.store;
+        if (store != null && this.contact != null) {
+            Geary.ContactFlags flags = new Geary.ContactFlags();
+            flags.add(Geary.ContactFlags.ALWAYS_LOAD_REMOTE_IMAGES);
+
+            yield store.account.get_contact_store().mark_contacts_async(
+                Geary.Collection.single(this.contact),
+                enabled ? flags : null,
+                !enabled ? flags : null //,
+                // XXX cancellable
+            );
+        }
+    }
+
+    /** Returns a string representation for debugging */
+    public string to_string() {
+        return "Contact(\"%s\")".printf(this.display_name);
+    }
+
+}
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 8603f615..2a43765d 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -63,7 +63,8 @@ public class GearyController : Geary.BaseObject {
 
         public Geary.Account account { get; private set; }
         public Geary.Folder? inbox = null;
-        public Geary.App.EmailStore store { get; private set; }
+        public Geary.App.EmailStore emails { get; private set; }
+        public Application.ContactStore contacts { get; private set; }
 
         public bool authentication_failed = false;
         public bool authentication_prompting = false;
@@ -74,9 +75,12 @@ public class GearyController : Geary.BaseObject {
 
         public Cancellable cancellable { get; private set; default = new Cancellable(); }
 
-        public AccountContext(Geary.Account account) {
+        public AccountContext(Geary.Account account,
+                              Geary.App.EmailStore emails,
+                              Application.ContactStore contacts) {
             this.account = account;
-            this.store = new Geary.App.EmailStore(account);
+            this.emails = emails;
+            this.contacts = contacts;
         }
 
         public Geary.Account.Status get_effective_status() {
@@ -922,7 +926,11 @@ public class GearyController : Geary.BaseObject {
     }
 
     private async void connect_account_async(Geary.Account account, Cancellable? cancellable = null) {
-        AccountContext context = new AccountContext(account);
+        AccountContext context = new AccountContext(
+            account,
+            new Geary.App.EmailStore(account),
+            new Application.ContactStore(account)
+        );
 
         // XXX Need to set this early since
         // on_folders_available_unavailable expects it to be there
@@ -1329,8 +1337,9 @@ public class GearyController : Geary.BaseObject {
                 Geary.App.Conversation convo = Geary.Collection.get_first(
                     selected
                 );
-                Geary.App.EmailStore? store = get_store_for_folder(
-                    convo.base_folder
+
+                AccountContext? context = this.accounts.get(
+                    convo.base_folder.account.information
                 );
 
                 // It's possible for a conversation with zero email to
@@ -1338,10 +1347,10 @@ public class GearyController : Geary.BaseObject {
                 // last email was removed but the conversation monitor
                 // hasn't signalled its removal yet. In this case,
                 // just don't load it since it will soon disappear.
-                if (store != null && convo.get_count() > 0) {
+                if (context != null && convo.get_count() > 0) {
                     viewer.load_conversation.begin(
                         convo,
-                        store,
+                        context.emails,
                         (obj, ret) => {
                             try {
                                 viewer.load_conversation.end(ret);
@@ -1630,7 +1639,7 @@ public class GearyController : Geary.BaseObject {
     private void mark_email(Gee.Collection<Geary.EmailIdentifier> ids,
         Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove) {
         if (ids.size > 0) {
-            Geary.App.EmailStore? store = get_store_for_folder(current_folder);
+            Geary.App.EmailStore? store = get_email_store_for_folder(current_folder);
             if (store != null) {
                 store.mark_email_async.begin(
                     ids, flags_to_add, flags_to_remove, cancellable_folder
@@ -1802,7 +1811,7 @@ public class GearyController : Geary.BaseObject {
     private void copy_email(Gee.Collection<Geary.EmailIdentifier> ids,
         Geary.FolderPath destination) {
         if (ids.size > 0) {
-            Geary.App.EmailStore? store = get_store_for_folder(current_folder);
+            Geary.App.EmailStore? store = get_email_store_for_folder(current_folder);
             if (store != null) {
                 store.copy_email_async.begin(
                     ids, destination, cancellable_folder
@@ -2186,7 +2195,7 @@ public class GearyController : Geary.BaseObject {
         // Load the widget's content
         Geary.Email? full = null;
         if (referred != null) {
-            Geary.App.EmailStore? store = get_store_for_folder(current_folder);
+            Geary.App.EmailStore? store = get_email_store_for_folder(current_folder);
             if (store != null) {
                 try {
                     full = yield store.fetch_email_async(
@@ -2757,7 +2766,7 @@ public class GearyController : Geary.BaseObject {
         Gee.MultiMap<Geary.EmailIdentifier, Type>? selected_operations = null;
         try {
             if (current_folder != null) {
-                Geary.App.EmailStore? store = get_store_for_folder(current_folder);
+                Geary.App.EmailStore? store = get_email_store_for_folder(current_folder);
                 if (store != null) {
                     selected_operations = yield store
                         .get_supported_operations_async(get_selected_email_ids(false), cancellable);
@@ -2837,9 +2846,9 @@ public class GearyController : Geary.BaseObject {
         return selected_conversations.read_only_view;
     }
 
-    private inline Geary.App.EmailStore? get_store_for_folder(Geary.Folder target) {
+    private inline Geary.App.EmailStore? get_email_store_for_folder(Geary.Folder target) {
         AccountContext? context = this.accounts.get(target.account.information);
-        return context != null ? context.store : null;
+        return context != null ? context.emails : null;
     }
 
     private bool should_add_folder(Gee.Collection<Geary.Folder>? all,
diff --git a/src/client/meson.build b/src/client/meson.build
index 77db6b1a..1f3f15a7 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -3,6 +3,8 @@ geary_client_vala_sources = files(
   'application/application-avatar-store.vala',
   'application/application-certificate-manager.vala',
   'application/application-command.vala',
+  'application/application-contact-store.vala',
+  'application/application-contact.vala',
   'application/autostart-manager.vala',
   'application/geary-application.vala',
   'application/geary-args.vala',


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