!6 Update to 43.0

From: @dwl301 
Reviewed-by: @zhang__3125 
Signed-off-by: @zhang__3125
This commit is contained in:
openeuler-ci-bot 2023-02-17 04:42:39 +00:00 committed by Gitee
commit 2a550d4cbe
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 1029 additions and 11 deletions

84
213.patch Normal file
View File

@ -0,0 +1,84 @@
From 0e4907177d80097b29d74b1a8af3aeed36074196 Mon Sep 17 00:00:00 2001
From: Niels De Graef <nielsdegraef@gmail.com>
Date: Thu, 29 Sep 2022 18:41:21 +0200
Subject: [PATCH] pane: Select the new individual after saving changes
Fixes: https://gitlab.gnome.org/GNOME/gnome-contacts/-/issues/271
---
src/contacts-contact-pane.vala | 16 ++++++++++++++--
src/core/contacts-contact.vala | 14 +++++++++++++-
2 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/src/contacts-contact-pane.vala b/src/contacts-contact-pane.vala
index ffdcbf33..ee9ee6c3 100644
--- a/src/contacts-contact-pane.vala
+++ b/src/contacts-contact-pane.vala
@@ -176,12 +176,24 @@ public class Contacts.ContactPane : Adw.Bin {
}
try {
- yield contact.apply_changes (this.store.aggregator.primary_store);
+ // The new individual. Even when editing an exisiting contact, it might
+ // be a different Individual than before, so make sure to adjust our
+ // selected contact afterwards
+ unowned var individual =
+ yield contact.apply_changes (this.store.aggregator.primary_store);
+ debug ("Applied changes resulted in individual (%s)",
+ (individual != null)? individual.id : "null");
+
+ if (individual != null) {
+ var pos = yield this.store.find_individual_for_id (individual.id);
+ if (pos != Gtk.INVALID_LIST_POSITION)
+ this.store.selection.selected = pos;
+ }
} catch (Error err) {
warning ("Couldn't save changes: %s", err.message);
+ show_contact (null);
// XXX do something better here
}
- show_contact_sheet (contact);
}
public void edit_contact () {
diff --git a/src/core/contacts-contact.vala b/src/core/contacts-contact.vala
index 866ec187..5fcc7425 100644
--- a/src/core/contacts-contact.vala
+++ b/src/core/contacts-contact.vala
@@ -258,14 +258,21 @@ public class Contacts.Contact : GLib.Object, GLib.ListModel {
* Applies any pending changes to all chunks. This can mean either a new
* persona is made, or it is saved in the chunk's referenced persona.
* When a new persona is made, it will be added to @store.
+ *
+ * Returns the Individual that was created from applying the changes
*/
- public async void apply_changes (PersonaStore store) throws GLib.Error {
+ public async unowned Individual? apply_changes (PersonaStore store) throws GLib.Error {
+ unowned Individual? individual = null;
+
// For those that were a persona: save the properties using the API
for (uint i = 0; i < this.chunks.length; i++) {
unowned var chunk = this.chunks[i];
if (chunk.persona == null)
continue;
+ if (individual == null)
+ individual = chunk.persona.individual;
+
if (!(chunk.property_name in chunk.persona.writeable_properties)) {
warning ("Can't save to unwriteable property '%s' to persona %s",
chunk.property_name, chunk.persona.uid);
@@ -303,6 +310,11 @@ public class Contacts.Contact : GLib.Object, GLib.ListModel {
var persona = yield store.add_persona_from_details (new_details);
debug ("Successfully created new persona %p", persona);
// FIXME: should we set the persona for these chunks?
+
+ if (individual == null && persona != null)
+ individual = persona.individual;
}
+
+ return individual;
}
}
--
GitLab

883
214.patch Normal file
View File

@ -0,0 +1,883 @@
From 380136c4e39f1f6d5ce5894b2b9f3506c3a9d1a6 Mon Sep 17 00:00:00 2001
From: Niels De Graef <nielsdegraef@gmail.com>
Date: Sun, 9 Oct 2022 10:07:04 +0200
Subject: [PATCH 1/2] Keep track if a chunk has changed
Introduce a property `dirty` which signifies if a `Contacts.Chunk`
chagned compared to its original value. That way, we can make sure we
don't try to save a property that didn't change, saving us some
necessary work. Although normally folks does a similar check, it's still
good to prevent going into folks (or anything similar) in the first
placE.
As a side effect, it solves a problem we currently had with the
`NicknameChunk`: when calling `save_to_persona()`, it erroneously
represented an empty value as both `""` and `null`. Since those are
different, it would try to save them and the E-D-S would time out in
that case (and throw an appropriate error). As a consequence, when
editing a contact, Contacts would always get blocked on the "nickname"
property.
Fixes: https://gitlab.gnome.org/GNOME/gnome-contacts/-/issues/271
---
meson.build | 2 +-
src/core/contacts-addresses-chunk.vala | 36 +++++-
src/core/contacts-alias-chunk.vala | 13 ++-
src/core/contacts-avatar-chunk.vala | 10 +-
src/core/contacts-bin-chunk.vala | 110 ++++++++++++++++++-
src/core/contacts-birthday-chunk.vala | 26 ++++-
src/core/contacts-chunk.vala | 7 +-
src/core/contacts-contact.vala | 6 +
src/core/contacts-email-addresses-chunk.vala | 17 ++-
src/core/contacts-full-name-chunk.vala | 13 ++-
src/core/contacts-im-addresses-chunk.vala | 25 ++++-
src/core/contacts-nickname-chunk.vala | 13 ++-
src/core/contacts-notes-chunk.vala | 14 ++-
src/core/contacts-phones-chunk.vala | 18 ++-
src/core/contacts-roles-chunk.vala | 21 +++-
src/core/contacts-structured-name-chunk.vala | 10 +-
src/core/contacts-urls-chunk.vala | 14 ++-
17 files changed, 331 insertions(+), 24 deletions(-)
diff --git a/meson.build b/meson.build
index 8fba912f..07294c75 100644
--- a/meson.build
+++ b/meson.build
@@ -49,7 +49,7 @@ glib = dependency('glib-2.0', version: '>=' + min_glib_version)
gmodule_export = dependency('gmodule-export-2.0', version: '>=' + min_glib_version)
# gnome_desktop = dependency('gnome-desktop-3.0')
goa = dependency('goa-1.0')
-gtk4_dep = dependency('gtk4', version: '>= 4.6')
+gtk4_dep = dependency('gtk4', version: '>= 4.4')
libadwaita_dep = dependency('libadwaita-1', version: '>= 1.2.alpha')
# E-D-S
libebook = dependency('libebook-1.2', version: '>=' + min_eds_version)
diff --git a/src/core/contacts-addresses-chunk.vala b/src/core/contacts-addresses-chunk.vala
index c322e916..bcdf5087 100644
--- a/src/core/contacts-addresses-chunk.vala
+++ b/src/core/contacts-addresses-chunk.vala
@@ -36,7 +36,7 @@ public class Contacts.AddressesChunk : BinChunk {
}
}
- emptiness_check ();
+ finish_initialization ();
}
protected override BinChunkChild create_empty_child () {
@@ -85,6 +85,26 @@ public class Contacts.Address : BinChunkChild {
this.parameters = address_field.parameters;
}
+ protected override int compare_internal (BinChunkChild other)
+ requires (other is Address) {
+ var this_types = this.parameters["type"];
+ var other_types = other.parameters["type"];
+
+ // Put home address first.
+ // FIXME: we should be minding case sensitivity here
+ if (("HOME" in this_types) != ("HOME" in other_types))
+ return ("HOME" in this_types)? -1 : 1;
+
+ // If no specific preference by type, compare by string
+ unowned var other_address = (Address) other;
+ var nr_cmp = strcmp (to_string (""), other_address.to_string (""));
+ if (nr_cmp != 0)
+ return nr_cmp;
+
+ // Fall back to an even dumber comparison
+ return dummy_compare_parameters (other);
+ }
+
/**
* Returns the TypeDescriptor that describes the type of this address
* (for example home, work, ...)
@@ -135,4 +155,18 @@ public class Contacts.Address : BinChunkChild {
return new PostalAddressFieldDetails (this.address, this.parameters);
}
+
+ public override BinChunkChild copy () {
+ var address = new Address ();
+ address.address.address_format = this.address.address_format;
+ address.address.country = this.address.country;
+ address.address.extension = this.address.extension;
+ address.address.locality = this.address.locality;
+ address.address.po_box = this.address.po_box;
+ address.address.postal_code = this.address.postal_code;
+ address.address.region = this.address.region;
+ address.address.street = this.address.street;
+ copy_parameters (address);
+ return address;
+ }
}
diff --git a/src/core/contacts-alias-chunk.vala b/src/core/contacts-alias-chunk.vala
index 921e9cf2..e2d0f209 100644
--- a/src/core/contacts-alias-chunk.vala
+++ b/src/core/contacts-alias-chunk.vala
@@ -19,6 +19,8 @@ using Folks;
public class Contacts.AliasChunk : Chunk {
+ private string original_alias = "";
+
public string alias {
get { return this._alias; }
set {
@@ -26,10 +28,13 @@ public class Contacts.AliasChunk : Chunk {
return;
bool was_empty = this.is_empty;
+ bool was_dirty = this.dirty;
this._alias = value;
notify_property ("alias");
if (this.is_empty != was_empty)
notify_property ("is-empty");
+ if (was_dirty != this.dirty)
+ notify_property ("dirty");
}
}
private string _alias = "";
@@ -38,11 +43,17 @@ public class Contacts.AliasChunk : Chunk {
public override bool is_empty { get { return this._alias.strip () == ""; } }
+ public override bool dirty {
+ get { return this.alias.strip () == this.original_alias.strip (); }
+ }
+
construct {
if (persona != null) {
return_if_fail (persona is AliasDetails);
- persona.bind_property ("alias", this, "alias", BindingFlags.SYNC_CREATE);
+ persona.bind_property ("alias", this, "alias");
+ this._alias = ((AliasDetails) persona).alias;
}
+ this.original_alias = this.alias;
}
public override Value? to_value () {
diff --git a/src/core/contacts-avatar-chunk.vala b/src/core/contacts-avatar-chunk.vala
index 56dbce27..850c6cf6 100644
--- a/src/core/contacts-avatar-chunk.vala
+++ b/src/core/contacts-avatar-chunk.vala
@@ -19,6 +19,8 @@ using Folks;
public class Contacts.AvatarChunk : Chunk {
+ private LoadableIcon? original_avatar = null;
+
public LoadableIcon? avatar {
get { return this._avatar; }
set {
@@ -35,11 +37,17 @@ public class Contacts.AvatarChunk : Chunk {
public override bool is_empty { get { return this._avatar == null; } }
+ public override bool dirty {
+ get { return this.avatar != this.original_avatar; }
+ }
+
construct {
if (persona != null) {
return_if_fail (persona is AvatarDetails);
- persona.bind_property ("avatar", this, "avatar", BindingFlags.SYNC_CREATE);
+ persona.bind_property ("avatar", this, "avatar");
+ this._avatar = ((AvatarDetails) persona).avatar;
}
+ this.original_avatar = this.avatar;
}
public override Value? to_value () {
diff --git a/src/core/contacts-bin-chunk.vala b/src/core/contacts-bin-chunk.vala
index 3ce05e58..eac00608 100644
--- a/src/core/contacts-bin-chunk.vala
+++ b/src/core/contacts-bin-chunk.vala
@@ -29,6 +29,9 @@ using Folks;
*/
public abstract class Contacts.BinChunk : Chunk, GLib.ListModel {
+ private BinChunkChild[] original_elements;
+ private bool original_elements_set = false;
+
private GenericArray<BinChunkChild> elements = new GenericArray<BinChunkChild> ();
public override bool is_empty {
@@ -43,6 +46,30 @@ public abstract class Contacts.BinChunk : Chunk, GLib.ListModel {
}
}
+ public override bool dirty {
+ get {
+ // If we're hitting this, a subclass forgot to set the field
+ return_if_fail (this.original_elements_set);
+
+ var non_empty_count = nr_nonempty_children ();
+ if (this.original_elements.length != non_empty_count)
+ return true;
+
+ // Since we guarantee ordering by BinChunkChild::compare,
+ // we can just check for equality by paired indices (ignoring the empty
+ // ones though)
+ for (uint i = 0, j = 0; i < this.elements.length; i++, j++) {
+ if (this.elements[i].is_empty) {
+ j--;
+ continue;
+ }
+ if (this.elements[i].compare (this.original_elements[j]) != 0)
+ return true;
+ }
+ return false;
+ }
+ }
+
/**
* Should be called by subclasses when they add a child.
*
@@ -94,6 +121,15 @@ public abstract class Contacts.BinChunk : Chunk, GLib.ListModel {
return false;
}
+ private uint nr_nonempty_children () {
+ uint result = 0;
+ for (uint i = 0; i < this.elements.length; i++) {
+ if (!this.elements[i].is_empty)
+ result++;
+ }
+ return result;
+ }
+
public override Value? to_value () {
var afds = new Gee.HashSet<AbstractFieldDetails> ();
for (uint i = 0; i < this.elements.length; i++) {
@@ -117,6 +153,21 @@ public abstract class Contacts.BinChunk : Chunk, GLib.ListModel {
return afds;
}
+ /**
+ * A helper finish the initialization of a BinChunk. It makes sure to set the
+ * "original_elements" field (which is used to calculate the "dirty"
+ * property) as well as doing an initial emptiness check
+ */
+ protected void finish_initialization () {
+ // Make a deep copy to ensure changes don't propagate to original_elements
+ this.original_elements = this.elements.copy ((child) => {
+ return child.copy ();
+ }).steal ();
+ this.original_elements_set = true;
+
+ emptiness_check ();
+ }
+
// ListModel implementation
public uint n_items { get { return this.elements.length; } }
@@ -163,6 +214,19 @@ public abstract class Contacts.BinChunkChild : GLib.Object {
*/
public abstract AbstractFieldDetails? create_afd ();
+ /**
+ * Creates a deep copy of this child
+ */
+ public abstract BinChunkChild copy ();
+
+ // Helper to copy this object's parameters field into that of @copy
+ protected void copy_parameters (BinChunkChild copy) {
+ copy.parameters.clear ();
+ var iter = this.parameters.map_iterator ();
+ while (iter.next ())
+ copy.parameters[iter.get_key ()] = iter.get_value ();
+ }
+
// A helper to change a string field with the proper propery notifies
protected void change_string_prop (string prop_name,
ref string old_value,
@@ -180,8 +244,8 @@ public abstract class Contacts.BinChunkChild : GLib.Object {
}
/**
- * Compares 2 children in an intuitive manner, so that preferred children go
- * first and empty children are last
+ * Compares 2 children in such a way that unequal children are sorted in an
+ * intuitive manner
*/
public int compare (BinChunkChild other) {
// Fields with a PREF hint always go first (see vCard PREF attribute)
@@ -195,7 +259,7 @@ public abstract class Contacts.BinChunkChild : GLib.Object {
return empty? 1 : -1;
// FIXME: maybe also compare the types? (e.g. put HOME before WORK)
- return 0;
+ return compare_internal (other);
}
/**
@@ -213,4 +277,44 @@ public abstract class Contacts.BinChunkChild : GLib.Object {
}
return false;
}
+
+ /**
+ * Should be implemented by subclasses to compare with logic specific to that
+ * property. Note that we ideally try to go for a stable sort
+ */
+ protected abstract int compare_internal (BinChunkChild other);
+
+ // Helper to do a very dumb ordering with this function
+ protected int dummy_compare_parameters (BinChunkChild other) {
+ // TYPE is a special vcard param, so use that
+ var this_types = this.parameters["type"].to_array ();
+ var other_types = other.parameters["type"].to_array ();
+
+ // If one type is more specific than the other, use that
+ if (this_types.length != other_types.length)
+ return other_types.length - this_types.length;
+
+ for (uint i = 0; i < this_types.length; i++) {
+ var type_cmp = strcmp (this_types[i], other_types[i]);
+ if (type_cmp != 0)
+ return type_cmp;
+ }
+
+ // If the number of parameters is larger, assume it's more specific
+ // so put it up front
+ if (this.parameters.size != other.parameters.size)
+ return other.parameters.size - this.parameters.size;
+
+ // Go over all parameters and check for any difference in size
+ var keys = this.parameters.get_keys ();
+ foreach (string key in keys) {
+ var this_params = this.parameters[key];
+ var other_params = other.parameters[key];
+
+ if (this_params.size != other_params.size)
+ return other_params.size - this_params.size;
+ }
+
+ return 0;
+ }
}
diff --git a/src/core/contacts-birthday-chunk.vala b/src/core/contacts-birthday-chunk.vala
index 087da6a6..d929dc5f 100644
--- a/src/core/contacts-birthday-chunk.vala
+++ b/src/core/contacts-birthday-chunk.vala
@@ -23,19 +23,25 @@ using Folks;
*/
public class Contacts.BirthdayChunk : Chunk {
+ private DateTime? original_birthday = null;
+
public DateTime? birthday {
get { return this._birthday; }
set {
- if (this._birthday == null && value == null)
+ if (this.birthday == null && value == null)
return;
- if (this._birthday != null && value != null
- && this._birthday.equal (value.to_utc ()))
+ if (this.birthday != null && value != null && this.birthday.equal (value))
return;
+ bool was_empty = this.is_empty;
+ bool was_dirty = this.dirty;
this._birthday = (value != null)? value.to_utc () : null;
notify_property ("birthday");
- notify_property ("is-empty");
+ if (was_empty != this.is_empty)
+ notify_property ("is-empty");
+ if (was_dirty != this.dirty)
+ notify_property ("dirty");
}
}
private DateTime? _birthday = null;
@@ -44,11 +50,21 @@ public class Contacts.BirthdayChunk : Chunk {
public override bool is_empty { get { return this.birthday == null; } }
+ public override bool dirty {
+ get {
+ if (this.birthday != null && this.original_birthday != null)
+ return !this.birthday.equal (this.original_birthday);
+ return this.birthday != this.original_birthday;
+ }
+ }
+
construct {
if (persona != null) {
return_if_fail (persona is BirthdayDetails);
- persona.bind_property ("birthday", this, "birthday", BindingFlags.SYNC_CREATE);
+ persona.bind_property ("birthday", this, "birthday");
+ this._birthday = ((BirthdayDetails) persona).birthday;
}
+ this.original_birthday = this.birthday;
}
public override Value? to_value () {
diff --git a/src/core/contacts-chunk.vala b/src/core/contacts-chunk.vala
index 998ad690..328a0424 100644
--- a/src/core/contacts-chunk.vala
+++ b/src/core/contacts-chunk.vala
@@ -42,10 +42,11 @@ public abstract class Contacts.Chunk : GLib.Object {
public abstract bool is_empty { get; }
/**
- * A separate field to keep track of whether something has changed.
- * If it did, we know we'll have to (possibly) save the changes.
+ * A separate field to keep track of whether this has changed from its
+ * original value. If it did, we know we'll have to (possibly) save the
+ * changes.
*/
- public bool changed { get; protected set; default = false; }
+ public abstract bool dirty { get; }
/**
* Converts this chunk into a GLib.Value, as expected by API like
diff --git a/src/core/contacts-contact.vala b/src/core/contacts-contact.vala
index 5fcc7425..761f447b 100644
--- a/src/core/contacts-contact.vala
+++ b/src/core/contacts-contact.vala
@@ -273,6 +273,12 @@ public class Contacts.Contact : GLib.Object, GLib.ListModel {
if (individual == null)
individual = chunk.persona.individual;
+ if (!chunk.dirty) {
+ debug ("Not saving unchanged property '%s' to persona %s",
+ chunk.property_name, chunk.persona.uid);
+ continue;
+ }
+
if (!(chunk.property_name in chunk.persona.writeable_properties)) {
warning ("Can't save to unwriteable property '%s' to persona %s",
chunk.property_name, chunk.persona.uid);
diff --git a/src/core/contacts-email-addresses-chunk.vala b/src/core/contacts-email-addresses-chunk.vala
index 1119a2cb..36f57156 100644
--- a/src/core/contacts-email-addresses-chunk.vala
+++ b/src/core/contacts-email-addresses-chunk.vala
@@ -32,7 +32,7 @@ public class Contacts.EmailAddressesChunk : BinChunk {
}
}
- emptiness_check ();
+ finish_initialization ();
}
protected override BinChunkChild create_empty_child () {
@@ -72,6 +72,15 @@ public class Contacts.EmailAddress : BinChunkChild {
this.parameters = email_field.parameters;
}
+ protected override int compare_internal (BinChunkChild other)
+ requires (other is EmailAddress) {
+ unowned var other_email_addr = (EmailAddress) other;
+ var addr_cmp = strcmp (this.raw_address, other_email_addr.raw_address);
+ if (addr_cmp != 0)
+ return addr_cmp;
+ return dummy_compare_parameters (other);
+ }
+
/**
* Returns the TypeDescriptor that describes the type of the email address
* (for example personal, work, ...)
@@ -86,6 +95,12 @@ public class Contacts.EmailAddress : BinChunkChild {
return new EmailFieldDetails (this.raw_address, this.parameters);
}
+ public override BinChunkChild copy () {
+ var email_address = new EmailAddress ();
+ email_address.raw_address = this.raw_address;
+ copy_parameters (email_address);
+ return email_address;
+ }
public string get_mailto_uri () {
return "mailto:" + Uri.escape_string (this.raw_address, "@" , false);
diff --git a/src/core/contacts-full-name-chunk.vala b/src/core/contacts-full-name-chunk.vala
index 647f5561..e59fb382 100644
--- a/src/core/contacts-full-name-chunk.vala
+++ b/src/core/contacts-full-name-chunk.vala
@@ -24,6 +24,8 @@ using Folks;
*/
public class Contacts.FullNameChunk : Chunk {
+ private string original_full_name = "";
+
public string full_name {
get { return this._full_name; }
set {
@@ -31,10 +33,13 @@ public class Contacts.FullNameChunk : Chunk {
return;
bool was_empty = this.is_empty;
+ bool was_dirty = this.dirty;
this._full_name = value;
notify_property ("full-name");
if (this.is_empty != was_empty)
notify_property ("is-empty");
+ if (was_dirty != this.dirty)
+ notify_property ("dirty");
}
}
private string _full_name = "";
@@ -43,11 +48,17 @@ public class Contacts.FullNameChunk : Chunk {
public override bool is_empty { get { return this._full_name.strip () == ""; } }
+ public override bool dirty {
+ get { return this.full_name.strip () != this.original_full_name.strip (); }
+ }
+
construct {
if (persona != null) {
return_if_fail (persona is NameDetails);
- persona.bind_property ("full-name", this, "full-name", BindingFlags.SYNC_CREATE);
+ persona.bind_property ("full-name", this, "full-name");
+ this._full_name = ((NameDetails) persona).full_name;
}
+ this.original_full_name = this.full_name;
}
public override Value? to_value () {
diff --git a/src/core/contacts-im-addresses-chunk.vala b/src/core/contacts-im-addresses-chunk.vala
index 031f8045..95cdd3ad 100644
--- a/src/core/contacts-im-addresses-chunk.vala
+++ b/src/core/contacts-im-addresses-chunk.vala
@@ -39,7 +39,7 @@ public class Contacts.ImAddressesChunk : BinChunk {
}
}
- emptiness_check ();
+ finish_initialization ();
}
protected override BinChunkChild create_empty_child () {
@@ -90,10 +90,33 @@ public class Contacts.ImAddress : BinChunkChild {
this.parameters = im_field.parameters;
}
+ protected override int compare_internal (BinChunkChild other)
+ requires (other is ImAddress) {
+ unowned var other_im_addr = (ImAddress) other;
+
+ var protocol_cmp = strcmp (this.protocol, other_im_addr.protocol);
+ if (protocol_cmp != 0)
+ return protocol_cmp;
+
+ var addr_cmp = strcmp (this.address, other_im_addr.address);
+ if (addr_cmp != 0)
+ return addr_cmp;
+
+ return dummy_compare_parameters (other);
+ }
+
public override AbstractFieldDetails? create_afd () {
if (this.is_empty)
return null;
return new ImFieldDetails (this.address, this.parameters);
}
+
+ public override BinChunkChild copy () {
+ var ima = new ImAddress ();
+ ima.protocol = this.protocol;
+ ima.address = this.address;
+ copy_parameters (ima);
+ return ima;
+ }
}
diff --git a/src/core/contacts-nickname-chunk.vala b/src/core/contacts-nickname-chunk.vala
index ba505f08..81cf1d98 100644
--- a/src/core/contacts-nickname-chunk.vala
+++ b/src/core/contacts-nickname-chunk.vala
@@ -22,6 +22,8 @@ using Folks;
*/
public class Contacts.NicknameChunk : Chunk {
+ private string original_nickname = "";
+
public string nickname {
get { return this._nickname; }
set {
@@ -29,10 +31,13 @@ public class Contacts.NicknameChunk : Chunk {
return;
bool was_empty = this.is_empty;
+ bool was_dirty = this.dirty;
this._nickname = value;
notify_property ("nickname");
if (this.is_empty != was_empty)
notify_property ("is-empty");
+ if (was_dirty != this.dirty)
+ notify_property ("dirty");
}
}
private string _nickname = "";
@@ -41,11 +46,17 @@ public class Contacts.NicknameChunk : Chunk {
public override bool is_empty { get { return this._nickname.strip () == ""; } }
+ public override bool dirty {
+ get { return this.nickname.strip () != this.original_nickname.strip (); }
+ }
+
construct {
if (persona != null) {
return_if_fail (persona is NameDetails);
- persona.bind_property ("nickname", this, "nickname", BindingFlags.SYNC_CREATE);
+ persona.bind_property ("nickname", this, "nickname");
+ this._nickname = ((NameDetails) persona).nickname;
}
+ this.original_nickname = this.nickname;
}
public override Value? to_value () {
diff --git a/src/core/contacts-notes-chunk.vala b/src/core/contacts-notes-chunk.vala
index 45b5c43b..2f1ee3ae 100644
--- a/src/core/contacts-notes-chunk.vala
+++ b/src/core/contacts-notes-chunk.vala
@@ -36,7 +36,7 @@ public class Contacts.NotesChunk : BinChunk {
}
}
- emptiness_check ();
+ finish_initialization ();
}
protected override BinChunkChild create_empty_child () {
@@ -76,10 +76,22 @@ public class Contacts.Note : BinChunkChild {
this.parameters = note_field.parameters;
}
+ protected override int compare_internal (BinChunkChild other)
+ requires (other is Note) {
+ return strcmp (this.text, ((Note) other).text);
+ }
+
public override AbstractFieldDetails? create_afd () {
if (this.is_empty)
return null;
return new NoteFieldDetails (this.text, this.parameters);
}
+
+ public override BinChunkChild copy () {
+ var note = new Note ();
+ note.text = this.text;
+ copy_parameters (note);
+ return note;
+ }
}
diff --git a/src/core/contacts-phones-chunk.vala b/src/core/contacts-phones-chunk.vala
index 8135d98a..c8e0ce3a 100644
--- a/src/core/contacts-phones-chunk.vala
+++ b/src/core/contacts-phones-chunk.vala
@@ -36,7 +36,7 @@ public class Contacts.PhonesChunk : BinChunk {
}
}
- emptiness_check ();
+ finish_initialization ();
}
protected override BinChunkChild create_empty_child () {
@@ -80,6 +80,15 @@ public class Contacts.Phone : BinChunkChild {
this.parameters = phone_field.parameters;
}
+ protected override int compare_internal (BinChunkChild other)
+ requires (other is Phone) {
+ unowned var other_phone = (Phone) other;
+ var nr_cmp = strcmp (this.raw_number, other_phone.raw_number);
+ if (nr_cmp != 0)
+ return nr_cmp;
+ return dummy_compare_parameters (other);
+ }
+
/**
* Returns the TypeDescriptor that describes the type of phone number
* (for example mobile, work, fax, ...)
@@ -94,4 +103,11 @@ public class Contacts.Phone : BinChunkChild {
return new PhoneFieldDetails (this.raw_number, this.parameters);
}
+
+ public override BinChunkChild copy () {
+ var phone = new Phone ();
+ phone.raw_number = this.raw_number;
+ copy_parameters (phone);
+ return phone;
+ }
}
diff --git a/src/core/contacts-roles-chunk.vala b/src/core/contacts-roles-chunk.vala
index bec585b2..948c42b9 100644
--- a/src/core/contacts-roles-chunk.vala
+++ b/src/core/contacts-roles-chunk.vala
@@ -37,7 +37,7 @@ public class Contacts.RolesChunk : BinChunk {
}
}
- emptiness_check ();
+ finish_initialization ();
}
protected override BinChunkChild create_empty_child () {
@@ -72,6 +72,16 @@ public class Contacts.OrgRole : BinChunkChild {
this.parameters = role_field.parameters;
}
+ protected override int compare_internal (BinChunkChild other)
+ requires (other is OrgRole) {
+ unowned var other_orgrole = (OrgRole) other;
+ var orgs_cmp = strcmp (this.role.organisation_name,
+ other_orgrole.role.organisation_name);
+ if (orgs_cmp != 0)
+ return orgs_cmp;
+ return strcmp (this.role.title, other_orgrole.role.title);
+ }
+
public override AbstractFieldDetails? create_afd () {
if (this.is_empty)
return null;
@@ -79,6 +89,15 @@ public class Contacts.OrgRole : BinChunkChild {
return new RoleFieldDetails (this.role, this.parameters);
}
+ public override BinChunkChild copy () {
+ var org_role = new OrgRole ();
+ org_role.role.organisation_name = this.role.organisation_name;
+ org_role.role.role = this.role.role;
+ org_role.role.title = this.role.title;
+ copy_parameters (org_role);
+ return org_role;
+ }
+
public string to_string () {
if (this.role.title != "") {
if (this.role.organisation_name != "") {
diff --git a/src/core/contacts-structured-name-chunk.vala b/src/core/contacts-structured-name-chunk.vala
index 07cbc8f9..388aa9f1 100644
--- a/src/core/contacts-structured-name-chunk.vala
+++ b/src/core/contacts-structured-name-chunk.vala
@@ -25,6 +25,8 @@ using Folks;
*/
public class Contacts.StructuredNameChunk : Chunk {
+ private StructuredName original_structured_name;
+
public StructuredName structured_name {
get { return this._structured_name; }
set {
@@ -51,11 +53,17 @@ public class Contacts.StructuredNameChunk : Chunk {
}
}
+ public override bool dirty {
+ get { return !this.original_structured_name.equal (this._structured_name); }
+ }
+
construct {
if (persona != null) {
return_if_fail (persona is NameDetails);
- persona.bind_property ("structured-name", this, "structured-name", BindingFlags.SYNC_CREATE);
+ persona.bind_property ("structured-name", this, "structured-name");
+ this._structured_name = ((NameDetails) persona).structured_name;
}
+ this.original_structured_name = this.structured_name;
}
public override Value? to_value () {
diff --git a/src/core/contacts-urls-chunk.vala b/src/core/contacts-urls-chunk.vala
index 671fc4dd..62b02c0e 100644
--- a/src/core/contacts-urls-chunk.vala
+++ b/src/core/contacts-urls-chunk.vala
@@ -36,7 +36,7 @@ public class Contacts.UrlsChunk : BinChunk {
}
}
- emptiness_check ();
+ finish_initialization ();
}
protected override BinChunkChild create_empty_child () {
@@ -76,6 +76,11 @@ public class Contacts.Url : BinChunkChild {
this.parameters = url_field.parameters;
}
+ protected override int compare_internal (BinChunkChild other)
+ requires (other is Url) {
+ return strcmp (this.raw_url, ((Url) other).raw_url);
+ }
+
/**
* Tries to return an absolute URL (with a scheme).
* Since we know contact URL values are for web addresses, we try to fall
@@ -92,4 +97,11 @@ public class Contacts.Url : BinChunkChild {
return new UrlFieldDetails (this.raw_url, this.parameters);
}
+
+ public override BinChunkChild copy () {
+ var url = new Url ();
+ url.raw_url = this.raw_url;
+ copy_parameters (url);
+ return url;
+ }
}
--
GitLab
From a84eae026f1869c2de083db7f04472a84e017fa9 Mon Sep 17 00:00:00 2001
From: Niels De Graef <nielsdegraef@gmail.com>
Date: Mon, 10 Oct 2022 20:16:45 +0200
Subject: [PATCH 2/2] contact: Copy the chunks before applying changes
When applying the changes of certain fields, we've seen that this leads
to a `individuals_changed_detailed()` being called with the same
individual in the `removed` and `added` set. The signal callback
propagates to several layers, until it lands in the
`Contact:on_individual_personas_changed()` function. There, all chunks
related to the persona are removed, even when we still might be applying
changes of some of the other chunks.
The `apply_changes()` method in other words should keep its own copy to
prevent that.
Fixes: https://gitlab.gnome.org/GNOME/gnome-contacts/-/issues/271
---
src/core/contacts-contact.vala | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/core/contacts-contact.vala b/src/core/contacts-contact.vala
index 761f447b..742bab71 100644
--- a/src/core/contacts-contact.vala
+++ b/src/core/contacts-contact.vala
@@ -264,9 +264,12 @@ public class Contacts.Contact : GLib.Object, GLib.ListModel {
public async unowned Individual? apply_changes (PersonaStore store) throws GLib.Error {
unowned Individual? individual = null;
+ // Create a (shallow) copy of the chunks
+ var chunks = this.chunks.copy ((chunk) => { return chunk; });
+
// For those that were a persona: save the properties using the API
- for (uint i = 0; i < this.chunks.length; i++) {
- unowned var chunk = this.chunks[i];
+ for (uint i = 0; i < chunks.length; i++) {
+ unowned var chunk = chunks[i];
if (chunk.persona == null)
continue;
@@ -297,8 +300,8 @@ public class Contacts.Contact : GLib.Object, GLib.ListModel {
// Find those without a persona, and save them into the primary store
var new_details = new HashTable<string, Value?> (str_hash, str_equal);
- for (uint i = 0; i < this.chunks.length; i++) {
- unowned var chunk = this.chunks[i];
+ for (uint i = 0; i < chunks.length; i++) {
+ unowned var chunk = chunks[i];
if (chunk.persona != null)
continue;
--
GitLab

29
216.patch Normal file
View File

@ -0,0 +1,29 @@
From 5565cd0961aaa204599e0af57fd8806a65758c10 Mon Sep 17 00:00:00 2001
From: Niels De Graef <nielsdegraef@gmail.com>
Date: Fri, 14 Oct 2022 08:56:30 +0200
Subject: [PATCH] main-window: Hide the link button
There's definitely something going wrong with linking contacts, as it
forgets any link between app restarts. Since we really don't want to
expose something that's completely broken to our users, hide the button
that allows the linking. It's a shitty workaround for now.
---
data/ui/contacts-main-window.ui | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data/ui/contacts-main-window.ui b/data/ui/contacts-main-window.ui
index 3d01bdc7..fd8f0a17 100644
--- a/data/ui/contacts-main-window.ui
+++ b/data/ui/contacts-main-window.ui
@@ -167,7 +167,7 @@
</child>
<child>
<object class="GtkButton" id="link_button">
- <property name="focus_on_click">False</property>
+ <property name="visible">False</property>
<property name="label" translatable="yes" comments="Link refers to the verb, from linking contacts together">Link</property>
<property name="tooltip-text" translatable="yes">Link Selected Contacts Together</property>
<property name="action-name">win.link-marked-contacts</property>
--
GitLab

Binary file not shown.

BIN
gnome-contacts-43.0.tar.xz Normal file

Binary file not shown.

View File

@ -1,31 +1,47 @@
%global gtk4_version 4.6
Name: gnome-contacts
Version: 42.0
Version: 43.0
Release: 1
Summary: Integrated address book for GNOME
Summary: Contacts manager for GNOME
License: GPLv2+
URL: https://wiki.gnome.org/Apps/Contacts
Source0: https://download.gnome.org/sources/%{name}/42/%{name}-%{version}.tar.xz
Source0: https://download.gnome.org/sources/%{name}/43/%{name}-%{version}.tar.xz
BuildRequires: desktop-file-utils docbook-dtds docbook-style-xsl gettext meson vala libappstream-glib
BuildRequires: libxslt pkgconfig(folks) >= 0.11.4 pkgconfig(folks-eds)
BuildRequires: pkgconfig(folks-telepathy) pkgconfig(gee-0.8) pkgconfig(goa-1.0) pkgconfig(libadwaita-1)
BuildRequires: pkgconfig(gobject-introspection-1.0) pkgconfig(libportal)
Patch0: 213.patch
Patch1: 214.patch
Patch2: 216.patch
BuildRequires: desktop-file-utils
BuildRequires: docbook-dtds
BuildRequires: docbook-style-xsl
BuildRequires: gettext
BuildRequires: meson
BuildRequires: vala
BuildRequires: libappstream-glib
BuildRequires: libxslt
BuildRequires: pkgconfig(folks)
BuildRequires: pkgconfig(folks-eds)
BuildRequires: pkgconfig(gee-0.8)
BuildRequires: pkgconfig(goa-1.0)
BuildRequires: pkgconfig(gobject-introspection-1.0)
BuildRequires: pkgconfig(gtk4) >= %{gtk4_version}
BuildRequires: pkgconfig(libadwaita-1)
BuildRequires: pkgconfig(libportal-gtk4)
Requires: folks >= 1:0.11.4 gtk4%{?_isa} >= %{gtk4_version} hicolor-icon-theme
Requires: gtk4%{?_isa} >= %{gtk4_version}
Requires: hicolor-icon-theme
%description
Contacts is GNOME's integrated address book. It is written in Vala and uses libfolks
(also written in Vala) and Evolution Data Server.
%{name} is a standalone contacts manager for GNOME desktop.
%package_help
%prep
%autosetup -n %{name}-%{version}
%autosetup -p1 -n %{name}-%{version}
%build
export VALAFLAGS="-g"
%meson
%meson_build
@ -40,11 +56,14 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/org.gnome.Contacts.d
%files -f %{name}.lang
%license COPYING
%{_bindir}/gnome-contacts
%{_libexecdir}/gnome-contacts/
%{_libexecdir}/gnome-contacts-search-provider
%{_datadir}/applications/org.gnome.Contacts.desktop
%{_datadir}/dbus-1/services/org.gnome.Contacts.service
%{_datadir}/dbus-1/services/org.gnome.Contacts.SearchProvider.service
%{_datadir}/glib-2.0/schemas/org.gnome.Contacts.gschema.xml
%dir %{_datadir}/gnome-shell
%dir %{_datadir}/gnome-shell/search-providers
%{_datadir}/gnome-shell/search-providers/org.gnome.Contacts.search-provider.ini
%{_datadir}/icons/hicolor/*/apps/org.gnome.Contacts*.svg
%{_datadir}/metainfo/org.gnome.Contacts.appdata.xml
@ -54,6 +73,9 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/org.gnome.Contacts.d
%{_mandir}/man1/gnome-contacts.1*
%changelog
* Mon Jan 02 2023 lin zhang <lin.zhang@turbolinux.com.cn> - 43.0-1
- Update to 43.0
* Mon Mar 28 2022 lin zhang <lin.zhang@turbolinux.com.cn> - 42.0-1
- Update to 42.0