Explorar o código

Refactor `converse-vcard` and add API method to fetch a VCard

JC Brand %!s(int64=7) %!d(string=hai) anos
pai
achega
8662f751e3
Modificáronse 5 ficheiros con 228 adicións e 185 borrados
  1. 2 0
      CHANGES.md
  2. 77 51
      docs/source/developer_api.rst
  3. 32 29
      spec/controlbox.js
  4. 4 4
      spec/protocol.js
  5. 113 101
      src/converse-vcard.js

+ 2 - 0
CHANGES.md

@@ -29,6 +29,8 @@
 ### API changes
 - New API method `_converse.disco.supports` to check whether a certain
   service discovery feature is supported by an entity.
+- New API method `_converse.api.vcard.get` which fetches the VCard for a
+  particular JID.
 
 ### UX/UI changes
 - Use CSS3 fade transitions to render various elements.

+ 77 - 51
docs/source/developer_api.rst

@@ -829,6 +829,67 @@ To return an array of chat boxes, provide an array of JIDs:
 | url         | The URL of the chat box heading.                    |
 +-------------+-----------------------------------------------------+
 
+.. _`listen-grouping`:
+
+The **listen** grouping
+-----------------------
+
+Converse.js emits events to which you can subscribe from your own JavaScript.
+
+Concerning events, the following methods are available under the "listen"
+grouping:
+
+* **on(eventName, callback, [context])**:
+
+    Calling the ``on`` method allows you to subscribe to an event.
+    Every time the event fires, the callback method specified by ``callback`` will be
+    called.
+
+    Parameters:
+
+    * ``eventName`` is the event name as a string.
+    * ``callback`` is the callback method to be called when the event is emitted.
+    * ``context`` (optional), the value of the `this` parameter for the callback.
+
+    For example:
+
+.. code-block:: javascript
+
+        _converse.api.listen.on('message', function (messageXML) { ... });
+
+* **once(eventName, callback, [context])**:
+
+    Calling the ``once`` method allows you to listen to an event
+    exactly once.
+
+    Parameters:
+
+    * ``eventName`` is the event name as a string.
+    * ``callback`` is the callback method to be called when the event is emitted.
+    * ``context`` (optional), the value of the `this` parameter for the callback.
+
+    For example:
+
+.. code-block:: javascript
+
+        _converse.api.listen.once('message', function (messageXML) { ... });
+
+* **not(eventName, callback)**
+
+    To stop listening to an event, you can use the ``not`` method.
+
+    Parameters:
+
+    * ``eventName`` is the event name as a string.
+    * ``callback`` refers to the function that is to be no longer executed.
+
+    For example:
+
+.. code-block:: javascript
+
+        _converse.api.listen.not('message', function (messageXML) { ... });
+
+
 The **rooms** grouping
 ----------------------
 
@@ -1130,63 +1191,28 @@ Example:
     });
 
 
-.. _`listen-grouping`:
-
-The **listen** grouping
+The **vcard** grouping
 -----------------------
 
-Converse.js emits events to which you can subscribe from your own JavaScript.
-
-Concerning events, the following methods are available under the "listen"
-grouping:
-
-* **on(eventName, callback, [context])**:
-
-    Calling the ``on`` method allows you to subscribe to an event.
-    Every time the event fires, the callback method specified by ``callback`` will be
-    called.
-
-    Parameters:
-
-    * ``eventName`` is the event name as a string.
-    * ``callback`` is the callback method to be called when the event is emitted.
-    * ``context`` (optional), the value of the `this` parameter for the callback.
-
-    For example:
-
-.. code-block:: javascript
-
-        _converse.api.listen.on('message', function (messageXML) { ... });
-
-* **once(eventName, callback, [context])**:
-
-    Calling the ``once`` method allows you to listen to an event
-    exactly once.
-
-    Parameters:
+get
+~~~
 
-    * ``eventName`` is the event name as a string.
-    * ``callback`` is the callback method to be called when the event is emitted.
-    * ``context`` (optional), the value of the `this` parameter for the callback.
+Returns a Promise which results with the VCard data for a particular JID.
 
-    For example:
+Example:
 
 .. code-block:: javascript
 
-        _converse.api.listen.once('message', function (messageXML) { ... });
-
-* **not(eventName, callback)**
-
-    To stop listening to an event, you can use the ``not`` method.
-
-    Parameters:
-
-    * ``eventName`` is the event name as a string.
-    * ``callback`` refers to the function that is to be no longer executed.
-
-    For example:
-
-.. code-block:: javascript
+    converse.plugins.add('myplugin', {
+        initialize: function () {
 
-        _converse.api.listen.not('message', function (messageXML) { ... });
+            _converse.api.waitUntil('rosterContactsFetched').then(() => {
+                this._converse.api.vcard.get('someone@example.org').then(
+                    (vcard) => {
+                        // Do something with the vcard...
+                    }
+                );
+            });
 
+        }
+    });

