=?utf-8?q?=5Bfolks=5D_Bug_633781_=E2=80=94_Allow_to_set_avatar_on_individ?= =?utf-8?q?uals?=



commit 0737c2d78c4041bb211bd005d28a6960babddb70
Author: Philip Withnall <philip tecnocode co uk>
Date:   Fri Aug 12 12:18:16 2011 +0200

    Bug 633781 â Allow to set avatar on individuals
    
    Make Individual.avatar writeable and write new avatars back to the personas
    from writeable persona stores in an individual.
    
    Closes: bgo#633781

 NEWS                      |    5 ++
 folks/individual.vala     |   64 +++++++++++++++++++
 tests/eds/set-avatar.vala |  154 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 222 insertions(+), 1 deletions(-)
---
diff --git a/NEWS b/NEWS
index 881613b..21af72f 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,11 @@ Bugs fixed:
 * Bug 658874 â Typo?
 * Bug 658730 â ngettext (Plural forms) needed for
   "Imported %u buddies from '%s'."
+* Bug 633781 â Allow to set avatar on individuals
+
+API changes:
+* Individual.avatar is now settable using Individual.change_avatar() (not new
+  API, but it no longer always returns an error)
 
 Overview of changes from libfolks 0.6.2 to libfolks 0.6.2.1
 ===========================================================
diff --git a/folks/individual.vala b/folks/individual.vala
index 59548ee..f9ae1bc 100644
--- a/folks/individual.vala
+++ b/folks/individual.vala
@@ -145,6 +145,70 @@ public class Folks.Individual : Object,
       set { this.change_avatar.begin (value); } /* not writeable */
     }
 
+  /*
+   * Change the individual's avatar.
+   *
+   * It's preferred to call this rather than setting { link Individual.avatar}
+   * directly, as this method gives error notification and will only return once
+   * the avatar has been written to the relevant backing stores (or the
+   * operation's failed).
+   *
+   * Setting this property is only guaranteed to succeed (and be written to
+   * the backing store) if
+   * { link IndividualAggregator.ensure_individual_property_writeable} has been
+   * called successfully on the individual for the property name `avatar`.
+   *
+   * @param avatar the new avatar (or `null` to unset the avatar)
+   * @throws PropertyError if setting the avatar failed
+   * @since UNRELEASED
+   */
+  public async void change_avatar (LoadableIcon? avatar) throws PropertyError
+    {
+      if ((this._avatar != null && this._avatar.equal (avatar)) ||
+          (this._avatar == null && avatar == null))
+        {
+          return;
+        }
+
+      debug ("Setting avatar of individual '%s' to '%p'â", this.id, avatar);
+
+      PropertyError? persona_error = null;
+      var avatar_changed = false;
+
+      /* Try to write it to only the writeable Personas which have the
+       * "avatar" property as writeable. */
+      foreach (var p in this._persona_set)
+        {
+          var a = p as AvatarDetails;
+          if (a != null && p.store.is_writeable == true &&
+              "avatar" in p.writeable_properties)
+            {
+              try
+                {
+                  yield a.change_avatar (avatar);
+                  debug ("    written to writeable persona '%s'", p.uid);
+                  avatar_changed = true;
+                }
+              catch (PropertyError e)
+                {
+                  /* Store the first error so we can throw it if setting the
+                   * avatar fails on every other persona. */
+                  if (persona_error == null)
+                    {
+                      persona_error = e;
+                    }
+                }
+            }
+        }
+
+      /* Failure? */
+      if (avatar_changed == false)
+        {
+          assert (persona_error != null);
+          throw persona_error;
+        }
+    }
+
   /**
    * { inheritDoc}
    */
diff --git a/tests/eds/set-avatar.vala b/tests/eds/set-avatar.vala
index 55559e3..90e8b20 100644
--- a/tests/eds/set-avatar.vala
+++ b/tests/eds/set-avatar.vala
@@ -38,6 +38,8 @@ public class SetAvatarTests : Folks.TestCase
       this._eds_backend = new EdsTest.Backend ();
 
       this.add_test ("setting avatar on e-d-s persona", this.test_set_avatar);
+      this.add_test ("setting avatar on e-d-s individual",
+          this.test_set_individual_avatar);
     }
 
   public override void set_up ()
@@ -70,7 +72,7 @@ public class SetAvatarTests : Folks.TestCase
 
       this._test_set_avatar_async ();
 
