浏览代码

Support for XEP-0203 delayed delivery of presence stanzas

JC Brand 8 年之前
父节点
当前提交
17e0e70e4e
共有 3 个文件被更改,包括 200 次插入185 次删除
  1. 1 0
      docs/CHANGES.md
  2. 156 160
      spec/presence.js
  3. 43 25
      src/converse-core.js

+ 1 - 0
docs/CHANGES.md

@@ -16,6 +16,7 @@
     * Templates are no longer stored as attributes on the `_converse` object.
       If you need a particular template, use `require` to load it.
 
+- Better support for delayed delivery of presence stanzas (XEP-0203). [jcbrand]
 - The chat room `description` is now shown in the heading, not the `subject`.
   [jcbrand]
 - Chat room features are shown in the sidebar. [jcbrand]

+ 156 - 160
spec/presence.js

@@ -21,174 +21,170 @@
             test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
             var contact_jid = mock.cur_names[8].replace(/ /g,'.').toLowerCase() + '@localhost';
             var contact = _converse.roster.get(contact_jid);
-            var stanza;
-            runs(function () {
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          from="'+contact_jid+'/priority-1-resource">'+
-                '    <priority>1</priority>'+
-                '    <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" ext="voice-v1 camera-v1 video-v1"'+
-                '       ver="AcN1/PEN8nq7AHD+9jpxMV4U6YM=" node="http://pidgin.im/"/>'+
-                '    <x xmlns="vcard-temp:x:update">'+
-                '        <photo>ce51d94f7f22b87a21274abb93710b9eb7cc1c65</photo>'+
-                '    </x>'+
-                '    <delay xmlns="urn:xmpp:delay" stamp="2017-02-15T20:26:05Z" from="jabbim.hu"/>'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(contact.get('chat_status')).toBe('online');
-                expect(_.keys(contact.get('resources')).length).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
+            var stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          from="'+contact_jid+'/priority-1-resource">'+
+            '    <priority>1</priority>'+
+            '    <c xmlns="http://jabber.org/protocol/caps" hash="sha-1" ext="voice-v1 camera-v1 video-v1"'+
+            '       ver="AcN1/PEN8nq7AHD+9jpxMV4U6YM=" node="http://pidgin.im/"/>'+
+            '    <x xmlns="vcard-temp:x:update">'+
+            '        <photo>ce51d94f7f22b87a21274abb93710b9eb7cc1c65</photo>'+
+            '    </x>'+
+            '    <delay xmlns="urn:xmpp:delay" stamp="2017-02-15T20:26:05Z" from="'+contact_jid+'/priority-1-resource"/>'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(contact.get('chat_status')).toBe('online');
+            expect(_.keys(contact.get('resources')).length).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          from="'+contact_jid+'/priority-0-resource">'+
-                '    <status/>'+
-                '    <priority>0</priority>'+
-                '    <show>xa</show>'+
-                '    <c xmlns="http://jabber.org/protocol/caps" ver="GyIX/Kpa4ScVmsZCxRBboJlLAYU=" hash="sha-1"'+
-                '       node="http://www.igniterealtime.org/projects/smack/"/>'+
-                '    <delay xmlns="urn:xmpp:delay" stamp="2017-02-15T17:02:24Z" from="jabbim.hu"/>'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('online');
-                expect(_.keys(contact.get('resources')).length).toBe(2);
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
-                expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          from="'+contact_jid+'/priority-0-resource">'+
+            '    <status/>'+
+            '    <priority>0</priority>'+
+            '    <show>xa</show>'+
+            '    <c xmlns="http://jabber.org/protocol/caps" ver="GyIX/Kpa4ScVmsZCxRBboJlLAYU=" hash="sha-1"'+
+            '       node="http://www.igniterealtime.org/projects/smack/"/>'+
+            '    <delay xmlns="urn:xmpp:delay" stamp="2017-02-15T17:02:24Z" from="'+contact_jid+'/priority-0-resource"/>'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('online');
+            expect(_.keys(contact.get('resources')).length).toBe(2);
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          from="'+contact_jid+'/priority-2-resource">'+
-                '    <priority>2</priority>'+
-                '    <show>dnd</show>'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('dnd');
-                expect(_.keys(contact.get('resources')).length).toBe(3);
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
-                expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
-                expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
-                expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          from="'+contact_jid+'/priority-2-resource">'+
+            '    <priority>2</priority>'+
+            '    <show>dnd</show>'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('dnd');
+            expect(_.keys(contact.get('resources')).length).toBe(3);
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
+            expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
+            expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          from="'+contact_jid+'/priority-3-resource">'+
-                '    <priority>3</priority>'+
-                '    <show>away</show>'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('away');
-                expect(_.keys(contact.get('resources')).length).toBe(4);
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
-                expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
-                expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
-                expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
-                expect(contact.get('resources')['priority-3-resource']['priority']).toBe(3);
-                expect(contact.get('resources')['priority-3-resource']['status']).toBe('away');
-            });
-            waits(1000); // XXX: Bit of a hack. With jasmine 2 we can mock the date instead.
-            runs(function () {
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          from="'+contact_jid+'/newer-priority-1-resource">'+
-                '    <priority>1</priority>'+
-                '    <show>dnd</show>'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('away');
-                expect(_.keys(contact.get('resources')).length).toBe(5);
-                expect(contact.get('resources')['newer-priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['newer-priority-1-resource']['status']).toBe('dnd');
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
-                expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
-                expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
-                expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
-                expect(contact.get('resources')['priority-3-resource']['priority']).toBe(3);
-                expect(contact.get('resources')['priority-3-resource']['status']).toBe('away');
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          from="'+contact_jid+'/priority-3-resource">'+
+            '    <priority>3</priority>'+
+            '    <show>away</show>'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('away');
+            expect(_.keys(contact.get('resources')).length).toBe(4);
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
+            expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
+            expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
+            expect(contact.get('resources')['priority-3-resource']['priority']).toBe(3);
+            expect(contact.get('resources')['priority-3-resource']['status']).toBe('away');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          type="unavailable"'+
-                '          from="'+contact_jid+'/priority-3-resource">'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('dnd');
-                expect(_.keys(contact.get('resources')).length).toBe(4);
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
-                expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
-                expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
-                expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
-                expect(contact.get('resources')['newer-priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['newer-priority-1-resource']['status']).toBe('dnd');
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          from="'+contact_jid+'/older-priority-1-resource">'+
+            '    <priority>1</priority>'+
+            '    <show>dnd</show>'+
+            '    <delay xmlns="urn:xmpp:delay" stamp="2017-02-15T15:02:24Z" from="'+contact_jid+'/older-priority-1-resource"/>'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('away');
+            expect(_.keys(contact.get('resources')).length).toBe(5);
+            expect(contact.get('resources')['older-priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['older-priority-1-resource']['status']).toBe('dnd');
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
+            expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
+            expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
+            expect(contact.get('resources')['priority-3-resource']['priority']).toBe(3);
+            expect(contact.get('resources')['priority-3-resource']['status']).toBe('away');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          type="unavailable"'+
-                '          from="'+contact_jid+'/priority-2-resource">'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('dnd');
-                expect(_.keys(contact.get('resources')).length).toBe(3);
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
-                expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
-                expect(contact.get('resources')['newer-priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['newer-priority-1-resource']['status']).toBe('dnd');
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          type="unavailable"'+
+            '          from="'+contact_jid+'/priority-3-resource">'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('dnd');
+            expect(_.keys(contact.get('resources')).length).toBe(4);
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
+            expect(contact.get('resources')['priority-2-resource']['priority']).toBe(2);
+            expect(contact.get('resources')['priority-2-resource']['status']).toBe('dnd');
+            expect(contact.get('resources')['older-priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['older-priority-1-resource']['status']).toBe('dnd');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          type="unavailable"'+
-                '          from="'+contact_jid+'/priority-1-resource">'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('dnd');
-                expect(_.keys(contact.get('resources')).length).toBe(2);
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
-                expect(contact.get('resources')['newer-priority-1-resource']['priority']).toBe(1);
-                expect(contact.get('resources')['newer-priority-1-resource']['status']).toBe('dnd');
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          type="unavailable"'+
+            '          from="'+contact_jid+'/priority-2-resource">'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('online');
+            expect(_.keys(contact.get('resources')).length).toBe(3);
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            expect(contact.get('resources')['priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['priority-1-resource']['status']).toBe('online');
+            expect(contact.get('resources')['older-priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['older-priority-1-resource']['status']).toBe('dnd');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          type="unavailable"'+
-                '          from="'+contact_jid+'/newer-priority-1-resource">'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('xa');
-                expect(_.keys(contact.get('resources')).length).toBe(1);
-                expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
-                expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          type="unavailable"'+
+            '          from="'+contact_jid+'/priority-1-resource">'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('dnd');
+            expect(_.keys(contact.get('resources')).length).toBe(2);
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+            expect(contact.get('resources')['older-priority-1-resource']['priority']).toBe(1);
+            expect(contact.get('resources')['older-priority-1-resource']['status']).toBe('dnd');
 
-                stanza = $(
-                '<presence xmlns="jabber:client"'+
-                '          to="dummy@localhost/converse.js-21770972"'+
-                '          type="unavailable"'+
-                '          from="'+contact_jid+'/priority-0-resource">'+
-                '</presence>');
-                _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
-                expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('offline');
-                expect(_.keys(contact.get('resources')).length).toBe(0);
-            });
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          type="unavailable"'+
+            '          from="'+contact_jid+'/older-priority-1-resource">'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('xa');
+            expect(_.keys(contact.get('resources')).length).toBe(1);
+            expect(contact.get('resources')['priority-0-resource']['priority']).toBe(0);
+            expect(contact.get('resources')['priority-0-resource']['status']).toBe('xa');
+
+            stanza = $(
+            '<presence xmlns="jabber:client"'+
+            '          to="dummy@localhost/converse.js-21770972"'+
+            '          type="unavailable"'+
+            '          from="'+contact_jid+'/priority-0-resource">'+
+            '</presence>');
+            _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
+            expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('offline');
+            expect(_.keys(contact.get('resources')).length).toBe(0);
         }));
     });
 }));

+ 43 - 25
src/converse-core.js

@@ -177,6 +177,7 @@
         Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
         Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
         Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub');
+        Strophe.addNamespace('DELAY', 'urn:xmpp:delay');
 
         // Instance level constants
         this.TIMEOUTS = { // Set as module attr so that we can override in tests.
@@ -855,22 +856,44 @@
                 return this;
             },
 
-            addResource: function (resource, priority, chat_status) {
-                var resources = this.get('resources');
-                if (!_.isObject(resources)) { resources = {}; }
+            addResource: function (presence) {
+                /* Adds a new resource and it's associated attributes as taken
+                 * from the passed in presence stanza.
+                 *
+                 * Also updates the contact's chat_status if the presence has
+                 * higher priority (and is newer).
+                 */
+                var jid = presence.getAttribute('from'),
+                    chat_status = _.propertyOf(presence.querySelector('show'))('textContent') || 'online',
+                    resource = Strophe.getResourceFromJid(jid),
+                    priority = _.propertyOf(presence.querySelector('priority'))('textContent') || 0,
+                    delay = presence.querySelector('delay[xmlns="'+Strophe.NS.DELAY+'"]'),
+                    timestamp = _.isNull(delay) ? moment().format() : moment(delay.getAttribute('stamp')).format();
+
+                priority = _.isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10);
+
+                var resources = _.isObject(this.get('resources')) ? this.get('resources') : {};
                 resources[resource] = {
-                    'priority': _.isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
+                    'priority': priority,
                     'status': chat_status,
-                    'timestamp': moment().format()
+                    'timestamp': timestamp 
                 };
-                this.set({'resources': resources});
+                var changed = {'resources': resources};
+                var hpr = this.getHighestPriorityResource();
+                if (priority == hpr.priority && timestamp == hpr.timestamp) {
+                    // Only set the chat status if this is the newest resource
+                    // with the highest priority
+                    changed.chat_status = chat_status;
+                }
+                this.save(changed);
                 return resources;
             },
 
             removeResource: function (resource) {
-                /* Remove the passed in resource from the contact's resources
-                 * map.
-                 * Return the amount of resources left over.
+                /* Remove the passed in resource from the contact's resources map.
+                 *
+                 * Also recomputes the chat_status given that there's one less
+                 * resource.
                  */
                 var resources = this.get('resources');
                 if (!_.isObject(resources)) {
@@ -878,13 +901,15 @@
                 } else {
                     delete resources[resource];
                 }
-                this.save({'resources': resources});
-                return _.size(resources);
+                this.save({
+                    'resources': resources,
+                    'chat_status': _.propertyOf(
+                        this.getHighestPriorityResource())('status') || 'offline'
+                });
             },
 
-            getHighestPriorityStatus: function () {
-                /* Return the chat status assigned to the resource with the
-                 * highest priority.
+            getHighestPriorityResource: function () {
+                /* Return the resource with the highest priority.
                  *
                  * If multiple resources have the same priority, take the
                  * newest one.
@@ -897,10 +922,9 @@
                             _.reverse
                         )(resources)[0];
                     if (!_.isUndefined(val)) {
-                        return val.status;
+                        return val;
                     }
                 }
-                return 'offline';
             },
 
             removeFromRoster: function (callback) {
@@ -1225,7 +1249,6 @@
                     resource = Strophe.getResourceFromJid(jid),
                     chat_status = _.propertyOf(presence.querySelector('show'))('textContent') || 'online',
                     status_message = _.propertyOf(presence.querySelector('status'))('textContent'),
-                    priority = _.propertyOf(presence.querySelector('priority'))('textContent') || 0,
                     contact = this.get(bare_jid);
 
                 if (this.isSelf(bare_jid)) {
@@ -1258,14 +1281,9 @@
                     this.handleIncomingSubscription(presence);
                 } else if (presence_type === 'unavailable' && contact) {
                     contact.removeResource(resource);
-                    contact.save({'chat_status': contact.getHighestPriorityStatus()});
-                } else if (contact) { // presence_type is undefined
-                    var resources = contact.addResource(resource, priority, chat_status);
-                    if (priority >= _.flow(_.values, _.partial(_.map, _, 'priority'), _.max)(resources)) {
-                        // Only save if it's the resource with the highest
-                        // priority
-                        contact.save({'chat_status': chat_status});
-                    }
+                } else if (contact) {
+                    // presence_type is undefined
+                    contact.addResource(presence);
                 }
             }
         });