+ 32 - 29
spec/controlbox.js

@@ -1146,35 +1146,38 @@
 
                 var stanza = $pres({from: 'data@enterprise/resource', type: 'subscribe'});
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                expect(_converse.roster.pluck('jid').length).toBe(1);
-                expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
-
-                // Taken from the spec
-                // http://xmpp.org/rfcs/rfc3921.html#rfc.section.7.3
-                stanza = $iq({
-                    to: _converse.connection.jid,
-                    type: 'result',
-                    id: 'roster_1'
-                }).c('query', {
-                    xmlns: 'jabber:iq:roster',
-                }).c('item', {
-                    jid: 'romeo@example.net',
-                    name: 'Romeo',
-                    subscription:'both'
-                }).c('group').t('Friends').up().up()
-                .c('item', {
-                    jid: 'mercutio@example.org',
-                    name: 'Mercutio',
-                    subscription:'from'
-                }).c('group').t('Friends').up().up()
-                .c('item', {
-                    jid: 'benvolio@example.org',
-                    name: 'Benvolio',
-                    subscription:'both'
-                }).c('group').t('Friends');
-                _converse.roster.onReceivedFromServer(stanza.tree());
-                expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
-                done();
+                test_utils.waitUntil(function () {
+                    return $('a:contains("Contact requests")').length;
+                }).then(function () {
+                    expect(_converse.roster.pluck('jid').length).toBe(1);
+                    expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
+                    // Taken from the spec
+                    // http://xmpp.org/rfcs/rfc3921.html#rfc.section.7.3
+                    stanza = $iq({
+                        to: _converse.connection.jid,
+                        type: 'result',
+                        id: 'roster_1'
+                    }).c('query', {
+                        xmlns: 'jabber:iq:roster',
+                    }).c('item', {
+                        jid: 'romeo@example.net',
+                        name: 'Romeo',
+                        subscription:'both'
+                    }).c('group').t('Friends').up().up()
+                    .c('item', {
+                        jid: 'mercutio@example.org',
+                        name: 'Mercutio',
+                        subscription:'from'
+                    }).c('group').t('Friends').up().up()
+                    .c('item', {
+                        jid: 'benvolio@example.org',
+                        name: 'Benvolio',
+                        subscription:'both'
+                    }).c('group').t('Friends');
+                    _converse.roster.onReceivedFromServer(stanza.tree());
+                    expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
+                    done();
+                });
             }));
         });
 

+ 4 - 4
spec/protocol.js

@@ -64,7 +64,7 @@
                 spyOn(_converse.roster, "addAndSubscribe").and.callThrough();
                 spyOn(_converse.roster, "addContact").and.callThrough();
                 spyOn(_converse.roster, "sendContactAddIQ").and.callThrough();
-                spyOn(_converse, "getVCard").and.callThrough();
+                spyOn(_converse.api.vcard, "get").and.callThrough();
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
                     sent_stanza = iq;
@@ -172,7 +172,7 @@
                 // A contact should now have been created
                 expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy();
                 expect(contact.get('jid')).toBe('contact@example.org');
