Commit 02412733 authored by Julian Sparber's avatar Julian Sparber

Use individual directly

This removes most of the Contact class and makes use of the Individual
of folks directly.
The aim of this refactor is to make the code more readable.
Since Individual provides most of the needed features the Contact class
adds a complicated abstraction without any real benefit.

Transform FakePersona a real Persona which can be used as a placeholder
till folks returns the real Persona.

Use SearchQuery to find the best matching contact when opening
gnome-contacts with a e-mail address
parent b3689c66
......@@ -78,7 +78,7 @@ public class Contacts.AccountsList : ListBox {
}
var source = (persona_store as Edsf.PersonaStore).source;
var parent_source = eds_source_registry.ref_source (source.parent);
var provider_name = Contact.format_persona_store_name (persona_store);
var provider_name = ContactUtils.format_persona_store_name (persona_store);
debug ("Contact store \"%s\"", provider_name);
......
......@@ -89,14 +89,17 @@ public class Contacts.App : Gtk.Application {
return -1;
}
public void show_contact (Contact? contact) {
window.set_shown_contact (contact);
public void show_contact (Individual? individual) {
window.set_shown_contact (individual);
}
public async void show_individual (string id) {
var contact = yield contacts_store.find_contact ( (c) => {
return c.individual.id == id;
});
Individual? contact = null;
try {
contact = yield contacts_store.aggregator.look_up_individual (id);
} catch (Error e) {
debug ("Couldn't look up individual");
}
if (contact != null) {
show_contact (contact);
} else {
......@@ -172,7 +175,6 @@ public class Contacts.App : Gtk.Application {
var settings = new GLib.Settings ("org.freedesktop.folks");
settings.set_string ("primary-store",
"eds:%s".printf(e_store.id));
contacts_store.refresh ();
}
}
contacts_store.backend_store.disconnect (stores_changed_id);
......@@ -239,11 +241,10 @@ public class Contacts.App : Gtk.Application {
}
public async void show_by_email (string email_address) {
var contact = yield contacts_store.find_contact ( (c) => {
return c.has_email (email_address);
});
if (contact != null) {
show_contact (contact);
var query = new SimpleQuery(email_address, Query.MATCH_FIELDS_ADDRESSES);
Individual individual = yield contacts_store.find_contact (query);
if (individual != null) {
show_contact (individual);
} else {
var dialog = new MessageDialog (this.window, DialogFlags.DESTROY_WITH_PARENT, MessageType.ERROR, ButtonsType.CLOSE,
_("No contact with email address %s found"), email_address);
......
......@@ -34,7 +34,7 @@ public class Contacts.AvatarSelector : Popover {
// This will provide the default thumbnails
private Gnome.DesktopThumbnailFactory thumbnail_factory;
private Contact contact;
private Individual individual;
[GtkChild]
private FlowBox personas_thumbnail_grid;
......@@ -53,10 +53,10 @@ public class Contacts.AvatarSelector : Popover {
*/
public signal void set_avatar (GLib.Icon avatar_icon);
public AvatarSelector (Gtk.Widget relative, Contact? contact) {
public AvatarSelector (Gtk.Widget relative, Individual? individual) {
this.set_relative_to(relative);
this.thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL);
this.contact = contact;
this.individual = individual;
update_thumbnail_grids ();
......@@ -158,8 +158,8 @@ public class Contacts.AvatarSelector : Popover {
}
private void update_thumbnail_grids () {
if (this.contact != null) {
foreach (var p in contact.individual.personas) {
if (this.individual != null) {
foreach (var p in individual.personas) {
var button = thumbnail_for_persona (p);
if (button != null)
this.personas_thumbnail_grid.add (button);
......
......@@ -29,17 +29,17 @@ public class Contacts.Avatar : DrawingArea {
private Gdk.Pixbuf? pixbuf = null;
private Gdk.Pixbuf? cache = null;
private Contact? contact = null;
private Individual? individual = null;
// We want to lazily load the Pixbuf to make sure we don't draw all contact avatars at once.
// As long as there is no need for it to be drawn, keep this to false.
private bool avatar_loaded = false;
public Avatar (int size, Contact? contact = null) {
this.contact = contact;
if (contact != null) {
contact.individual.notify["avatar"].connect ( (s, p) => {
load_avatar.begin ();
});
public Avatar (int size, Individual? individual = null) {
this.individual = individual;
if (individual != null) {
individual.notify["avatar"].connect ( (s, p) => {
load_avatar.begin ();
});
}
this.size = size;
......@@ -47,7 +47,7 @@ public class Contacts.Avatar : DrawingArea {
set_size_request (size, size);
// If we don't have an avatar, don't try to load it later
this.avatar_loaded = (contact == null || contact.individual.avatar == null);
this.avatar_loaded = (individual == null || individual.avatar == null);
show ();
}
......@@ -70,16 +70,16 @@ public class Contacts.Avatar : DrawingArea {
}
private async void load_avatar () {
assert (this.contact != null);
assert (this.individual != null);
this.avatar_loaded = true;
try {
var stream = yield this.contact.individual.avatar.load_async (this.size);
var stream = yield this.individual.avatar.load_async (this.size);
this.cache = null;
this.pixbuf = yield new Gdk.Pixbuf.from_stream_at_scale_async (stream, this.size, this.size, true);
queue_draw ();
} catch (Error e) {
debug ("Couldn't load avatar of contact %s. Reason: %s", this.contact.individual.display_name, e.message);
debug ("Couldn't load avatar of contact %s. Reason: %s", this.individual.display_name, e.message);
}
}
......@@ -112,13 +112,13 @@ public class Contacts.Avatar : DrawingArea {
private Gdk.Pixbuf create_fallback () {
string name = "";
bool show_label = false;
if (this.contact != null && this.contact.individual != null) {
if (this.individual != null) {
name = find_display_name ();
/* If we don't have a usable name use the display_name
* to generate the color but don't show any label
*/
if (name == "") {
name = this.contact.individual.display_name;
name = this.individual.display_name;
} else {
show_label = true;
}
......@@ -136,7 +136,7 @@ public class Contacts.Avatar : DrawingArea {
private string find_display_name () {
string name = "";
Persona primary_persona = null;
foreach (var p in this.contact.individual.personas) {
foreach (var p in this.individual.personas) {
if (p.store.is_primary_store) {
primary_persona = p;
break;
......@@ -144,12 +144,12 @@ public class Contacts.Avatar : DrawingArea {
}
name = look_up_alias_for_display_name (primary_persona);
if (name == "") {
foreach (var p in this.contact.individual.personas) {
foreach (var p in this.individual.personas) {
name = look_up_alias_for_display_name (p);
}
}
if (name == "") {
foreach (var p in this.contact.individual.personas) {
foreach (var p in this.individual.personas) {
name = look_up_name_details_for_display_name (p);
}
}
......
......@@ -297,14 +297,14 @@ public class Contacts.ContactEditor : ContactForm {
});
}
public ContactEditor (Contact? contact, Store store, GLib.ActionGroup editor_actions) {
public ContactEditor (Individual? individual, Store store, GLib.ActionGroup editor_actions) {
this.store = store;
this.contact = contact;
this.individual = individual;
this.add_detail_button.get_popover ().insert_action_group ("edit", editor_actions);
if (contact != null) {
this.remove_button.sensitive = contact.can_remove_personas ();
if (individual != null) {
this.remove_button.sensitive = ContactUtils.can_remove_personas (individual);
} else {
this.remove_button.hide ();
}
......@@ -320,7 +320,7 @@ public class Contacts.ContactEditor : ContactForm {
create_avatar_button ();
create_name_entry ();
if (contact != null)
if (individual != null)
fill_in_contact ();
else
fill_in_empty ();
......@@ -333,7 +333,7 @@ public class Contacts.ContactEditor : ContactForm {
int last_store_position = 0;
bool is_first_persona = true;
var personas = this.contact.get_personas_for_display ();
var personas = ContactUtils.get_personas_for_display (individual);
foreach (var p in personas) {
if (!is_first_persona) {
var box = new Box (Orientation.HORIZONTAL, 12);
......@@ -735,7 +735,7 @@ public class Contacts.ContactEditor : ContactForm {
} else {
var details = p as EmailDetails;
if (details != null) {
var emails = Contact.sort_fields<EmailFieldDetails>(details.email_addresses);
var emails = ContactUtils.sort_fields<EmailFieldDetails>(details.email_addresses);
foreach (var email in emails) {
attach_row_with_entry (row, TypeSet.email, email, email.value);
rows.set (row, { email });
......@@ -763,7 +763,7 @@ public class Contacts.ContactEditor : ContactForm {
} else {
var details = p as PhoneDetails;
if (details != null) {
var phones = Contact.sort_fields<PhoneFieldDetails>(details.phone_numbers);
var phones = ContactUtils.sort_fields<PhoneFieldDetails>(details.phone_numbers);
foreach (var phone in phones) {
attach_row_with_entry (row, TypeSet.phone, phone, phone.value, type);
rows.set (row, { phone });
......@@ -984,8 +984,8 @@ public class Contacts.ContactEditor : ContactForm {
if (field_entry.value.changed && !props_set.has_key (field_entry.key)) {
PropertyData p = PropertyData ();
p.persona = null;
if (contact != null) {
p.persona = contact.find_persona_from_uid (entry.key);
if (individual != null) {
p.persona = ContactUtils.find_persona_from_uid (individual, entry.key);
}
switch (field_entry.key) {
......@@ -1024,18 +1024,7 @@ public class Contacts.ContactEditor : ContactForm {
return unlink_personas;
}
public void add_new_row_for_property (Persona? p, string prop_name, string? type = null) {
/* Somehow, I need to ensure that p is the main/default/first persona */
Persona persona = null;
if (contact != null) {
if (p == null) {
persona = new FakePersona (this.store, contact);
writable_personas[persona.uid] = new HashMap<string, Field?> ();
} else {
persona = p;
}
}
public void add_new_row_for_property (Persona? persona, string prop_name, string? type = null) {
int next_idx = 0;
foreach (var fields in writable_personas.values) {
if (fields.has_key (prop_name)) {
......@@ -1055,7 +1044,7 @@ public class Contacts.ContactEditor : ContactForm {
// Creates the contact's current avatar in a big button on top of the Editor
private void create_avatar_button () {
this.avatar = new Avatar (PROFILE_SIZE, this.contact);
this.avatar = new Avatar (PROFILE_SIZE, this.individual);
int width = this.get_allocated_width ();
if (width <= RESIZE_WIDTH)
......@@ -1073,7 +1062,7 @@ public class Contacts.ContactEditor : ContactForm {
// Show the avatar popover when the avatar is clicked
private void on_avatar_button_clicked (Button avatar_button) {
if (this.avatar_selector == null)
this.avatar_selector = new AvatarSelector (avatar_button, this.contact);
this.avatar_selector = new AvatarSelector (avatar_button, this.individual);
this.avatar_selector.set_avatar.connect ( (icon) => {
this.avatar.set_data ("value", icon);
......@@ -1110,8 +1099,8 @@ public class Contacts.ContactEditor : ContactForm {
this.name_entry.placeholder_text = _("Add name");
this.name_entry.set_data ("changed", false);
if (this.contact != null)
this.name_entry.text = this.contact.individual.display_name;
if (this.individual != null)
this.name_entry.text = this.individual.display_name;
/* structured name change */
this.name_entry.changed.connect (() => {
......
......@@ -40,7 +40,7 @@ public abstract class Contacts.ContactForm : Grid {
"notes"
};
protected Contact? contact;
protected Individual? individual;
protected Store store;
......@@ -80,7 +80,7 @@ public abstract class Contacts.ContactForm : Grid {
}
protected Label create_persona_store_label (Persona p) {
var store_name = new Label (Contact.format_persona_store_name_for_contact (p));
var store_name = new Label (ContactUtils.format_persona_store_name_for_contact (p));
var attrList = new Pango.AttrList ();
attrList.insert (Pango.attr_weight_new (Pango.Weight.BOLD));
store_name.set_attributes (attrList);
......
......@@ -28,17 +28,16 @@ public class Contacts.ContactList : ListBox {
private class ContactDataRow : ListBoxRow {
private const int LIST_AVATAR_SIZE = 48;
public Contact contact;
public Individual individual;
private Label label;
private Avatar avatar;
public CheckButton selector_button;
// Whether the selector should always be visible (or only on hover)
private bool checkbox_exposed = false;
public ContactDataRow(Contact c) {
this.contact = c;
this.contact.individual.notify.connect (on_contact_changed);
this.contact.notify["hidden"].connect ((o, p) => changed());
public ContactDataRow(Individual i) {
this.individual = i;
this.individual.notify.connect (on_contact_changed);
get_style_context (). add_class ("contact-data-row");
......@@ -46,9 +45,9 @@ public class Contacts.ContactList : ListBox {
grid.margin = 3;
grid.margin_start = 9;
grid.set_column_spacing (10);
this.avatar = new Avatar (LIST_AVATAR_SIZE, this.contact);
this.avatar = new Avatar (LIST_AVATAR_SIZE, this.individual);
this.label = new Label (c.individual.display_name);
this.label = new Label (individual.display_name);
this.label.ellipsize = Pango.EllipsizeMode.END;
this.label.valign = Align.CENTER;
this.label.halign = Align.START;
......@@ -71,7 +70,7 @@ public class Contacts.ContactList : ListBox {
}
private void on_contact_changed (Object obj, ParamSpec pspec) {
this.label.set_text (this.contact.individual.display_name);
this.label.set_text (this.individual.display_name);
changed ();
}
......@@ -94,7 +93,7 @@ public class Contacts.ContactList : ListBox {
}
}
public signal void selection_changed (Contact? contact);
public signal void selection_changed (Individual? individual);
public signal void contacts_marked (int contacts_marked);
int nr_contacts_marked = 0;
......@@ -124,8 +123,8 @@ public class Contacts.ContactList : ListBox {
this.store.added.connect (contact_added_cb);
this.store.removed.connect (contact_removed_cb);
foreach (var c in this.store.get_contacts ())
contact_added_cb (this.store, c);
foreach (var i in this.store.get_contacts ())
contact_added_cb (this.store, i);
get_style_context ().add_class ("contacts-contact-list");
......@@ -148,8 +147,8 @@ public class Contacts.ContactList : ListBox {
}
private int compare_rows (ListBoxRow row_a, ListBoxRow row_b) {
var a = ((ContactDataRow) row_a).contact.individual;
var b = ((ContactDataRow) row_b).contact.individual;
var a = ((ContactDataRow) row_a).individual;
var b = ((ContactDataRow) row_b).individual;
// Always prefer favourites over non-favourites.
if (a.is_favourite != b.is_favourite)
......@@ -171,7 +170,7 @@ public class Contacts.ContactList : ListBox {
}
private void update_header (ListBoxRow row, ListBoxRow? before) {
var current = ((ContactDataRow) row).contact.individual;
var current = ((ContactDataRow) row).individual;
if (before == null) {
if (current.is_favourite)
......@@ -181,7 +180,7 @@ public class Contacts.ContactList : ListBox {
return;
}
var previous = ((ContactDataRow) before).contact.individual;
var previous = ((ContactDataRow) before).individual;
if (!current.is_favourite && previous.is_favourite) {
row.set_header (create_header_label (_("All Contacts")));
} else {
......@@ -203,12 +202,17 @@ public class Contacts.ContactList : ListBox {
return label;
}
private void contact_added_cb (Store store, Contact c) {
var row = new ContactDataRow(c);
row.selector_button.toggled.connect ( () => { on_row_checkbox_toggled (row); });
row.selector_button.visible = (this.state == UiState.SELECTING);
private void contact_added_cb (Store store, Individual i) {
// Don't create a row for ignorable contacts
if (!ContactUtils.is_ignorable (i)) {
var row = new ContactDataRow (i);
row.selector_button.toggled.connect ( () => { on_row_checkbox_toggled (row); });
row.selector_button.visible = (this.state == UiState.SELECTING);
add (row);
add (row);
} else {
debug ("Contact %s was ignored", i.id);
}
}
private void on_row_checkbox_toggled (ContactDataRow row) {
......@@ -226,57 +230,77 @@ public class Contacts.ContactList : ListBox {
contacts_marked (this.nr_contacts_marked);
}
private void contact_removed_cb (Store store, Contact c) {
var row = find_row_for_contact (c);
private void contact_removed_cb (Store store, Individual i) {
var row = find_row_for_contact (i);
if (row != null)
row.destroy ();
}
public override void row_selected (ListBoxRow? row) {
var data = (ContactDataRow?) row as ContactDataRow;
var contact = data != null ? data.contact : null;
selection_changed (contact);
var individual = data != null ? data.individual : null;
selection_changed (individual);
#if HAVE_TELEPATHY
if (contact != null)
contact.fetch_contact_info ();
if (individual != null)
Contact.fetch_contact_info (individual);
#endif
}
private bool filter_row (ListBoxRow row) {
var contact = ((ContactDataRow) row).contact;
return !contact.hidden && this.filter_query.is_match (contact.individual) > 0;
var individual = ((ContactDataRow) row).individual;
return this.filter_query.is_match (individual) > 0;
}
public void select_contact (Contact? contact) {
if (contact == null) {
public void select_contact (Individual? individual) {
if (individual == null) {
/* deselect */
select_row (null);
return;
}
select_row (find_row_for_contact (contact));
select_row (find_row_for_contact (individual));
}
private ContactDataRow? find_row_for_contact (Contact contact) {
public void hide_contact (Individual? individual) {
if (individual != null) {
find_row_for_contact (individual).hide ();
}
}
private ContactDataRow? find_row_for_contact (Individual individual) {
foreach (var widget in get_children ()) {
var row = ((ContactDataRow) widget);
if (row.contact == contact)
if (row.individual == individual)
return row;
}
return null;
}
public LinkedList<Contact> get_marked_contacts () {
var cs = new LinkedList<Contact> ();
public LinkedList<Individual> get_marked_contacts () {
var cs = new LinkedList<Individual> ();
foreach (var widget in get_children ()) {
var row = widget as ContactDataRow;
if (row.selector_button.active)
cs.add (row.contact);
cs.add (row.individual);
}
return cs;
}
public LinkedList<Individual> get_marked_contacts_and_hide () {
var cs = new LinkedList<Individual> ();
foreach (var widget in get_children ()) {
var row = widget as ContactDataRow;
if (row.selector_button.active) {
row.visible = false;
cs.add (row.individual);
}
}
return cs;
}
public override bool button_press_event (Gdk.EventButton event) {
base.button_press_event (event);
......
......@@ -35,7 +35,7 @@ public class Contacts.ContactPane : Stack {
private Store store;
public Contact? contact = null;
public Individual? individual = null;
[GtkChild]
private Grid none_selected_page;
......@@ -69,7 +69,7 @@ public class Contacts.ContactPane : Stack {
/* Signals */
public signal void contacts_linked (string? main_contact, string linked_contact, LinkOperation operation);
public signal void will_delete (Contact contact);
public signal void will_delete (Individual individual);
/**
* Passes the changed display name to all listeners after edit mode has been completed.
*/
......@@ -97,17 +97,17 @@ public class Contacts.ContactPane : Stack {
this.edit_contact_actions.add_action_entries (action_entries, this);
}
public void add_suggestion (Contact c) {
public void add_suggestion (Individual i) {
var parent_overlay = this.get_parent () as Overlay;
remove_suggestion_grid ();
this.suggestion_grid = new LinkSuggestionGrid (c);
this.suggestion_grid = new LinkSuggestionGrid (i);
bind_property ("narrow", this.suggestion_grid, "orientation", BindingFlags.SYNC_CREATE);
parent_overlay.add_overlay (this.suggestion_grid);
this.suggestion_grid.suggestion_accepted.connect ( () => {
var linked_contact = c.individual.display_name;
link_contacts.begin (contact, c, this.store, (obj, result) => {
var linked_contact = this.individual.display_name;
link_contacts.begin (this.individual, i, this.store, (obj, result) => {
var operation = link_contacts.end (result);
this.contacts_linked (null, linked_contact, operation);
});
......@@ -116,18 +116,18 @@ public class Contacts.ContactPane : Stack {
this.suggestion_grid.suggestion_rejected.connect ( () => {
/* TODO: Add undo */
store.add_no_suggest_link (contact, c);
store.add_no_suggest_link (this.individual, i);
remove_suggestion_grid ();
});
}
public void show_contact (Contact? contact) {
if (this.contact == contact)
public void show_contact (Individual? individual) {
if (this.individual == individual)
return;
this.contact = contact;
this.individual = individual;
if (this.contact != null) {
if (this.individual != null) {
show_contact_sheet ();
} else {
remove_contact_sheet ();
......@@ -136,19 +136,18 @@ public class Contacts.ContactPane : Stack {
}
private void show_contact_sheet () {
assert (this.contact != null);
assert (this.individual != null);
remove_contact_sheet();
this.sheet = new ContactSheet (this.contact, this.store);
this.sheet = new ContactSheet (this.individual, this.store);
bind_property ("narrow", this.sheet, "narrow", BindingFlags.SYNC_CREATE);
this.contact_sheet_page.add (this.sheet);
set_visible_child (this.contact_sheet_page);
var matches = this.store.aggregator.get_potential_matches (this.contact.individual, MatchResult.HIGH);
foreach (var ind in matches.keys) {
var c = Contact.from_individual (ind);
if (c != null && this.contact.suggest_link_to (c)) {
add_suggestion (c);
var matches = this.store.aggregator.get_potential_matches (this.individual, MatchResult.HIGH);
foreach (var i in matches.keys) {
if (i != null && ContactUtils.suggest_link_to (this.store, this.individual, i)) {
add_suggestion (i);
break;
}
}
......@@ -170,7 +169,7 @@ public class Contacts.ContactPane : Stack {
if (this.editor != null)
remove_contact_editor ();
this.editor = new ContactEditor (this.contact, this.store, this.edit_contact_actions);
this.editor = new ContactEditor (this.individual, this.store, this.edit_contact_actions);
this.editor.remove_button.clicked.connect (delete_contact);
......@@ -204,21 +203,20 @@ public class Contacts.ContactPane : Stack {
var tok = action.name.split (".");
if (tok[0] == "add") {
editor.add_new_row_for_property (contact.find_primary_persona (),
editor.add_new_row_for_property (ContactUtils.find_primary_persona (individual),
tok[1],
tok.length > 2 ? tok[2].up () : null);
}
}
void delete_contact () {
if (contact != null) {
contact.hidden = true;
this.will_delete (contact);
if (individual != null) {
will_delete (individual);
}
}
public void start_editing() {
if (this.on_edit_mode || this.contact == null)
if (this.on_edit_mode || this.individual == null)
return;
this.on_edit_mode = true;
......@@ -239,7 +237,7 @@ public class Contacts.ContactPane : Stack {
remove_contact_editor ();
if (this.contact != null)
if (this.individual != null)
show_contact_sheet ();
else
set_visible_child (this.none_selected_page);
......@@ -248,23 +246,25 @@ public class Contacts.ContactPane : Stack {
private async void save_editor_changes () {
foreach (var prop in this.editor.properties_changed ().entries) {
try {
yield Contact.set_persona_property (prop.value.persona, prop.key, prop.value.value);
yield ContactUtils.set_persona_property (prop.value.persona, prop.key, prop.value.value);
} catch (Error e) {
show_message (e.message);
}
}
/* unlink personas */
/*
foreach (var persona in this.editor.get_unlink_personas()) {
unlink_persona.begin (store, contact, persona, (obj, result) => {
unlink_persona.end (result);
});
}
*/
if (this.editor.name_changed ()) {
var v = this.editor.get_full_name_value ();
try {
yield this.contact.set_individual_property ("full-name", v);
yield ContactUtils.set_individual_property (individual, "full-name", v);
display_name_changed (v.get_string ());
} catch (Error e) {
show_message (e.message);
......@@ -274,7 +274,7 @@ public class Contacts.ContactPane : Stack {
if (this.editor.avatar_changed ()) {
var v = this.editor.get_avatar_value ();
try {
yield this.contact.set_individual_property ("avatar", v);
yield ContactUtils.set_individual_property (individual, "avatar", v);
} catch (Error e) {
show_message (e.message);
}
......@@ -283,7 +283,7 @@ public class Contacts.ContactPane : Stack {
public void new_contact () {
this.on_edit_mode = true;
this.contact = null;
this.individual = null;
remove_contact_sheet ();
create_contact_editor ();
set_visible_child (this.contact_editor_page);
......@@ -316,22 +316,33 @@ public class Contacts.ContactPane : Stack {
return;
}
// Create a FakeContact temporary persona so we can show it already to the user
var fake_persona = new FakePersona (FakePersonaStore.the_store(), details);
var fake_personas = new HashSet<Persona> ();
fake_personas.add (fake_persona);
var fake_individual = new Individual(fake_personas);
this.parent_window.set_shown_contact (fake_individual);
// Create the contact
var primary_store = this.store.aggregator.primary_store;
Persona? persona = null;
try {
persona = yield Contact.create_primary_persona_for_details (primary_store, details);
persona = yield primary_store.add_persona_from_details (details);
} catch (Error e) {