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.
This commit is contained in:
Michael Gratton 2019-03-14 21:41:57 +11:00 committed by Michael James Gratton
parent 5ce9aa3013
commit 5339a6cbfc
5 changed files with 155 additions and 14 deletions

View file

@ -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

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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() {
@ -908,7 +912,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
@ -1314,8 +1322,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
@ -1323,10 +1332,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);
@ -1615,7 +1624,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
@ -1787,7 +1796,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
@ -2163,7 +2172,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(
@ -2734,7 +2743,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);
@ -2814,9 +2823,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,

View file

@ -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',