Improved roster performance.

Don't sort the roster group for each `chat_status` change. Instead batch
every 500ms.
This commit is contained in:
JC Brand 2017-12-20 12:17:58 +00:00
parent fb35ed844a
commit ce1954a9f7
6 changed files with 100 additions and 42 deletions

View File

@ -73,10 +73,11 @@
spyOn(_converse.chatboxviews, 'trimChats'); spyOn(_converse.chatboxviews, 'trimChats');
expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
var online_contacts = _converse.rosterview.$el.find('dt.roster-group').siblings('dd.current-xmpp-contact').find('a.open-chat'); var online_contacts = _converse.rosterview.$el.find('.roster-group .current-xmpp-contact a.open-chat');
expect(online_contacts.length).toBe(15);
for (i=0; i<online_contacts.length; i++) { for (i=0; i<online_contacts.length; i++) {
$el = $(online_contacts[i]); $el = $(online_contacts[i]);
jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost'; jid = $el.text().trim().replace(/ /g,'.').toLowerCase() + '@localhost';
$el.click(); $el.click();
chatboxview = _converse.chatboxviews.get(jid); chatboxview = _converse.chatboxviews.get(jid);
expect(_converse.chatboxes.length).toEqual(i+2); expect(_converse.chatboxes.length).toEqual(i+2);
@ -110,14 +111,14 @@
_converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced. _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
test_utils.waitUntil(function () { test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('.roster-group').length; return _converse.rosterview.$el.find('.roster-group').length;
}, 300) }, 300).then(function () {
.then(function () {
// Test that they can be maximized again // Test that they can be maximized again
var online_contacts = _converse.rosterview.$el.find('dt.roster-group').siblings('dd.current-xmpp-contact').find('a.open-chat'); var online_contacts = _converse.rosterview.$el.find('.roster-group .current-xmpp-contact a.open-chat');
expect(online_contacts.length).toBe(15);
for (i=0; i<online_contacts.length; i++) { for (i=0; i<online_contacts.length; i++) {
$el = $(online_contacts[i]); $el = $(online_contacts[i]);
jid = _.trim($el.text()).replace(/ /g,'.').toLowerCase() + '@localhost'; jid = _.trim($el.text().trim()).replace(/ /g,'.').toLowerCase() + '@localhost';
$el.click(); $el.click();
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled(); expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
@ -128,8 +129,8 @@
expect(chatboxview.minimize).toHaveBeenCalled(); expect(chatboxview.minimize).toHaveBeenCalled();
} }
return test_utils.waitUntil(function () { return test_utils.waitUntil(function () {
return _converse.chatboxviews.keys().length > 1; return _converse.chatboxviews.keys().length > 1;
}, 500); }, 500);
}).then(function () { }).then(function () {
var key = _converse.chatboxviews.keys()[1]; var key = _converse.chatboxviews.keys()[1];
trimmedview = trimmed_chatboxes.get(key); trimmedview = trimmed_chatboxes.get(key);

View File

@ -418,9 +418,8 @@
}); });
} }
test_utils.waitUntil(function () { test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('li:visible').length; return _converse.rosterview.$el.find('li:visible').length;
}, 500) }, 500).then(function () {
.then(function () {
// Check that usernames appear alphabetically per group // Check that usernames appear alphabetically per group
_.each(groups, function (name) { _.each(groups, function (name) {
var $contacts = _converse.rosterview.$('.roster-group[data-group="'+name+'"] li'); var $contacts = _converse.rosterview.$('.roster-group[data-group="'+name+'"] li');
@ -954,8 +953,7 @@
_addContacts(_converse); _addContacts(_converse);
test_utils.waitUntil(function () { test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('.roster-group').length; return _converse.rosterview.$el.find('.roster-group').length;
}, 500) }, 500).then(function () {
.then(function () {
var i, jid; var i, jid;
for (i=0; i<3; i++) { for (i=0; i<3; i++) {
jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost'; jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -977,7 +975,15 @@
jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost'; jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
_converse.roster.get(jid).set('chat_status', 'unavailable'); _converse.roster.get(jid).set('chat_status', 'unavailable');
} }
return test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('li.online').length
})
}).then(function () {
return test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('li:first').text().trim() === 'Candice van der Knijff'
}, 900);
}).then(function () {
var i;
var contacts = _converse.rosterview.$el.find('.current-xmpp-contact'); var contacts = _converse.rosterview.$el.find('.current-xmpp-contact');
for (i=0; i<3; i++) { for (i=0; i<3; i++) {
expect($(contacts[i]).hasClass('online')).toBeTruthy(); expect($(contacts[i]).hasClass('online')).toBeTruthy();

View File

@ -1,20 +1,21 @@
(function (root, factory) { (function (root, factory) {
define(["mock", "converse-core", "test_utils"], factory); define(["jasmine", "mock", "converse-core", "test-utils", "utils"], factory);
} (this, function (mock, converse, test_utils) { } (this, function (jasmine, mock, converse, test_utils, u) {
var _ = converse.env._; var _ = converse.env._;
var $iq = converse.env.$iq; var $iq = converse.env.$iq;
describe("Profiling", function() { describe("Profiling", function() {
afterEach(function () { xit("adds hundreds of contacts to the roster",
converse.user.logout(); mock.initConverseWithPromises(
test_utils.clearBrowserStorage(); null, ['rosterGroupsFetched'], {},
}); function (done, _converse) {
xit("adds hundreds of contacts to the roster", mock.initConverse(function(_converse) {
_converse.roster_groups = false; _converse.roster_groups = false;
expect(this.roster.pluck('jid').length).toBe(0); test_utils.openControlBox();
expect(_converse.roster.pluck('jid').length).toBe(0);
var stanza = $iq({ var stanza = $iq({
to: this.connection.jid, to: _converse.connection.jid,
type: 'result', type: 'result',
id: 'roster_1' id: 'roster_1'
}).c('query', { }).c('query', {
@ -29,16 +30,37 @@
}).c('group').t(group).up().up(); }).c('group').t(group).up().up();
} }
}); });
this.roster.onReceivedFromServer(stanza.tree()); _converse.roster.onReceivedFromServer(stanza.tree());
// expect(this.roster.pluck('jid').length).toBe(400);
return test_utils.waitUntil(function () {
var $group = _converse.rosterview.$el.find('.roster-group')
return $group.length && u.isVisible($group[0]);
}).then(function () {
var count = 0;
_converse.roster.each(function (contact) {
if (count < 10) {
contact.set('chat_status', 'online');
count += 1;
}
});
return test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('li.online').length
})
}).then(done);
})); }));
xit("adds hundreds of contacts to the roster, with roster groups", mock.initConverse(function(_converse) { xit("adds hundreds of contacts to the roster, with roster groups",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
// _converse.show_only_online_users = true; // _converse.show_only_online_users = true;
_converse.roster_groups = true; _converse.roster_groups = true;
expect(this.roster.pluck('jid').length).toBe(0); test_utils.openControlBox();
expect(_converse.roster.pluck('jid').length).toBe(0);
var stanza = $iq({ var stanza = $iq({
to: this.connection.jid, to: _converse.connection.jid,
type: 'result', type: 'result',
id: 'roster_1' id: 'roster_1'
}).c('query', { }).c('query', {
@ -53,8 +75,27 @@
}).c('group').t(group).up().up(); }).c('group').t(group).up().up();
} }
}); });
this.roster.onReceivedFromServer(stanza.tree()); _converse.roster.onReceivedFromServer(stanza.tree());
//expect(this.roster.pluck('jid').length).toBe(400);
return test_utils.waitUntil(function () {
var $group = _converse.rosterview.$el.find('.roster-group')
return $group.length && u.isVisible($group[0]);
}).then(function () {
_.each(['Friends', 'Colleagues', 'Family', 'Acquaintances'], function (group) {
var count = 0;
_converse.roster.each(function (contact) {
if (_.includes(contact.get('groups'), group)) {
if (count < 10) {
contact.set('chat_status', 'online');
count += 1;
}
}
});
});
return test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('li.online').length
})
}).then(done);
})); }));
}); });
})); }));

