Contacts API
Please create a REST-type API for the following namespaces:
SQL
Make scripts/Users/0.8.1-Users.mysql upgrade script to add "removable" to "label".
JS Model
(There might be an easier model that just pulls everything into the client side with one request (Labels and Contacts) and then uses methods to filter, and create / update / delete. And the same method would refresh if some message came in on a stream that the contacts changed.)
Q.Users.Label - for managing labels
Q.Users.Label.get(label, callback) - Q.getter for getting a label by its name
Q.Users.Label.byPrefix([prefix], callback) - Q.getter for all labels starting with this prefix
Q.Users.Label.byContactUserId(userId) - gets all the Q.Users.Label objects from users_contact rows connceting loggedInUser and contactUserId
Q.Users.Label.getAll([prefix], callback) - Q.getter for all labels
Q.Users.Label.prototype.contacts(callback, options) - should call Q.Users.Contacts.byLabel
userId, label, icon, title
Q.Users.Label.prototype.save() - saves the updated icon and title
Q.Users.Label.create(info, callback) -- makes a removable label, chooses a name that looks like "Users/3487c8a"
Q.Users.Label.prototype.remove() - removes the label and all contacts with that label
Q.Users.Contact - for managing contacts
Q.Users.Contact.byLabel(labels, callback) - Q.getter where callback gets err and an object of {userId: contact} pairs where contact is of type Users.Contact. You can pass null or a string or an array in labels
Q.Users.Contact.filter(contacts, options) - contacts is what's passed to the callback in byLabel and options can include prefix, limit, offset. This function returns an object that's a subset of the contacts.
userId, label, contactUserId, nickname, insertedTime - you can change the nickname only, and then call save()
Q.Users.Contact.setNickname(contactUserId, nickname, callback) - update nicknames for all contacts connecting loggedInUser and contactUserId
Q.Users.Contact.create(label, contactUserId, nickname, callback) -- adds a contact
Q.Users.Contact.prototype.remove(label, contactUserId, callback) -- removes the contact
(For the next section, just to explain to Vlad -- the users_link table stores info imported from address books that is then converted to contacts when people sign up ... so if e.g. your friends import address books with you in them, and then you sign up using one of the identifiers which they imported, all those links to into contacts for them, and now you are in their contacts. See users_link and users_contact tables)
Q.Users.Links - there will be no api for this because the primary key for links starts with identifier not userId. (The reason for that is that we wanted Users plugin to not rely on Streams plugin, and so implemented Users::saveContactsFromLinks in PHP, but as a result we needed the primary key to start with identifier). That's OK, what this means is that you can import your address books but then you won't have an interface to browse everyone you've imported. You will only be able to see users who actually joined the app, so your links converted to contacts. Oh, and of also the futureUsers you've invited, because you have them in your Streams/invited label.
Tools
Selecting existing contacts
Now, create a "Streams/contacts" tool. It will use the above API to list all your contacts as avatars. It will basically receive the avatars as a batch.
Actually, the Streams plugin can implement an additional slot for Users/contact handlers called "avatars". It would fill this slot with the exported data from avatars fetched for all the contacts that were retrieved. Q.Users.Contact.byLabel would also need to provide a hook for Streams.js to get these avatars.
The Streams plugin should also maintain a stream that you join, to which messages are posted when labels and contacts are added / changed / removed. The messages would contain enough info to update the client data structure completely.
Sketch the tool and have it let you edit labels, contacts, etc.
Selecting public contacts
"Streams/userChooser" - check out that tool, it already exists and the back end for it also
and how Streams_userChooser_response_data is superceded by Streams_Avatar::fetchByPrefix