-                expect(_converse.getVCard).toHaveBeenCalled();
+                expect(_converse.api.vcard.get).toHaveBeenCalled();
 
                 /* To subscribe to the contact's presence information,
                 * the user's client MUST send a presence stanza of
@@ -525,9 +525,9 @@
                 null, ['rosterGroupsFetched'], {},
                 function (done, _converse) {
 
+                spyOn(_converse, "emit");
                 test_utils.openControlBox(_converse);
                 test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
-                spyOn(_converse, "emit");
                 /* <presence
                  *     from='user@example.com'
                  *     to='contact@example.org'
@@ -541,10 +541,10 @@
                     'xmlns': Strophe.NS.NICK,
                 }).t('Clint Contact');
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                expect(_converse.emit).toHaveBeenCalledWith('contactRequest', jasmine.any(Object));
                 test_utils.waitUntil(function () {
                     return $('a:contains("Contact requests")').length;
                 }).then(function () {
+                    expect(_converse.emit).toHaveBeenCalledWith('contactRequest', jasmine.any(Object));
                     var $header = $('a:contains("Contact requests")');
                     expect($header.length).toBe(1);
                     expect($header.is(":visible")).toBeTruthy();

+ 113 - 101
src/converse-vcard.js

@@ -10,7 +10,87 @@
     define(["converse-core", "strophe.vcard"], factory);
 }(this, function (converse) {
     "use strict";
-    const { Strophe, _, moment, sizzle } = converse.env;
+    const { Promise, Strophe, _, moment, sizzle } = converse.env;
+
+
+    function onVCardData (_converse, jid, iq, callback) {
+        const vcard = iq.querySelector('vCard'),
+            img_type = _.get(vcard.querySelector('TYPE'), 'textContent'),
+            img = _.get(vcard.querySelector('BINVAL'), 'textContent'),
+            url = _.get(vcard.querySelector('URL'), 'textContent'),
+            fullname = _.get(vcard.querySelector('FN'), 'textContent');
+
+        if (jid) {
+            const contact = _converse.roster.get(jid);
+            if (contact) {
+                contact.save({
+                    'fullname': fullname || _.get(contact, 'fullname', jid),
+                    'image_type': img_type,
+                    'image': img,
+                    'url': url,
+                    'vcard_updated': moment().format()
+                });
+            }
+        }
+        if (callback) {
+            callback({
+                'stanza': iq,
+                'jid': jid,
+                'fullname': fullname || jid,
+                'image': img,
+                'image_type': img_type,
+                'url': url
+            });
+        }
+    }
+
+    function onVCardError (_converse, jid, iq, errback) {
+        const contact = _converse.roster.get(jid);
+        if (contact) {
+            contact.save({'vcard_updated': moment().format() });
+        }
+        if (errback) { errback({'stanza': iq, 'jid': jid}); }
+    }
+
+    function getVCard (_converse, jid) {
+        /* Request the VCard of another user. Returns a promise.
+         *
+         * Parameters:
+         *    (String) jid - The Jabber ID of the user whose VCard
+         *      is being requested.
+         */
+        if (Strophe.getBareJidFromJid(jid) === _converse.bare_jid) {
+            jid = null; // No 'to' attr when getting one's own vCard
+        }
+        return new Promise((resolve, reject) => {
+            if (!_converse.use_vcards) {
+                if (resolve) { resolve({'jid': jid}); }
+            } else {
+                _converse.connection.vcard.get(
+                    _.partial(onVCardData, _converse, jid, _, resolve),
+                    jid,
+                    _.partial(onVCardError, _converse, jid, _, resolve)
+                );
+            }
+        });
+    }
+
+    function updateChatBoxFromVCard (_converse, jid) {
+        _converse.api.vcard.get(jid)
+            .then((vcard) => {
+                const chatbox = _converse.chatboxes.getChatBox(vcard.jid);
+                if (!_.isUndefined(chatbox)) {
+                    chatbox.save(_.pick(vcard, ['fullname', 'url', 'image_type', 'image', 'vcard_updated']));
+                }
+            })
+            .catch(() => {
+                _converse.log(
+                    "updateChatBoxFromVCard: Error occured while attempting to update chatbox with VCard data",
+                    Strophe.LogLevel.ERROR
+                );
+            });
+    }
+
 
     converse.plugins.add('converse-vcard', {
 
@@ -25,17 +105,15 @@
                 createRequestingContact (presence) {
                     const { _converse } = this.__super__;
                     const bare_jid = Strophe.getBareJidFromJid(presence.getAttribute('from'));
-                    _converse.getVCard(
-                        bare_jid,
-                        _.partial(_converse.createRequestingContactFromVCard, presence),
-                        function (iq, jid) {
+
+                    _converse.api.vcard.get(bare_jid)
+                        .then(_.partial(_converse.createRequestingContactFromVCard, presence))
+                        .catch((vcard) => {
                             _converse.log(
-                                `Error while retrieving vcard for ${jid}`,
-                                Strophe.LogLevel.WARN
-                            );
-                            _converse.createRequestingContactFromVCard(presence, iq, jid);
-                        }
-                    );
+                                `Error while retrieving vcard for ${vcard.jid}`,
+                                Strophe.LogLevel.WARN);
+                            _converse.createRequestingContactFromVCard(presence, vcard.stanza, vcard.jid);
+                        });
                 }
             }
         },
@@ -49,8 +127,9 @@
                 use_vcards: true,
             });
 
