[gnome-contacts] Refactor link/unlink operations
- From: Niels De Graef <nielsdg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts] Refactor link/unlink operations
- Date: Mon, 3 Feb 2020 12:34:16 +0000 (UTC)
commit aa2433307d485b020b8630601dbd1e91b569cea9
Author: Julian Sparber <julian sparber net>
Date: Thu Jul 18 14:42:02 2019 +0200
Refactor link/unlink operations
This cleans up how linking and unlinking is done, by using the methodes
provided by folks.
src/contacts-contact-editor.vala | 6 +
src/contacts-contact-list.vala | 5 +-
src/contacts-contact-pane.vala | 16 +-
src/contacts-linked-personas-dialog.vala | 10 +-
src/contacts-linking.vala | 780 +++----------------------------
src/contacts-store.vala | 91 +---
src/contacts-utils.vala | 12 +
src/contacts-window.vala | 8 +-
8 files changed, 110 insertions(+), 818 deletions(-)
---
diff --git a/src/contacts-contact-editor.vala b/src/contacts-contact-editor.vala
index 8a45cce..396a681 100644
--- a/src/contacts-contact-editor.vala
+++ b/src/contacts-contact-editor.vala
@@ -100,6 +100,7 @@ public class Contacts.ContactEditor : ContactForm {
HashMap<int, RowData?> rows;
}
+ private HashSet<Persona> unlink_personas;
/* the key of the hash_map is the uid of the persona */
private HashMap<string, HashMap<string, Field?>> writable_personas;
@@ -116,6 +117,7 @@ public class Contacts.ContactEditor : ContactForm {
}
construct {
+ this.unlink_personas = new HashSet<Persona> ();
this.writable_personas = new HashMap<string, HashMap<string, Field?>> ();
this.container_grid.size_allocate.connect(on_container_grid_size_allocate);
}
@@ -872,6 +874,10 @@ public class Contacts.ContactEditor : ContactForm {
return props_set;
}
+ public HashSet<Persona> get_unlink_personas () {
+ return unlink_personas;
+ }
+
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) {
diff --git a/src/contacts-contact-list.vala b/src/contacts-contact-list.vala
index 5b7385e..aa3cd33 100644
--- a/src/contacts-contact-list.vala
+++ b/src/contacts-contact-list.vala
@@ -70,6 +70,7 @@ public class Contacts.ContactList : ListBox {
}
private void on_contact_changed (Object obj, ParamSpec pspec) {
+ //TODO: Update also the Avatar
this.label.set_text (this.individual.display_name);
changed ();
}
@@ -203,8 +204,8 @@ public class Contacts.ContactList : ListBox {
}
private void contact_added_cb (Store store, Individual i) {
- // Don't create a row for ignorable contacts
- if (!Contacts.Utils.is_ignorable (i)) {
+ // Don't create a row for ignorable contacts are the individual already has a row
+ if (!Contacts.Utils.is_ignorable (i) && find_row_for_contact(i) == null) {
var row = new ContactDataRow (i);
row.selector_button.toggled.connect ( () => { on_row_checkbox_toggled (row); });
row.selector_button.visible = (this.state == UiState.SELECTING);
diff --git a/src/contacts-contact-pane.vala b/src/contacts-contact-pane.vala
index 9e048ef..adaafeb 100644
--- a/src/contacts-contact-pane.vala
+++ b/src/contacts-contact-pane.vala
@@ -89,10 +89,12 @@ public class Contacts.ContactPane : Stack {
this.suggestion_grid.suggestion_accepted.connect ( () => {
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);
- });
+ var operation = new LinkOperation (this.store);
+ var to_link = new LinkedList<Individual> ();
+ to_link.add (this.individual);
+ to_link.add (i);
+ operation.do.begin (to_link);
+ this.contacts_linked (null, linked_contact, operation);
remove_suggestion_grid ();
});
@@ -260,6 +262,12 @@ public class Contacts.ContactPane : Stack {
show_message (e.message);
}
}
+
+ /* unlink personas */
+ if (this.editor.get_unlink_personas ().size > 0) {
+ var operation = new UnLinkOperation (this.store);
+ operation.do.begin (this.individual, this.editor.get_unlink_personas ());
+ }
}
public void new_contact () {
diff --git a/src/contacts-linked-personas-dialog.vala b/src/contacts-linked-personas-dialog.vala
index 6af7aef..8ae9413 100644
--- a/src/contacts-linked-personas-dialog.vala
+++ b/src/contacts-linked-personas-dialog.vala
@@ -79,15 +79,7 @@ public class Contacts.LinkedPersonasDialog : Dialog {
/* signal */
button.clicked.connect (() => {
- unlink_persona.begin (store, individual, p, (obj, result) => {
- unlink_persona.end (result);
-
- row_grid.destroy ();
-
- this.any_unlinked = true;
- /* TODO: Support undo */
- /* TODO: Ensure we don't get suggestion for this linkage again */
- });
+ // TODO: handly unlinking
});
row_grid.show_all ();
diff --git a/src/contacts-linking.vala b/src/contacts-linking.vala
index 76043de..d5ddd2f 100644
--- a/src/contacts-linking.vala
+++ b/src/contacts-linking.vala
@@ -22,751 +22,87 @@ using Gee;
namespace Contacts {
public class LinkOperation : Object {
- internal class Change {
- public PersonaAttribute attribute;
- public Persona persona;
- public Object old_value;
- }
- private Persona? _added_persona;
- private Individual? main_contact;
- private ArrayList<Persona>? split_out_personas;
- ArrayList<Change> changes;
-
- public LinkOperation() {
- changes = new ArrayList<Change> ();
- }
-
- public void set_main_contact (Individual? individual) {
- main_contact = individual;
- }
-
- public void set_split_out_contact (Individual? individual) {
- if (individual != null) {
- split_out_personas = new ArrayList<Persona> ();
- split_out_personas.add_all (individual.personas);
+ private weak Store store;
+ private HashSet<HashSet<Persona>> personas_to_link;
+ private bool finished { get; set; default = false; }
+
+ public LinkOperation(Store store) {
+ this.store = store;
+ this.personas_to_link = new HashSet<HashSet<Persona>> ();
+ }
+
+ /* Link individuals */
+ public async void do (LinkedList<Individual> individuals) {
+ var personas_to_link = new HashSet<Persona> ();
+ foreach (var i in individuals) {
+ var saved_personas = new HashSet<Persona> ();
+ foreach (var persona in i.personas) {
+ personas_to_link.add (persona);
+ saved_personas.add (persona);
+ }
+ this.personas_to_link.add (saved_personas);
}
- }
- public void added_persona (Persona persona) {
- _added_persona = persona;
- }
+ // We don't need to unlink the individuals because we are using every persona
+ yield link_personas(this.store, this.store.aggregator, personas_to_link);
- public void add_change (PersonaAttribute attribute, Persona persona, Object old_value) {
- var c = new Change ();
- c.attribute = attribute;
- c.persona = persona;
- c.old_value = old_value;
- changes.add (c);
+ finished = true;
}
+ /* Undo the linking */
public async void undo () {
- if (main_contact != null)
- main_contact.set_data ("contacts-master-at-join", true);
- if (split_out_personas != null) {
- foreach (var p in split_out_personas)
- p.set_data ("contacts-new-contact", true);
- }
-
- try {
- if (_added_persona != null) {
- yield _added_persona.store.remove_persona (_added_persona);
- }
- foreach (var c in changes) {
- if (c.persona != _added_persona) {
- yield c.attribute.set_value (c.persona, c.old_value);
- }
- }
- } catch (GLib.Error e) {
- warning ("Error when undoing linking: %s\n", e.message);
- }
-
- if (main_contact != null)
- main_contact.set_data ("contacts-master-at-join", false);
-
- if (split_out_personas != null) {
- foreach (var p in split_out_personas)
- p.set_data ("contacts-new-contact", false);
- }
- }
- }
-
- public abstract class PersonaAttribute : Object {
- public string property_name;
-
- public static HashSet<PersonaAttribute> create_set () {
- return new HashSet<PersonaAttribute>((HashDataFunc<PersonaAttribute>) PersonaAttribute.hash,
- (EqualDataFunc<PersonaAttribute>) PersonaAttribute.equal);
- }
-
- public virtual bool is_removable (Persona from_persona) {
- return (property_name in from_persona.writeable_properties);
- }
-
- public abstract bool is_referenced_by_persona (Persona persona);
-
- public abstract string to_string ();
-
- public virtual bool equal (PersonaAttribute that) {
- return this.property_name == that.property_name;
- }
-
- public virtual uint hash () {
- return this.property_name.hash ();
- }
-
- public abstract async void persona_apply_attributes (Persona persona,
- Set<PersonaAttribute> added_attributes,
- Set<PersonaAttribute> removed_attributes,
- LinkOperation operation);
- public abstract async void set_value (Persona persona, Object value);
- }
-
- internal class PersonaAttributeLocalId : PersonaAttribute {
- string value;
-
- public PersonaAttributeLocalId (string value) {
- property_name = "local-ids";
- this.value = value;
- }
-
- public override bool is_removable (Persona from_persona) {
- return base.is_removable (from_persona) && value != from_persona.iid;
- }
-
- public override string to_string () {
- return "local_id: " + value;
- }
-
- public override bool is_referenced_by_persona (Persona persona) {
- var details = persona as LocalIdDetails;
- if (details == null)
- return false;
-
- return value in details.local_ids;
- }
-
- public override async void persona_apply_attributes (Persona persona,
- Set<PersonaAttribute> added_attributes,
- Set<PersonaAttribute> removed_attributes,
- LinkOperation operation) {
- var details = persona as LocalIdDetails;
- if (details == null)
- return;
-
- var added_values = new HashSet<string> ();
- foreach (var added in added_attributes) {
- added_values.add (((PersonaAttributeLocalId)added).value);
- }
-
- var removed_values = new HashSet<string> ();
- foreach (var removed in removed_attributes) {
- removed_values.add (((PersonaAttributeLocalId)removed).value);
- }
-
- var new_values = new HashSet<string> ();
- bool changed = false;
- foreach (var v in details.local_ids) {
- if (v in removed_values) {
- changed = true;
- continue;
- }
- new_values.add (v);
- if (v in added_values)
- added_values.remove (v);
- }
- foreach (var v2 in added_values) {
- changed = true;
- new_values.add (v2);
- }
-
- if (changed) {
- try {
- var old_value = new HashSet<string> ();
- old_value.add_all (details.local_ids);
- yield details.change_local_ids (new_values);
- operation.add_change (this, persona, old_value);
- } catch (GLib.Error e) {
- warning ("Unable to set local ids when linking: %s\n", e.message);
- }
- }
- }
-
- public override async void set_value (Persona persona, Object value) {
- var details = persona as LocalIdDetails;
- if (details == null)
- return;
-
- try {
- var v = value as HashSet<string>;
- yield details.change_local_ids (v);
- } catch (GLib.Error e) {
- warning ("Unable to set local ids when undoing link: %s\n", e.message);
+ var individual = this.personas_to_link.first_match(() => {return true;})
+ .first_match(() => {return true;}).individual;
+ yield store.aggregator.unlink_individual (individual);
+ foreach (var personas in personas_to_link) {
+ yield link_personas (this.store, this.store.aggregator, personas);
}
}
-
- public override bool equal (PersonaAttribute _that) {
- var that = _that as PersonaAttributeLocalId;
- return
- that != null &&
- base.equal (that) &&
- this.value == that.value;
- }
-
- public override uint hash () {
- return this.value.hash () ^ base.hash ();
- }
}
- internal class PersonaAttributeImAddress : PersonaAttribute {
- string protocol;
- ImFieldDetails detail;
-
- public PersonaAttributeImAddress (string protocol, ImFieldDetails detail) {
- property_name = "im-addresses";
- this.protocol = protocol;
- this.detail = detail;
- }
-
- public override string to_string () {
- return "im_addresses: " + protocol + ":" + detail.value;
- }
-
- public override bool is_referenced_by_persona (Persona persona) {
- var details = persona as ImDetails;
- if (details == null)
- return false;
-
- return detail in details.im_addresses.get (protocol);
- }
-
- public override async void persona_apply_attributes (Persona persona,
- Set<PersonaAttribute> added_attributes,
- Set<PersonaAttribute> removed_attributes,
- LinkOperation operation) {
- var details = persona as ImDetails;
- if (details == null)
- return;
-
- var added_values = new HashMultiMap<string, ImFieldDetails> (null, null,
- AbstractFieldDetails<string>.hash_static,
- AbstractFieldDetails<string>.equal_static);
- foreach (var added in added_attributes) {
- added_values.set (((PersonaAttributeImAddress)added).protocol,
((PersonaAttributeImAddress)added).detail);
- }
-
- var removed_values = new HashMultiMap<string, ImFieldDetails> (null, null,
- AbstractFieldDetails<string>.hash_static,
-
AbstractFieldDetails<string>.equal_static);
-
- foreach (var removed in removed_attributes) {
- removed_values.set (((PersonaAttributeImAddress)removed).protocol,
((PersonaAttributeImAddress)removed).detail);
- }
-
- var new_values =
- new HashMultiMap<string, ImFieldDetails> (null, null,
- AbstractFieldDetails<string>.hash_static,
- AbstractFieldDetails<string>.equal_static);
- bool changed = false;
- foreach (var proto1 in details.im_addresses.get_keys ()) {
- foreach (var detail1 in details.im_addresses.get (proto1)) {
- if (removed_values.get (proto1).contains (detail1)) {
- changed = true;
- continue;
- }
- new_values.set (proto1, detail1);
- if (added_values.get (proto1).contains (detail1)) {
- added_values.remove (proto1, detail1);
- }
- }
- }
- foreach (var proto2 in added_values.get_keys ()) {
- foreach (var detail2 in added_values.get (proto2)) {
- changed = true;
- new_values.set (proto2, detail2);
- }
- }
-
- if (changed) {
- try {
- var old_value = details.im_addresses;
- yield details.change_im_addresses (new_values);
- operation.add_change (this, persona, old_value);
- } catch (GLib.Error e) {
- warning ("Unable to set im address when linking: %s\n", e.message);
- }
- }
+ public class UnLinkOperation : Object {
+ private weak Store store;
+ public UnLinkOperation(Store store) {
+ this.store = store;
}
- public override async void set_value (Persona persona, Object value) {
- var details = persona as ImDetails;
- if (details == null)
- return;
+ /* Remove a personas from individual */
+ public async void do (Individual main, Set<Persona> personas_to_remove) {
+ var personas_to_keep = new HashSet<Persona> ();
+ foreach (var persona in main.personas)
+ if (!personas_to_remove.contains (persona))
+ personas_to_keep.add (persona);
try {
- var v = value as HashMultiMap<string, ImFieldDetails>;
- yield details.change_im_addresses (v);
- } catch (GLib.Error e) {
- warning ("Unable to set local ids when undoing link: %s\n", e.message);
- }
- }
-
- public override bool equal (PersonaAttribute _that) {
- var that = _that as PersonaAttributeImAddress;
- return
- that != null &&
- base.equal (that) &&
- this.protocol == that.protocol &&
- this.detail.equal (that.detail);
- }
-
- public override uint hash () {
- return this.protocol.hash () ^ this.detail.hash () ^ base.hash ();
- }
- }
-
- internal class PersonaAttributeWebService : PersonaAttribute {
- string service;
- WebServiceFieldDetails detail;
-
- public PersonaAttributeWebService (string service, WebServiceFieldDetails detail) {
- property_name = "web-service-addresses";
- this.service = service;
- this.detail = detail;
- }
-
- public override string to_string () {
- return "web_service_addresses: " + service + ":" + detail.value;
- }
-
- public override bool is_referenced_by_persona (Persona persona) {
- var details = persona as WebServiceDetails;
- if (details == null)
- return false;
-
- return detail in details.web_service_addresses.get (service);
- }
-
- public override async void persona_apply_attributes (Persona persona,
- Set<PersonaAttribute> added_attributes,
- Set<PersonaAttribute> removed_attributes,
- LinkOperation operation) {
- var details = persona as WebServiceDetails;
- if (details == null)
- return;
-
- var added_values = new HashMultiMap<string, WebServiceFieldDetails> (null, null,
-
AbstractFieldDetails<string>.hash_static,
-
AbstractFieldDetails<string>.equal_static);
- foreach (var added in added_attributes) {
- added_values.set (((PersonaAttributeWebService)added).service,
((PersonaAttributeWebService)added).detail);
- }
-
- var removed_values = new HashMultiMap<string, WebServiceFieldDetails> (null, null,
-
AbstractFieldDetails<string>.hash_static,
-
AbstractFieldDetails<string>.equal_static);
- foreach (var removed in removed_attributes) {
- removed_values.set (((PersonaAttributeWebService)removed).service,
((PersonaAttributeWebService)removed).detail);
- }
-
- var new_values =
- new HashMultiMap<string, WebServiceFieldDetails> (null, null,
- AbstractFieldDetails<string>.hash_static,
- AbstractFieldDetails<string>.equal_static);
- bool changed = false;
- foreach (var srv1 in details.web_service_addresses.get_keys ()) {
- foreach (var detail1 in details.web_service_addresses.get (srv1)) {
- if (removed_values.get (srv1).contains (detail1)) {
- changed = true;
- continue;
- }
- new_values.set (srv1, detail1);
- if (added_values.get (srv1).contains (detail1)) {
- added_values.remove (srv1, detail1);
- }
- }
- }
- foreach (var srv2 in added_values.get_keys ()) {
- foreach (var detail2 in added_values.get (srv2)) {
- changed = true;
- new_values.set (srv2, detail2);
- }
- }
-
- if (changed) {
- try {
- var old_value = details.web_service_addresses;
- yield details.change_web_service_addresses (new_values);
- operation.add_change (this, persona, old_value);
- } catch (GLib.Error e) {
- warning ("Unable to set web service when linking: %s\n", e.message);
- }
- }
- }
-
- public override async void set_value (Persona persona, Object value) {
- var details = persona as WebServiceDetails;
- if (details == null)
- return;
-
- try {
- var v = value as HashMultiMap<string, WebServiceFieldDetails>;
- yield details.change_web_service_addresses (v);
- } catch (GLib.Error e) {
- warning ("Unable to set local ids when undoing link: %s\n", e.message);
- }
- }
-
- public override bool equal (PersonaAttribute _that) {
- var that = _that as PersonaAttributeWebService;
- return
- that != null &&
- base.equal (that) &&
- this.service == that.service &&
- this.detail.equal (that.detail);
- }
-
- public override uint hash () {
- return this.service.hash () ^ this.detail.hash () ^ base.hash ();
- }
- }
-
- public static void add_linkable_attributes (HashSet<PersonaAttribute> set, Persona persona) {
- if (persona is LocalIdDetails) {
- foreach (var id in ((LocalIdDetails) persona).local_ids) {
- set.add (new PersonaAttributeLocalId (id));
- }
- }
-
- if (persona is ImDetails) {
- foreach (var proto in ((ImDetails) persona).im_addresses.get_keys ()) {
- foreach (var im in ((ImDetails) persona).im_addresses.get (proto)) {
- set.add (new PersonaAttributeImAddress (proto, im));
- }
- }
- }
-
- if (persona is WebServiceDetails) {
- foreach (var srv in ((WebServiceDetails) persona).web_service_addresses.get_keys ()) {
- foreach (var web in ((WebServiceDetails) persona).web_service_addresses.get (srv)) {
- set.add (new PersonaAttributeWebService (srv, web));
- }
- }
- }
- }
-
- public static Set<PersonaAttribute> get_linkable_attributes (Persona persona) {
- var res = PersonaAttribute.create_set ();
- add_linkable_attributes (res, persona);
- return res;
- }
-
- public static Set<PersonaAttribute> get_linkable_attributes_for_individual (Individual individual) {
- var res = PersonaAttribute.create_set ();
- foreach (var persona in individual.personas)
- add_linkable_attributes (res, persona);
- return res;
- }
-
- public static bool persona_can_link_to (Persona persona, Set<PersonaAttribute> attributes) {
- var property_names = new HashSet<string>();
- foreach (var a in attributes)
- property_names.add (a.property_name);
-
- foreach (var p in property_names) {
- if (! (p in persona.writeable_properties))
- return false;
- }
- return true;
- }
-
- internal bool attr_type_equal (PersonaAttribute a, PersonaAttribute b) {
- return
- a.get_type() == b.get_type() &&
- a.property_name == b.property_name;
- }
-
- public static async void persona_apply_attributes (Persona persona,
- Set<PersonaAttribute>? added_attributes,
- Set<PersonaAttribute>? removed_attributes,
- LinkOperation operation) {
- var properties = new HashSet<PersonaAttribute>();
-
- if (added_attributes != null) {
- foreach (var a1 in added_attributes) {
- properties.add (a1);
- }
- }
- if (removed_attributes != null) {
- foreach (var a2 in removed_attributes) {
- properties.add (a2);
- }
- }
-
- foreach (var property in properties) {
- var added = PersonaAttribute.create_set ();
- var removed = PersonaAttribute.create_set ();
- if (added_attributes != null) {
- foreach (var a3 in added_attributes) {
- if (attr_type_equal (a3, property))
- added.add (a3);
- }
- }
- if (removed_attributes != null) {
- foreach (var a4 in removed_attributes) {
- if (attr_type_equal (a4, property))
- removed.add (a4);
- }
- }
- yield property.persona_apply_attributes (persona, added, removed, operation);
- }
- }
-
- public async LinkOperation link_contacts (Individual main, Individual? other, Store contacts_store) {
- // This should not be used as being replaced with the new individual
- // instead we should always pick this contact to keep around
- main.set_data ("contacts-master-at-join", true);
-
- var operation = new LinkOperation ();
- operation.set_split_out_contact (other);
-
- var main_linkables = get_linkable_attributes_for_individual (main);
- Set<PersonaAttribute>? other_linkables = null;
- if (other != null)
- other_linkables = get_linkable_attributes_for_individual (other);
- Set<PersonaAttribute>? linkables = null;
-
- // Remove all linkable data from each contact that is already in the other contact
- if (other_linkables != null) {
- main_linkables.remove_all (other_linkables);
- other_linkables.remove_all (main_linkables);
- }
-
- Persona? write_persona = null;
- foreach (var p1 in main.personas) {
- if (other_linkables != null &&
- persona_can_link_to (p1, other_linkables)) {
- write_persona = p1;
- linkables = other_linkables;
- if (write_persona.store.is_primary_store)
- break; // Exit if we find a primary persona, as we prefer these
- }
- }
-
- if (other != null &&
- (write_persona == null || !write_persona.store.is_primary_store)) {
- foreach (var p2 in other.personas) {
- if (persona_can_link_to (p2, main_linkables)) {
- // Only override main persona if its a primary store persona
- if (write_persona == null || p2.store.is_primary_store) {
- write_persona = p2;
- linkables = main_linkables;
- if (write_persona.store.is_primary_store)
- break; // Exit if we find a primary persona, as we prefer these
- }
- }
- }
- }
-
- if (write_persona == null) {
- var details = new HashTable<string, Value?> (str_hash, str_equal);
- try {
- var v = Value (typeof (string));
- v.set_string (main.display_name);
- details.set ("full-name", v);
- write_persona = yield contacts_store.aggregator.primary_store.add_persona_from_details (details);
- operation.added_persona (write_persona);
- linkables = main_linkables;
- if (other_linkables != null)
- linkables.add_all (other_linkables);
- } catch (GLib.Error e) {
- main.set_data ("contacts-master-at-join", false);
- warning ("Unable to create new persona when linking: %s\n", e.message);
- return operation;
- }
- }
-
- yield persona_apply_attributes (write_persona, linkables, null, operation);
-
- main.set_data ("contacts-master-at-join", false);
-
- return operation;
- }
-
- public async LinkOperation unlink_persona (Store store, Individual individual, Persona persona_to_unlink) {
- var persona_to_unlink_removals = PersonaAttribute.create_set ();
- var other_personas_removals = PersonaAttribute.create_set ();
-
- var operation = new LinkOperation ();
- operation.set_main_contact (individual);
-
- foreach (PersonaAttribute a1 in get_linkable_attributes (persona_to_unlink)) {
- // Check that this attribute actually is used to link this persona to the individual
- bool used_to_link = false;
- foreach (var persona in individual.personas) {
- if (persona != persona_to_unlink &&
- a1.is_referenced_by_persona (persona)) {
- used_to_link = true;
- break;
- }
- }
- if (!used_to_link)
- continue; // Wasn't used, no need to do anything about it
-
- if (a1.is_removable (persona_to_unlink)) {
- // We can remove the attribute from the persona, which should completely break any linkage
- // due to this attribute
- persona_to_unlink_removals.add (a1);
- } else {
- // We can't remove the attribute from the persona, need to make sure no other persona
- // references this
- other_personas_removals.add (a1);
- }
- }
-
- // At this point we know how to unlink the persona from the individual, however
- // doing so may cause the remaining personas to form disjoint sets rather than
- // a single Individual. Consider two subsets of personas A and B, and the unlinked
- // persona u which make up the original individual. When unlinking u A and B may be
- // disjoint if:
- // * A links to u and u Links to B, then the data from u that linked it to B was
- // removed (and no other links go between A and B)
- // or
- // * A and B both link to u, but to unlink them from u we removed the data in A and
- // B that caused this link (and no other links go between A and B)
- //
- // To fix this up we need to ensure that all the remaining personas in the inidivudal
- // do have links by picking (or creating if there is none) a persona where all linkable
- // attributes are writeable and ensuring that it can reach all the other remaining
- // personas. We do this the easy way by just adding all linkable attributes to this
- // persona
- var main_persona_additions = PersonaAttribute.create_set ();
- foreach (var p1 in individual.personas) {
- if (p1 == persona_to_unlink)
- continue;
- foreach (PersonaAttribute a2 in get_linkable_attributes (p1)) {
- if (a2 in other_personas_removals)
- continue;
- main_persona_additions.add (a2);
- }
- }
-
- // Find tha main persona that will be used to add the extra linking info to
- // avoid disjoint sets
- Persona? main_persona = null;
- foreach (var p2 in individual.personas) {
- if (p2 != persona_to_unlink && persona_can_link_to (p2, main_persona_additions)) {
- main_persona = p2;
- if (main_persona.store.is_primary_store)
- break; // Exit if we find a primary persona, as we prefer these
- }
- }
-
- // We make a copy of the personas as the on in the individual may start
- // changing now
- var other_personas = new HashSet<Persona>();
- foreach (var p3 in individual.personas) {
- if (p3 != persona_to_unlink &&
- p3 != main_persona)
- other_personas.add (p3);
- }
-
- // If we didn't find a main persona, and we need one because there are
- // other personas that we need to ensure linking in, then create one
- if (main_persona == null && other_personas.size > 1) {
- var details = new HashTable<string, Value?> (str_hash, str_equal);
- try {
- main_persona = yield store.aggregator.primary_store.add_persona_from_details (details);
- yield (main_persona as NameDetails).change_full_name (individual.display_name);
- operation.added_persona (main_persona);
- } catch (GLib.Error e) {
- warning ("Unable to create new persona when unlinking: %s\n", e.message);
- return operation;
- }
- }
-
- persona_to_unlink.set_data ("contacts-new-contact", true);
-
- // First apply all additions on the primary persona so that we avoid temporarily being
- // unlinked and then relinked
- if (main_persona != null)
- yield persona_apply_attributes (main_persona, main_persona_additions, other_personas_removals,
operation);
- foreach (var p in other_personas) {
- yield persona_apply_attributes (p, null, other_personas_removals, operation);
- }
- // Last we do the removals on the persona_to_unlink
- yield persona_apply_attributes (persona_to_unlink, null, persona_to_unlink_removals, operation);
-
- persona_to_unlink.set_data ("contacts-new-contact", false);
-
- return operation;
- }
-
- public class LinkOperation2 : Object {
- private Store contacts_store;
-
- /* One Set<Persona> per individual linked, with the intention
- * of restore the old perosonas set on undo operation */
- LinkedList< HashSet<Persona> > old_personas_distribution;
-
- public LinkOperation2 (Store contacts_store) {
- this.contacts_store = contacts_store;
-
- old_personas_distribution = new LinkedList< HashSet<Persona> > ();
- }
-
- public void add_persona_set (Set<Persona> persona_set) {
- if (persona_set.size > 0) {
- var s = new HashSet<Persona> ();
- foreach (var p in persona_set) {
- s.add (p);
- }
- old_personas_distribution.add (s);
+ yield store.aggregator.unlink_individual (main);
+ } catch (Error e) {
+ debug ("Couldn't link personas");
}
+ yield link_personas(this.store, this.store.aggregator, personas_to_keep);
}
+ /* Undo the unlinking */
public async void undo () {
- Individual ind = null;
- if (old_personas_distribution.size > 0) {
- var ps = old_personas_distribution.first ();
- foreach (var p in ps) {
- ind = p.individual;
- break;
- }
- }
- if (ind != null) {
- try {
- yield this.contacts_store.aggregator.unlink_individual (ind);
- } catch (GLib.Error e1) {
- warning ("Error unlinking individual ā%sā: %s", ind.id, e1.message);
- }
- }
-
- foreach (var ps in old_personas_distribution) {
- try {
- yield this.contacts_store.aggregator.link_personas (ps);
- } catch (GLib.Error e1) {
- warning ("Error linking personas: %s", e1.message);
- }
- }
}
}
- public async LinkOperation2 link_contacts_list (LinkedList<Individual> contact_list, Store contacts_store)
{
- var operation = new LinkOperation2 (contacts_store);
-
- var all_personas = new HashSet<Persona> ();
- foreach (var i in contact_list) {
- var ps = i.personas;
- all_personas.add_all (ps);
- operation.add_persona_set (ps);
- }
-
+ /* Workaround: link_personas creates a new persona in the primary-store,
+ * For some reason we can't change the primary-store directly,
+ * but we can change the gsettings property.
+ * Before linking we set the primary-store to be "key-file"
+ * that the linking persona isn't written to a real store
+ */
+ private async void link_personas (Store store, IndividualAggregator aggregator, Set<Persona> personas) {
+ var settings = new GLib.Settings ("org.freedesktop.folks");
+ var default_store = settings.get_string ("primary-store");
+ settings.set_string ("primary-store", "key-file:relationships.ini");
try {
- yield contacts_store.aggregator.link_personas (all_personas);
- } catch (GLib.Error e1) {
- warning ("Error linking personas: %s", e1.message);
+ yield aggregator.link_personas (personas);
+ } catch (Error e) {
+ debug ("%s", e.message);
}
- return operation;
+ // Rest primary-store
+ settings.set_string ("primary-store", default_store);
}
}
diff --git a/src/contacts-store.vala b/src/contacts-store.vala
index 9334900..4f1568f 100644
--- a/src/contacts-store.vala
+++ b/src/contacts-store.vala
@@ -42,19 +42,6 @@ public class Contacts.Store : GLib.Object {
get { return this.aggregator.is_prepared; }
}
- private bool individual_can_replace_at_split (Individual new_individual) {
- foreach (var p in new_individual.personas) {
- if (p.get_data<bool> ("contacts-new-contact"))
- return false;
- }
- return true;
- }
-
- private bool individual_should_replace_at_join (Individual old_individual) {
- var c = old_individual;
- return c.get_data<bool> ("contacts-master-at-join");
- }
-
private void read_dont_suggest_db () {
dont_suggest_link.clear ();
try {
@@ -151,73 +138,25 @@ public class Contacts.Store : GLib.Object {
}
private void on_individuals_changed_detailed (MultiMap<Individual?,Individual?> changes) {
- // Note: Apparently the current implementation doesn't necessarily pick
- // up unlinked individual as replacements.
- var replaced_individuals = new HashMap<Individual?, Individual?> ();
- var old_individuals = changes.get_keys();
-
- debug ("Individuals changed: %d old, %d new", old_individuals.size - 1, changes[null].size);
-
- // Pick best replacements at joins
- foreach (var old_individual in old_individuals) {
- if (old_individual == null)
- continue;
- foreach (var new_individual in changes[old_individual]) {
- if (new_individual == null)
- continue;
- if (!replaced_individuals.has_key (new_individual)
- || individual_should_replace_at_join (old_individual)) {
- replaced_individuals[new_individual] = old_individual;
- }
+ var to_add = new HashSet<Individual> ();
+ var to_remove = new HashSet<Individual> ();
+ foreach (var i in changes.get_keys()) {
+ if (i != null)
+ to_remove.add (i);
+ foreach (var new_i in changes[i]) {
+ to_add.add (new_i);
}
}
- foreach (var old_individual in old_individuals) {
- HashSet<Individual>? replacements = null;
- foreach (var new_individual in changes[old_individual]) {
- if (old_individual != null && new_individual != null &&
- replaced_individuals[new_individual] == old_individual) {
- if (replacements == null)
- replacements = new HashSet<Individual> ();
- replacements.add (new_individual);
- } else if (old_individual != null) {
- // Removing an old individual.
- removed (old_individual);
- } else if (new_individual != null) {
- // Adding a new individual.
- added (new_individual);
- }
- }
-
- // This old_individual was split up into one or more new ones
- // We have to pick one to be the one that we keep around
- // in the old Contact, the rest gets new Contacts
- // This is important to get right, as we might be displaying
- // the contact and unlink a specific persona from the contact
- if (replacements != null) {
- Individual? main_individual = null;
- foreach (var i in replacements) {
- main_individual = i;
- // If this was marked as being possible to replace the
- // contact on split then we can otherwise bail immediately
- // Otherwise need to look for other possible better
- // replacements that we should reuse
- if (individual_can_replace_at_split (i))
- break;
- }
+ // Add new individuals
+ foreach (var i in to_add) {
+ if (i.personas.size > 0)
+ added (i);
+ }
- /* Not sure
- var c = Contact.from_individual (old_individual);
- c.replace_individual (main_individual);
- */
- foreach (var i in replacements) {
- if (i != main_individual) {
- // Already replaced this old_individual, i.e. we're splitting
- // old_individual. We just make this a new one.
- added (i);
- }
- }
- }
+ // Remove old individuals
+ foreach (var i in to_remove) {
+ removed (i);
}
}
diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala
index ccaec8d..f25c3a6 100644
--- a/src/contacts-utils.vala
+++ b/src/contacts-utils.vala
@@ -164,6 +164,18 @@ namespace Contacts.Utils {
return stores;
}
+ public PersonaStore? get_key_file_address_book (Store contacts_store) {
+ foreach (var backend in contacts_store.backend_store.enabled_backends.values) {
+ foreach (var persona_store in backend.persona_stores.values) {
+ if (persona_store.type_id == "key-file") {
+ return persona_store;
+ }
+ }
+ }
+ return null;
+ }
+
+
public void show_error_dialog (string error, Gtk.Window toplevel) {
var dialog = new Gtk.MessageDialog (toplevel,
Gtk.DialogFlags.MODAL,
diff --git a/src/contacts-window.vala b/src/contacts-window.vala
index 8c156a5..0ea8f1a 100644
--- a/src/contacts-window.vala
+++ b/src/contacts-window.vala
@@ -419,18 +419,16 @@ public class Contacts.Window : Gtk.ApplicationWindow {
set_shown_contact (null);
this.state = UiState.NORMAL;
- LinkOperation2 operation = null;
- link_contacts_list.begin (contact_list, this.store, (obj, result) => {
- operation = link_contacts_list.end (result);
- });
+ var operation = new LinkOperation (this.store);
+ operation.do.begin (contact_list);
string msg = ngettext ("%d contacts linked",
"%d contacts linked",
contact_list.size).printf (contact_list.size);
var b = new Button.with_mnemonic (_("_Undo"));
+ var notification = new InAppNotification (msg, b);
- var notification = new InAppNotification (msg);
/* signal handlers */
b.clicked.connect ( () => {
/* here, we will unlink the thing in question */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]