85
85
* in this Individual. There shouldn't be any entries with a number < 1.
86
86
* This is used for working out when to disconnect from store signals. */
87
87
private HashMap<PersonaStore, uint> stores;
88
/* The number of Personas in this Individual which have
89
* Persona.is_user == true. Iff this is > 0, Individual.is_user == true. */
90
private uint persona_user_count = 0;
91
private HashTable<string, GenericArray<string>> _im_addresses;
90
94
* The trust level of the Individual.
115
119
public string presence_message { get; private set; }
122
* Whether the Individual is the user.
124
* Iff the Individual represents the user – the person who owns the
125
* account in the backend for each {@link Persona} in the Individual –
128
* It is //not// guaranteed that every {@link Persona} in the Individual has
129
* its {@link Persona.is_user} set to the same value as the Individual. For
130
* example, the user could own two Telepathy accounts, and have added the
131
* other account as a contact in each account. The accounts will expose a
132
* {@link Persona} for the user (which will have {@link Persona.is_user} set
133
* to `true`) //and// a {@link Persona} for the contact for the other account
134
* (which will have {@link Persona.is_user} set to `false`).
136
* It is guaranteed that iff this property is set to `true` on an Individual,
137
* there will be at least one {@link Persona} in the Individual with its
138
* {@link Persona.is_user} set to `true`.
140
* It is guaranteed that there will only ever be one Individual with this
141
* property set to `true`.
145
public bool is_user { get; private set; }
118
148
* A unique identifier for the Individual.
120
150
* This uniquely identifies the Individual, and persists across
227
258
this._groups = value;
228
259
this._persona_list.foreach ((p) =>
230
if (p is Groups && ((Persona) p).store.is_writeable == true)
231
((Groups) p).groups = value;
261
if (p is Groupable && ((Persona) p).store.is_writeable == true)
262
((Groupable) p).groups = value;
270
public HashTable<string, GenericArray<string>> im_addresses
272
get { return this._im_addresses; }
237
277
* The set of {@link Persona}s encapsulated by this Individual.
239
279
* Changing the set of personas may cause updates to the aggregated properties
290
330
* @param group a freeform group identifier
291
331
* @param is_member whether the Individual should be a member of the group
293
334
public async void change_group (string group, bool is_member)
295
336
this._persona_list.foreach ((p) =>
298
((Groups) p).change_group.begin (group, is_member);
339
((Groupable) p).change_group.begin (group, is_member);
301
342
/* don't notify, since it hasn't happened in the persona backing stores
319
365
* `personas`. Otherwise, it will have to have personas added using the
320
366
* {@link Folks.Individual.personas} property after construction.
368
* @param personas a list of {@link Persona}s to initialise the
369
* {@link Individual} with, or `null`
322
370
* @return a new Individual
324
372
public Individual (GLib.List<Persona>? personas)
375
new HashTable<string, GenericArray<string>> (str_hash, str_equal);
326
376
this._persona_set = new HashSet<Persona> (null, null);
327
377
this.stores = new HashMap<PersonaStore, uint> (null, null);
328
378
this.personas = personas;
630
682
this.trust_level = trust_level;
634
* GLib/C convenience functions (for built-in casting, etc.)
638
* Get the Individual's alias.
640
* The alias is a user-chosen name for the Individual; how the user wants that
641
* Individual to be represented in UIs.
643
* @return the Individual's alias
645
public unowned string get_alias ()
651
* Get a mapping of group ID to whether the Individual is a member.
653
* Freeform group IDs are mapped to a boolean which is `true` if the
654
* Individual is a member of the group, and `false` otherwise.
656
* @return a mapping of group ID to membership status
658
public HashTable<string, bool> get_groups ()
665
* Get the Individual's current presence message.
667
* The presence message returned is from the same {@link Persona} which
668
* provided the presence type returned by
669
* {@link Individual.get_presence_type}.
671
* If none of the {@link Persona}s in the Individual have a presence message
672
* set, an empty string is returned.
674
* @return the Individual's presence message
676
public unowned string get_presence_message ()
678
return this.presence_message;
682
* Get the Individual's current presence type.
684
* The presence type returned is from the same {@link Persona} which provided
685
* the presence message returned by {@link Individual.get_presence_message}.
687
* If none of the {@link Persona}s in the Individual have a presence type set,
688
* {@link PresenceType.UNSET} is returned.
690
* @return the Individual's presence type
692
public Folks.PresenceType get_presence_type ()
694
return this.presence_type;
698
* Whether the Individual is online.
700
* This will be `true` if any of the Individual's {@link Persona}s have a
701
* presence type higher than {@link PresenceType.OFFLINE}, as determined by
702
* {@link Presence.typecmp}.
704
* @return `true` if the Individual is online, `false` otherwise
706
public bool is_online ()
709
return p.is_online ();
685
private void update_im_addresses ()
687
/* populate the IM addresses as the union of our Personas' addresses */
688
foreach (var persona in this.personas)
690
if (persona is IMable)
692
var imable = (IMable) persona;
693
imable.im_addresses.foreach ((k, v) =>
695
var cur_protocol = (string) k;
696
var cur_addresses = (GenericArray<string>) v;
697
var old_im_array = this._im_addresses.lookup (cur_protocol);
698
var im_array = new GenericArray<string> ();
700
/* use a set to eliminate duplicates */
701
var address_set = new HashSet<string> (str_hash, str_equal);
702
if (old_im_array != null)
704
old_im_array.foreach ((old_address) =>
706
address_set.add ((string) old_address);
709
cur_addresses.foreach ((cur_address) =>
711
address_set.add ((string) cur_address);
713
foreach (string addr in address_set)
716
this._im_addresses.insert (cur_protocol, im_array);
720
this.notify_property ("im-addresses");
712
723
private void connect_to_persona (Persona persona)
715
726
persona.notify["avatar"].connect (this.notify_avatar_cb);
716
727
persona.notify["presence-message"].connect (this.notify_presence_cb);
717
728
persona.notify["presence-type"].connect (this.notify_presence_cb);
729
persona.notify["im-addresses"].connect (this.notify_im_addresses_cb);
718
730
persona.notify["is-favourite"].connect (this.notify_is_favourite_cb);
720
if (persona is Groups)
732
if (persona is Groupable)
722
((Groups) persona).group_changed.connect (
734
((Groupable) persona).group_changed.connect (
723
735
this.persona_group_changed_cb);
731
743
persona.notify["presence-message"].disconnect (
732
744
this.notify_presence_cb);
733
745
persona.notify["presence-type"].disconnect (this.notify_presence_cb);
746
persona.notify["im-addresses"].disconnect (
747
this.notify_im_addresses_cb);
734
748
persona.notify["is-favourite"].disconnect (
735
749
this.notify_is_favourite_cb);
737
if (persona is Groups)
751
if (persona is Groupable)
739
((Groups) persona).group_changed.disconnect (
753
((Groupable) persona).group_changed.disconnect (
740
754
this.persona_group_changed_cb);
814
836
this.personas_changed (added, removed);
838
/* Update this.is_user */
839
bool new_is_user = (this.persona_user_count > 0) ? true : false;
840
if (new_is_user != this.is_user)
841
this.is_user = new_is_user;
816
843
/* If all the Personas have been removed, remove the Individual */
817
844
if (this._persona_set.size < 1)
819
846
this.removed (replacement_individual);
823
850
/* TODO: Base this upon our ID in permanent storage, once we have that. */