-            _converse.createRequestingContactFromVCard = function (presence, iq, jid, fullname, img, img_type, url) {
-                const bare_jid = Strophe.getBareJidFromJid(jid);
+            _converse.createRequestingContactFromVCard = function (presence, vcard) {
+                const bare_jid = Strophe.getBareJidFromJid(vcard.jid);
+                let fullname = vcard.fullname;
                 if (!fullname) {
                     const nick_el = sizzle(`nick[xmlns="${Strophe.NS.NICK}"]`, presence);
                     fullname = nick_el.length ? nick_el[0].textContent : bare_jid;
@@ -61,68 +140,15 @@
                     ask: null,
                     requesting: true,
                     fullname: fullname,
-                    image: img,
-                    image_type: img_type,
-                    url,
+                    image: vcard.image,
+                    image_type: vcard.image_type,
+                    url: vcard.url,
                     vcard_updated: moment().format()
                 };
                 _converse.roster.create(user_data);
                 _converse.emit('contactRequest', user_data);
             };
 
-            _converse.onVCardError = function (jid, iq, errback) {
-                const contact = _.get(_converse.roster, jid);
-                if (contact) {
-                    contact.save({ 'vcard_updated': moment().format() });
-                }
-                if (errback) { errback(iq, jid); }
-            };
-
-            _converse.onVCardData = function (jid, iq, callback) {
-                const vcard = iq.querySelector('vCard'),
-                    img_type = _.get(vcard.querySelector('TYPE'), 'textContent'),
-                    img = _.get(vcard.querySelector('BINVAL'), 'textContent'),
-                    url = _.get(vcard.querySelector('URL'), 'textContent'),
-                    fullname = _.get(vcard.querySelector('FN'), 'textContent');
-
-                if (jid) {
-                    const contact = _converse.roster.get(jid);
-                    if (contact) {
-                        contact.save({
-                            'fullname': fullname || _.get(contact, 'fullname', jid),
-                            'image_type': img_type,
-                            'image': img,
-                            'url': url,
-                            'vcard_updated': moment().format()
-                        });
-                    }
-                }
-                if (callback) {
-                    callback(iq, jid, fullname, img, img_type, url);
-                }
-            };
-
-            _converse.getVCard = function (jid, callback, errback) {
-                /* Request the VCard of another user.
-                 *
-                 * Parameters:
-                 *    (String) jid - The Jabber ID of the user whose VCard
-                 *      is being requested.
-                 *    (Function) callback - A function to call once the VCard is
-                 *      returned.
-                 *    (Function) errback - A function to call if an error occured
-                 *      while trying to fetch the VCard.
-                 */
-                if (!_converse.use_vcards) {
-                    if (callback) { callback(null, jid); }
-                } else {
-                    _converse.connection.vcard.get(
-                        _.partial(_converse.onVCardData, jid, _, callback),
-                        jid,
-                        _.partial(_converse.onVCardError, jid, _, errback));
-                }
-            };
-
             /* Event handlers */
             _converse.on('addClientFeatures', () => {
                 if (_converse.use_vcards) {
@@ -138,47 +164,33 @@
                     const jid = chatbox.model.get('jid'),
                         contact = _converse.roster.get(jid);
                     if ((contact) && (!contact.get('vcard_updated'))) {
-                        _converse.getVCard(
-                            jid,
-                            function (iq, jid, fullname, image, image_type, url) {
-                                chatbox.model.save({
-                                    'fullname' : fullname || jid,
-                                    'url': url,
-                                    'image_type': image_type,
-                                    'image': image
-                                });
-                            },
-                            function () {
-                                _converse.log(
-                                    "updateVCardForChatBox: Error occured while fetching vcard",
-                                    Strophe.LogLevel.ERROR
-                                );
-                            }
-                        );
+                        updateChatBoxFromVCard(_converse, jid);
                     }
                 }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
             };
             _converse.on('chatBoxInitialized', updateVCardForChatBox);
 
-            const onContactAdd = function (contact) {
-                if (!contact.get('vcard_updated')) {
-                    // This will update the vcard, which triggers a change
-                    // request which will rerender the roster contact.
-                    _converse.getVCard(contact.get('jid'));
-                }
-            };
-            _converse.on('initialized', function () {
-                _converse.roster.on("add", onContactAdd);
+            _converse.on('initialized', () => {
+                _converse.roster.on("add", (contact) => {
+                    if (!contact.get('vcard_updated')) {
+                        _converse.api.vcard.get(contact.get('jid'));
+                    }
+                });
             });
 
             _converse.on('statusInitialized', function fetchOwnVCard () {
                 if (_converse.xmppstatus.get('fullname') === undefined) {
-                    _converse.getVCard(
-                        null, // No 'to' attr when getting one's own vCard
-                        function (iq, jid, fullname) {
-                            _converse.xmppstatus.save({'fullname': fullname});
-                        }
-                    );
+                    _converse.api.vcard.get(_converse.bare_jid).then((vcard) => {
+                        _converse.xmppstatus.save({'fullname': vcard.fullname});
+                    });
+                }
+            });
+
+            _.extend(_converse.api, {
+                'vcard': {
+                    'get' (jid) {
+                        return getVCard(_converse, jid);
+                    }
                 }
             });
         }