-      Timeout.add_seconds (5, () => {
+      var timeout_id = Timeout.add_seconds (5, () => {
             this._main_loop.quit ();
             assert_not_reached ();
           });
@@ -79,6 +81,10 @@ public class SetAvatarTests : Folks.TestCase
 
       assert (this._found_before_update);
       assert (this._found_after_update);
+
+      Source.remove (timeout_id);
+      this._aggregator = null;
+      this._main_loop = null;
     }
 
   private async void _test_set_avatar_async ()
@@ -150,6 +156,152 @@ public class SetAvatarTests : Folks.TestCase
             });
         }
     }
+
+  void test_set_individual_avatar ()
+    {
+      var c1 = new Gee.HashMap<string, Value?> ();
+      this._main_loop = new GLib.MainLoop (null, false);
+      var avatar_path = Environment.get_variable ("AVATAR_FILE_PATH");
+      this._avatar = new FileIcon (File.new_for_path (avatar_path));
+      Value? v;
+
+      this._found_before_update = false;
+      this._found_after_update = false;
+
+      this._eds_backend.reset ();
+
+      v = Value (typeof (string));
+      v.set_string ("John McClane");
+      c1.set ("full_name", (owned) v);
+      this._eds_backend.add_contact (c1);
+
+      this._test_set_individual_avatar_async ();
+
+      var timeout_id = Timeout.add_seconds (5, () =>
+        {
+          this._main_loop.quit ();
+          assert_not_reached ();
+        });
+
+      this._main_loop.run ();
+
+      assert (this._found_before_update);
+      assert (this._found_after_update);
+
+      Source.remove (timeout_id);
+      this._aggregator = null;
+      this._main_loop = null;
+    }
+
+  private async void _test_set_individual_avatar_async ()
+    {
+      yield this._eds_backend.commit_contacts_to_addressbook ();
+
+      var store = BackendStore.dup ();
+      yield store.prepare ();
+
+      this._aggregator = new IndividualAggregator ();
+      this._aggregator.individuals_changed_detailed.connect ((changes) =>
+        {
+          var added = changes.get_values ();
+          var removed = changes.get_keys ();
+
+          foreach (Individual i in added)
+            {
+              assert (i != null);
+
+              var name = (Folks.NameDetails) i;
+
+              if (name.full_name == "John McClane")
+                {
+                  i.notify["avatar"].connect (
+                      this._notify_individual_avatar_cb);
+                  this._found_before_update = true;
+
+                  /* Just set the avatar on the individual */
+                  i.change_avatar.begin (this._avatar, (obj, res) =>
+                    {
+                      try
+                        {
+                          i.change_avatar.end (res);
+
+                          assert (this._found_before_update == true);
+                          assert (this._found_after_update == true);
+
+                          this._check_individual_has_avatar.begin (i,
+                              (obj, res) =>
+                            {
+                              assert (
+                                  this._check_individual_has_avatar.end (res)
+                                      == true);
+
+                              this._main_loop.quit ();
+                            });
+                        }
+                      catch (PropertyError e)
+                        {
+                          critical ("Unexpected error changing avatar: %s",
+                              e.message);
+                        }
+                    });
+                }
+            }
+
+          assert (removed.size == 1);
+
+          foreach (var i in removed)
+            {
+              assert (i == null);
+            }
+        });
+
+      try
+        {
+          yield this._aggregator.prepare ();
+        }
+      catch (GLib.Error e)
+        {
+          GLib.warning ("Error when calling prepare: %s\n", e.message);
+        }
+    }
+
+  private void _notify_individual_avatar_cb (Object individual_obj,
+      ParamSpec ps)
+    {
+      /* Note: we can't check whether the avatar's correct here, as that's an
+       * async operation, and if we start that operation in this signal
+       * callback it'll probably end up finishing after the rest of the code in
+       * _test_set_individual_avatar_async() has already failed. */
+      this._found_after_update = true;
+    }
+
+  private async bool _check_individual_has_avatar (Individual i)
+    {
+      var name = (Folks.NameDetails) i;
+
+      if (name.full_name == "John McClane")
+        {
+          var individual_equal = yield TestUtils.loadable_icons_content_equal (
+              i.avatar, this._avatar, -1);
+
+          if (individual_equal == true)
+            {
+              foreach (var p in i.personas)
+                {
+                  var persona_equal =
+                      yield TestUtils.loadable_icons_content_equal (
+                          (p as AvatarDetails).avatar, this._avatar, -1);
+
+                  if (p.store.type_id == "eds" && persona_equal == true)
+                    {
+                      return true;
+                    }
+                }
+            }
+        }
+
+      return false;
+    }
 }
 
 public int main (string[] args)



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