Break the roster up in to current, pending and requesting contacts.

This commit is contained in:
JC Brand 2012-07-15 21:03:34 +02:00
parent 28d71fba34
commit f333968dd9

241
chat.js
View File

@ -459,10 +459,9 @@ xmppchat.ControlBoxView = xmppchat.ChatBoxView.extend({
subscribeToContact: function (ev) { subscribeToContact: function (ev) {
ev.preventDefault(); ev.preventDefault();
var jid = $(ev.target).attr('data-recipient'); var jid = $(ev.target).attr('data-recipient');
xmppchat.roster.stropheRoster.add(jid, '', [], function (iq) { xmppchat.connection.roster.add(jid, '', [], function (iq) {
// XXX: We can set the name here!!! // XXX: We can set the name here!!!
//xmppchat.connection.send($pres({'type':'result', 'id': $(iq).attr('id')})); xmppchat.connection.roster.subscribe(jid);
xmppchat.roster.stropheRoster.subscribe(jid);
}); });
$(ev.target).parent().remove(); $(ev.target).parent().remove();
$('form.search-xmpp-contact').hide(); $('form.search-xmpp-contact').hide();
@ -635,13 +634,14 @@ xmppchat.ChatBoxesView = Backbone.View.extend({
xmppchat.RosterItem = Backbone.Model.extend({ xmppchat.RosterItem = Backbone.Model.extend({
initialize: function (jid, subscription) { initialize: function (jid, subscription, ask) {
// FIXME: the fullname is set to user_id for now... // FIXME: the fullname is set to user_id for now...
var user_id = Strophe.getNodeFromJid(jid); var user_id = Strophe.getNodeFromJid(jid);
this.set({ this.set({
'id': jid, 'id': jid,
'jid': jid, 'jid': jid,
'ask': ask,
'bare_jid': Strophe.getBareJidFromJid(jid), 'bare_jid': Strophe.getBareJidFromJid(jid),
'user_id': user_id, 'user_id': user_id,
'subscription': subscription, 'subscription': subscription,
@ -655,10 +655,12 @@ xmppchat.RosterItem = Backbone.Model.extend({
xmppchat.RosterItemView = Backbone.View.extend({ xmppchat.RosterItemView = Backbone.View.extend({
tagName: 'li', tagName: 'dd',
events: { events: {
'click a.open-chat': 'openChat', 'click a.open-chat': 'openChat',
'click a.remove-xmpp-contact': 'removeContact' 'click a.remove-xmpp-contact': 'removeContact',
'click button.accept-xmpp-request': 'acceptRequest',
'click button.decline-xmpp-request': 'declineRequest'
}, },
openChat: function (e) { openChat: function (e) {
@ -693,6 +695,48 @@ xmppchat.RosterItemView = Backbone.View.extend({
}); });
}, },
acceptRequest: function () {
xmppchat.connection.roster.authorize(this.model.get('jid'));
xmppchat.connection.roster.subscribe(this.model.get('jid'));
},
declineRequest: function () {
var that = this;
xmppchat.connection.roster.unauthorize(this.model.get('jid'));
that.trigger('decline-request', that.model);
},
template: _.template(
'<a class="open-chat" title="Click to chat with this contact" href="#"><%= fullname %></a>' +
'<a class="remove-xmpp-contact" title="Click to remove this contact" href="#"></a>'),
request_template: _.template('<%= fullname %>' +
'<button type="button" class="accept-xmpp-request">' +
'Accept</button>' +
'<button type="button" class="decline-xmpp-request">' +
'Decline</button>' +
''),
render: function () {
var item = this.model,
ask = item.get('ask'),
subscription = item.get('subscription');
$(this.el).addClass(item.get('status')).attr('id', 'online-users-'+item.get('user_id'));
if (ask === 'subscribe') {
this.$el.addClass('pending-xmpp-contact');
$(this.el).html(this.template(item.toJSON()));
} else if (ask === 'request') {
this.$el.addClass('requesting-xmpp-contact');
$(this.el).html(this.request_template(item.toJSON()));
} else if (subscription === 'both') {
this.$el.addClass('current-xmpp-contact');
this.$el.html(this.template(item.toJSON()));
}
return this;
},
initialize: function () { initialize: function () {
var that = this; var that = this;
this.options.model.on('change', function (item, changed) { this.options.model.on('change', function (item, changed) {
@ -700,29 +744,18 @@ xmppchat.RosterItemView = Backbone.View.extend({
$(this.el).attr('class', item.changed.status); $(this.el).attr('class', item.changed.status);
} }
}, this); }, this);
},
template: _.template(
'<a class="open-chat" title="Click to chat with this contact" href="#"><%= fullname %></a>' +
'<a class="remove-xmpp-contact" title="Click to remove this contact" href="#"></a>'),
render: function () {
var item = this.model;
$(this.el).addClass(item.get('status')).attr('id', 'online-users-'+item.get('user_id'));
$(this.el).html(this.template(item.toJSON()));
return this;
} }
}); });
xmppchat.Roster = (function (stropheRoster, _, $, console) { xmppchat.Roster = (function (_, $, console) {
var ob = _.clone(stropheRoster), var ob = {},
Collection = Backbone.Collection.extend({ Collection = Backbone.Collection.extend({
model: xmppchat.RosterItem, model: xmppchat.RosterItem,
stropheRoster: stropheRoster, stropheRoster: xmppchat.connection.roster,
initialize: function () { initialize: function () {
this._connection = this.stropheRoster._connection = xmppchat.connection; this._connection = xmppchat.connection;
}, },
comparator : function (rosteritem) { comparator : function (rosteritem) {
@ -753,15 +786,15 @@ xmppchat.Roster = (function (stropheRoster, _, $, console) {
}, },
getRoster: function () { getRoster: function () {
return stropheRoster.get(); return xmppchat.connection.roster.get();
}, },
getItem: function (id) { getItem: function (id) {
return Backbone.Collection.prototype.get.call(this, id); return Backbone.Collection.prototype.get.call(this, id);
}, },
addRosterItem: function (jid, subscription) { addRosterItem: function (jid, subscription, ask) {
var model = new xmppchat.RosterItem(jid, subscription); var model = new xmppchat.RosterItem(jid, subscription, ask);
this.add(model); this.add(model);
}, },
@ -818,106 +851,26 @@ xmppchat.Roster = (function (stropheRoster, _, $, console) {
} }
return count; return count;
} }
}); });
var collection = new Collection(); var collection = new Collection();
_.extend(ob, collection); _.extend(ob, collection);
_.extend(ob, Backbone.Events); _.extend(ob, Backbone.Events);
ob.updateHandler = function (items) { ob.rosterHandler = function (items) {
var model, item; var model, item;
for (var i=0; i<items.length; i++) { for (var i=0; i<items.length; i++) {
item = items[i]; item = items[i];
if (item.ask === 'subscribe') {
// We are subscribing to them and have to wait for
// authorization
continue;
}
model = ob.getItem(item.jid); model = ob.getItem(item.jid);
if (!model) { if (!model) {
if (item.subscription === 'to') { ob.addRosterItem(item.jid, item.subscription, item.ask);
ob.addRosterItem(item.jid, item.subscription);
}
if (item.subscription === 'both') {
ob.addRosterItem(item.jid, item.subscription);
}
} else { } else {
if (item.subscription === 'none') { model.set({'subscription': item.subscription, 'ask': item.ask});
ob.remove(model);
}
} }
/*
model = ob.getItem(item.jid);
if (model) {
if (model.get('subscription') === 'both') {
if (item.subscription === 'to') {
// Other user unsubscribed, so we unsubscribe as well.
ob.unsubscribe(item.jid);
} else if (item.subscription === 'from') {
// We have just unsubscribed, remove user.
ob.unauthorize(item.jid);
ob.remove(model);
} else if (item.subscription === 'none') {
if (item.ask === 'subscribe') {
// We already authorized it, so must be something
// wrong.
ob.unauthorize(item.jid);
}
ob.remove(model);
} else {
// Update the model
model = ob.getItem(item.jid);
model.subscription = item.subscription;
}
} else {
if (item.subscription === 'none') {
ob.remove(model);
} else {
// Update the model
model = ob.getItem(item.jid);
model.subscription = item.subscription;
}
}
} else {
if (item.subscription === 'from') {
ob.subscribe(item.jid);
} else if (item.subscription === 'both') {
ob.addRosterItem(item.jid, item.subscription);
} else if (item.subscription === 'to') {
ob.addRosterItem(item.jid, item.subscription);
}
}
*/
} }
}; };
ob.promptUserForSubscription = function (jid, approve_callback, decline_callback) {
var user_id = Strophe.getNodeFromJid(jid);
$("<span></span>").dialog({
title: user_id+ ' wants add you as a contact.',
dialogClass: 'approve-xmpp-contact-dialog',
resizable: false,
width: 200,
position: {
my: 'center',
at: 'center',
of: '#online-users-container'
},
modal: true,
buttons: {
"Approve": function() {
$(this).dialog( "close" );
approve_callback(jid);
},
"Decline": function() {
$(this).dialog( "close" );
decline_callback(jid);
}
}
});
};
ob.presenceHandler = function (presence) { ob.presenceHandler = function (presence) {
var jid = $(presence).attr('from'), var jid = $(presence).attr('from'),
bare_jid = Strophe.getBareJidFromJid(jid), bare_jid = Strophe.getBareJidFromJid(jid),
@ -931,22 +884,26 @@ xmppchat.Roster = (function (stropheRoster, _, $, console) {
} }
if (ptype === 'subscribe') { if (ptype === 'subscribe') {
if (ob.getItem(bare_jid)) { if (ob.getItem(bare_jid)) {
xmppchat.roster.authorize(bare_jid); xmppchat.connection.roster.authorize(bare_jid);
} else { } else {
ob.promptUserForSubscription(bare_jid, ob.addRosterItem(bare_jid, 'none', 'request');
function () { // Approved
xmppchat.roster.authorize(bare_jid);
xmppchat.roster.subscribe(bare_jid);
},
function () { // Declined
xmppchat.roster.unauthorize(bare_jid);
});
} }
} else if (ptype === 'subscribed') { } else if (ptype === 'subscribed') {
return true; return true;
} else if (ptype === 'unsubscribe') { } else if (ptype === 'unsubscribe') {
return true; return true;
} else if (ptype === 'unsubscribed') { } else if (ptype === 'unsubscribed') {
/* Upon receiving the presence stanza of type "unsubscribed",
* the user SHOULD acknowledge receipt of that subscription state
* notification by sending a presence stanza of type "unsubscribe"
* this step lets the user's server know that it MUST no longer
* send notification of the subscription state change to the user.
*/
xmppchat.xmppstatus.sendPresence('unsubscribe');
if (xmppchat.connection.roster.findItem(bare_jid)) {
xmppchat.roster.remove(bare_jid);
xmppchat.connection.roster.remove(bare_jid);
}
return true; return true;
} else if (ptype === 'error') { } else if (ptype === 'error') {
return true; return true;
@ -976,7 +933,7 @@ xmppchat.Roster = (function (stropheRoster, _, $, console) {
xmppchat.RosterView= (function (roster, _, $, console) { xmppchat.RosterView= (function (roster, _, $, console) {
var View = Backbone.View.extend({ var View = Backbone.View.extend({
el: $('#xmpp-contacts'), el: $('#xmppchat-roster'),
model: roster, model: roster,
rosteritemviews: {}, rosteritemviews: {},
@ -984,7 +941,12 @@ xmppchat.RosterView= (function (roster, _, $, console) {
this.model.on("add", function (item) { this.model.on("add", function (item) {
var view = new xmppchat.RosterItemView({model: item}); var view = new xmppchat.RosterItemView({model: item});
this.rosteritemviews[item.id] = view; this.rosteritemviews[item.id] = view;
$(this.el).append(view.render().el); if (item.get('ask') === 'request') {
view.on('decline-request', function (item) {
this.model.remove(item.id);
}, this);
}
this.render();
}, this); }, this);
this.model.on('change', function (item) { this.model.on('change', function (item) {
@ -992,22 +954,43 @@ xmppchat.RosterView= (function (roster, _, $, console) {
}, this); }, this);
this.model.on("remove", function (item) { this.model.on("remove", function (item) {
var view = this.rosteritemviews[item.id]; delete this.rosteritemviews[item.id];
if (view) {
view.remove();
}
this.render(); this.render();
}, this); }, this);
}, },
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 () { render: function () {
this.$el.empty().html(this.template());
var models = this.model.sort().models, var models = this.model.sort().models,
children = $(this.el).children(), children = $(this.el).children(),
that = this; 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();
$(models).each(function (idx, model) { for (var i=0; i<models.length; i++) {
var user_id = Strophe.getNodeFromJid(model.id); var model = models[i],
$(that.el).append(children.filter('#online-users-'+user_id)); user_id = Strophe.getNodeFromJid(model.id),
view = this.rosteritemviews[model.id],
ask = model.get('ask'),
subscription = model.get('subscription');
if (ask === 'subscribe') {
pending_contacts.after(view.render().el);
} else if (ask === 'request') {
contact_requests.after(view.render().el);
} else if (subscription === 'both') {
my_contacts.after(view.render().el);
}
}
// 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) {
h.show();
}
}); });
$('#online-count').text(this.model.getNumOnlineContacts()); $('#online-count').text(this.model.getNumOnlineContacts());
} }
@ -1116,12 +1099,12 @@ $(document).ready(function () {
xmppchat.connection.bare_jid = Strophe.getBareJidFromJid(xmppchat.connection.jid); xmppchat.connection.bare_jid = Strophe.getBareJidFromJid(xmppchat.connection.jid);
xmppchat.roster = xmppchat.Roster(Strophe._connectionPlugins.roster, _, $, console); xmppchat.roster = xmppchat.Roster(_, $, console);
xmppchat.rosterview = Backbone.View.extend(xmppchat.RosterView(xmppchat.roster, _, $, console)); xmppchat.rosterview = Backbone.View.extend(xmppchat.RosterView(xmppchat.roster, _, $, console));
xmppchat.connection.addHandler(xmppchat.roster.presenceHandler, null, 'presence', null); xmppchat.connection.addHandler(xmppchat.roster.presenceHandler, null, 'presence', null);
xmppchat.roster.registerCallback(xmppchat.roster.updateHandler); xmppchat.connection.roster.registerCallback(xmppchat.roster.rosterHandler);
xmppchat.roster.getRoster(); xmppchat.roster.getRoster();
xmppchat.chatboxes = new xmppchat.ChatBoxes(); xmppchat.chatboxes = new xmppchat.ChatBoxes();