View File

@ -109,7 +109,10 @@
this.content = this.$content[0]; this.content = this.$content[0];
utils.refreshWebkit(); utils.refreshWebkit();
return this; return this;
} },
// Override to avoid the method in converse-chatview.js
'afterShown': _.noop
}); });
function onHeadlineMessage (message) { function onHeadlineMessage (message) {

View File

@ -678,13 +678,11 @@
}, },
initialize () { initialize () {
this.sortEventually = _.debounce(this.sortAndPositionAll, 500);
this.model.contacts.on("add", this.onContactAdded, this); this.model.contacts.on("add", this.onContactAdded, this);
this.model.contacts.on("change:subscription", this.onContactSubscriptionChange, this); this.model.contacts.on("change:subscription", this.onContactSubscriptionChange, this);
this.model.contacts.on("change:requesting", this.onContactRequestChange, this); this.model.contacts.on("change:requesting", this.onContactRequestChange, this);
this.model.contacts.on("change:chat_status", function (contact) { this.model.contacts.on("change:chat_status", this.sortEventually, this);
this.model.contacts.sort();
this.positionContact(contact).render();
}, this);
this.model.contacts.on("destroy", this.onRemove, this); this.model.contacts.on("destroy", this.onRemove, this);
this.model.contacts.on("remove", this.onRemove, this); this.model.contacts.on("remove", this.onRemove, this);
_converse.roster.on('change:groups', this.onContactGroupChange, this); _converse.roster.on('change:groups', this.onContactGroupChange, this);
@ -702,10 +700,15 @@
return this; return this;
}, },
onContactAdded (contact) { createContactView (contact) {
let contact_view = new _converse.RosterContactView({model: contact}); const contact_view = new _converse.RosterContactView({model: contact});
this.add(contact.get('id'), contact_view); this.add(contact.get('id'), contact_view);
contact_view = this.positionContact(contact).render(); contact_view.render();
return contact_view;
},
onContactAdded (contact) {
const contact_view = this.positionContact(contact);
if (contact_view.mayBeShown()) { if (contact_view.mayBeShown()) {
if (this.model.get('state') === _converse.CLOSED) { if (this.model.get('state') === _converse.CLOSED) {
u.hideElement(contact_view.el); u.hideElement(contact_view.el);
@ -721,8 +724,7 @@
/* Place the contact's DOM element in the correct alphabetical /* Place the contact's DOM element in the correct alphabetical
* position amongst the other contacts in this group. * position amongst the other contacts in this group.
*/ */
const view = this.get(contact.get('id')); const view = this.get(contact.get('id')) || this.createContactView(contact);
view.render();
const list = this.contacts_el; const list = this.contacts_el;
const index = this.model.contacts.indexOf(contact); const index = this.model.contacts.indexOf(contact);
if (index === 0) { if (index === 0) {
@ -736,6 +738,11 @@
return view; return view;
}, },
sortAndPositionAll () {
this.model.contacts.sort();
this.model.contacts.each(this.positionContact.bind(this));
},
show () { show () {
u.showElement(this.el); u.showElement(this.el);
_.each(this.getAll(), (contact_view) => { _.each(this.getAll(), (contact_view) => {

View File

@ -30,8 +30,8 @@ require.config(config);
var specs = [ var specs = [
//"spec/transcripts", //"spec/transcripts",
// "spec/profiling",
"jasmine", "jasmine",
"spec/profiling",
"spec/utils", "spec/utils",
"spec/converse", "spec/converse",
"spec/bookmarks", "spec/bookmarks",