Merge pull request #3 from ichim-david/master

Optimize actions done in RosterView.
This commit is contained in:
JC Brand 2013-02-28 00:31:46 -08:00
commit d7cb92c7af
2 changed files with 106 additions and 65 deletions

View File

@ -209,6 +209,7 @@
if (msgs.length) {
return sjcl.decrypt(hex_sha1(this.get('own_jid')), msgs[msgs.length-1]);
}
return undefined;
},
clearMessages: function (jid) {
@ -560,7 +561,7 @@
'<a href="{{user_profile_url}}" class="user">' +
'<img src="{{portrait_url}}" alt="Avatar of {{fullname}}" class="avatar" />' +
'<div class="chat-title"> {{ fullname }} </div>' +
'</a>' +
'</a>' +
'<p class="user-custom-message"><p/>' +
'</div>' +
'<div class="chat-content"></div>' +
@ -688,7 +689,9 @@
},
room_template: _.template(
'<dd class="available-chatroom">' +
'<a class="open-room" room-jid="{{jid}}" title="Click to open this chatroom" href="#">' +
'<a class="open-room" data-room-jid="{{jid}}"' +
' title="Click to open this chatroom"' +
' href="#">' +
'{{name}}</a></dd>'),
tab_template: _.template('<li><a class="s" href="#chatrooms">Rooms</a></li>'),
@ -738,7 +741,7 @@
ev.preventDefault();
var name, jid;
if (ev.type === 'click') {
jid = $(ev.target).attr('room-jid');
jid = $(ev.target).attr('data-room-jid');
} else {
name = $(ev.target).find('input.new-chatroom-name').val().trim().toLowerCase();
if (name) {
@ -1159,12 +1162,13 @@
view.messageReceived(message);
xmppchat.roster.addResource(partner_jid, resource);
}, this));
return;
return undefined;
} else if (!view.isVisible()) {
this.showChat(partner_jid);
}
view.messageReceived(message);
xmppchat.roster.addResource(partner_jid, resource);
return true;
},
initialize: function () {
@ -1186,7 +1190,7 @@
// Add the roster
xmppchat.roster = new xmppchat.RosterItems();
xmppchat.rosterview = new xmppchat.RosterView({'model':xmppchat.roster}).render();
xmppchat.rosterview = new xmppchat.RosterView({'model':xmppchat.roster});
xmppchat.rosterview.$el.appendTo(controlbox.contactspanel.$el);
// Rebind events (necessary for click events on tabs inserted via the panels)
@ -1224,11 +1228,19 @@
xmppchat.RosterItemView = Backbone.View.extend({
tagName: 'dd',
openChat: function () {
xmppchat.chatboxesview.openChat(this.model.get('jid'));
events: {
"click .accept-xmpp-request": "acceptRequest",
"click .decline-xmpp-request": "declineRequest",
"click .open-chat": "openChat",
"click .remove-xmpp-contact": "removeContact"
},
removeContact: function () {
openChat: function (ev) {
xmppchat.chatboxesview.openChat(this.model.get('jid'));
ev.preventDefault();
},
removeContact: function (ev) {
var that = this;
$("<span></span>").dialog({
title: 'Are you sure you want to remove this contact?',
@ -1247,7 +1259,12 @@
$(this).dialog( "close" );
xmppchat.connection.roster.remove(bare_jid, function (iq) {
xmppchat.connection.roster.unauthorize(bare_jid);
xmppchat.chatboxesview.controlbox.roster.remove(bare_jid);
// TODO inspect if chatboxes ever receives controlbox
if (xmppchat.chatboxesview.controlbox) {
xmppchat.chatboxesview.controlbox.roster.remove(bare_jid);
}
// remove model from view roster
xmppchat.rosterview.model.remove(bare_jid);
});
},
"Cancel": function() {
@ -1255,20 +1272,23 @@
}
}
});
ev.preventDefault();
},
acceptRequest: function () {
acceptRequest: function (ev) {
var jid = this.model.get('jid');
xmppchat.connection.roster.authorize(jid);
xmppchat.connection.roster.add(jid, this.model.get('fullname'), [], function (iq) {
xmppchat.connection.roster.subscribe(jid);
});
ev.preventDefault();
},
declineRequest: function () {
declineRequest: function (ev) {
var that = this;
xmppchat.connection.roster.unauthorize(this.model.get('jid'));
that.trigger('decline-request', that.model);
ev.preventDefault();
},
template: _.template(
@ -1299,29 +1319,12 @@
} else if (ask === 'request') {
this.$el.addClass('requesting-xmpp-contact');
this.$el.html(this.request_template(item.toJSON()));
this.$el.delegate('button.accept-xmpp-request', 'click', function (ev) {
ev.preventDefault();
that.acceptRequest();
});
this.$el.delegate('button.decline-xmpp-request', 'click', function (ev) {
ev.preventDefault();
that.declineRequest();
});
xmppchat.chatboxesview.openChat('controlbox');
} else if (subscription === 'both') {
} else if (subscription === 'both' || subscription === 'to') {
this.$el.addClass('current-xmpp-contact');
this.$el.html(this.template(item.toJSON()));
this.$el.delegate('a.open-chat', 'click', function (ev) {
ev.preventDefault();
that.openChat();
});
}
// Event handlers
this.$el.delegate('a.remove-xmpp-contact','click', function (ev) {
ev.preventDefault();
that.removeContact();
});
return this;
},
@ -1388,8 +1391,9 @@
return Backbone.Collection.prototype.get.call(this, id);
},
addRosterItem: function (jid, subscription, ask, name) {
addRosterItem: function (jid, subscription, ask, name, options) {
var model = new xmppchat.RosterItem(jid, subscription, ask, name);
model.options = options || {};
this.add(model);
},
@ -1437,6 +1441,7 @@
if (item) {
return _.size(item.get('resources'));
}
return 0;
},
getNumOnlineContacts: function () {
@ -1450,14 +1455,23 @@
},
rosterHandler: function (items) {
var model, item;
for (var i=0; i<items.length; i++) {
var model, item, i, items_length = items.length,
last_item = items[items_length - 1],
options = {};
for (i=0; i<items_length; i+=1) {
item = items[i];
model = this.getItem(item.jid);
if (!model) {
this.addRosterItem(item.jid, item.subscription, item.ask, item.name);
if (item === last_item) {
options.isLast = true;
}
this.addRosterItem(item.jid, item.subscription, item.ask, item.name, options);
} else {
model.set({'subscription': item.subscription, 'ask': item.ask});
// only modify model attributes if they are different from the
// ones that were already set when the rosterItem was added
if (model.get('subscription') !== item.subscription || model.get('ask') !== item.ask) {
model.set({'subscription': item.subscription, 'ask': item.ask});
}
}
}
},
@ -1580,38 +1594,40 @@
this.model.remove(item.id);
}, this);
}
this.render();
this.render(item);
}, this);
this.model.on('change', function (item) {
this.render();
this.render(item);
}, this);
this.model.on("remove", function (item) {
// remove element from the rosterView instance
this.rosteritemviews[item.id].$el.remove();
delete this.rosteritemviews[item.id];
this.render();
}, this);
this.$el.hide();
this.$el.html(this.template());
},
template: _.template('<dt id="xmpp-contact-requests">Contact requests</dt>' +
'<dt id="xmpp-contacts">My contacts</dt>' +
'<dt id="pending-xmpp-contacts">Pending contacts</dt>'),
render: function () {
this.$el.empty().html(this.template());
var models = this.model.sort().models,
children = this.$el.children(),
$my_contacts = this.$el.find('#xmpp-contacts').hide(),
$contact_requests = this.$el.find('#xmpp-contact-requests').hide(),
$pending_contacts = this.$el.find('#pending-xmpp-contacts').hide(),
$count, num;
for (var i=0; i<models.length; i++) {
var model = models[i],
user_id = Strophe.getNodeFromJid(model.id),
view = this.rosteritemviews[model.id],
ask = model.get('ask'),
subscription = model.get('subscription'),
render: function (item) {
if (!item) {
return this;
}
var $my_contacts = this.$el.find('#xmpp-contacts'),
$contact_requests = this.$el.find('#xmpp-contact-requests'),
$pending_contacts = this.$el.find('#pending-xmpp-contacts'),
$count, num, presence_change;
var user_id = Strophe.getNodeFromJid(item.id),
view = this.rosteritemviews[item.id],
ask = item.get('ask'),
subscription = item.get('subscription'),
crit = {order:'asc'};
if (ask === 'subscribe') {
@ -1620,20 +1636,46 @@
} else if (ask === 'request') {
$contact_requests.after(view.render().el);
$contact_requests.after($contact_requests.siblings('dd.requesting-xmpp-contact').tsort(crit));
} else if (subscription === 'both') {
$my_contacts.after(view.render().el);
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.offline').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.unavailable').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.away').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.busy').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.online').tsort('a', crit));
} else if (subscription === 'both' || subscription === 'to') {
if (!item.options.sorted) {
// this attribute will be true only after all of the elements have been added on the page
// at this point all offline
$my_contacts.after(view.render().el);
}
else {
// just by calling render will be enough to change the icon of the existing item without
// having to reinsert it and the sort will come from the presence change
view.render();
}
}
presence_change = view.model.changed['presence_type'];
if (presence_change) {
// resort all items only if the model has changed it's presence_type as this render
// is also triggered when the resource is changed which always comes before the presence change
// therefore we avoid resorting when the change doesn't affect the position of the item
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.offline').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.unavailable').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.away').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.busy').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.online').tsort('a', crit));
}
if (item.options.isLast && !item.options.sorted) {
// this will be true after all of the roster items have been added with the default
// options where all of the items are offline and now we can show the rosterView
item.options.sorted = true;
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.offline').tsort('a', crit));
$my_contacts.after($my_contacts.siblings('dd.current-xmpp-contact.unavailable').tsort('a', crit));
this.$el.show();
}
// Hide the headings if there are no contacts under them
_.each([$my_contacts, $contact_requests, $pending_contacts], function (h) {
if (h.nextUntil('dt').length > 0) {
if (h.nextUntil('dt').length) {
h.show();
}
else {
h.hide();
}
});
$count = $('#online-count');
$count.text(this.model.getNumOnlineContacts());
@ -1824,7 +1866,7 @@
$(document).unbind('jarnxmpp.connected');
$(document).bind('jarnxmpp.connected', $.proxy(function (ev, connection) {
this.connection = connection
this.connection = connection;
// this.connection.xmlInput = function (body) { console.log(body); };
// this.connection.xmlOutput = function (body) { console.log(body); };
this.connection.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
@ -1883,4 +1925,3 @@
return xmppchat;
}));

View File

@ -31,12 +31,12 @@
xmppchat.rosterview = new xmppchat.RosterView({'model':xmppchat.roster});
// stub
xmppchat.chatboxesview = {openChat: function () {} };
// Hack to make sure there is an element.
xmppchat.rosterview.$el = $('<dl id="xmppchat-roster"></dl>');
xmppchat.rosterview.render();
// by default the dts are hidden from css class and only later they will be hidden
// by jQuery therefore for the first check we will see if visible instead of none
it("should hide the requesting contacts heading if there aren't any", function () {
expect(xmppchat.rosterview.$el.find('dt#xmpp-contact-requests').css('display')).toEqual('none');
expect(xmppchat.rosterview.$el.find('dt#xmpp-contact-requests').is(':visible')).toEqual(false);
});
it("should be able to add requesting contacts, and they should be sorted alphabetically", function () {