Completely remove strophe.roster as a dependency.

This commit is contained in:
JC Brand 2015-04-06 20:55:38 +02:00
parent a4475a5459
commit 330b1434bd
8 changed files with 133 additions and 143 deletions

View File

@ -448,7 +448,7 @@
this.connection.jid,
this.connection.pass,
function (status, condition) {
converse.onConnect(status, condition, true);
converse.onConnStatusChanged(status, condition, true);
},
this.connection.wait,
this.connection.hold,
@ -468,9 +468,8 @@
view.renderLoginPanel();
};
this.onConnect = function (status, condition, reconnect) {
if ((status === Strophe.Status.CONNECTED) ||
(status === Strophe.Status.ATTACHED)) {
this.onConnStatusChanged = function (status, condition, reconnect) {
if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {
if ((typeof reconnect !== 'undefined') && (reconnect)) {
converse.log(status === Strophe.Status.CONNECTED ? 'Reconnected' : 'Reattached');
converse.onReconnected();
@ -3529,6 +3528,17 @@
return this;
},
removeResource: function (resource) {
var resources = item.get('resources');
var idx = _.indexOf(resources, resource);
if (idx !== -1) {
resources.splice(idx, 1);
item.save({'resources': resources});
return resources.length;
}
return 0;
},
removeFromRoster: function (callback) {
/* Instruct the XMPP server to remove this contact from our roster
* Parameters:
@ -3804,20 +3814,6 @@
}
},
removeResource: function (bare_jid, resource) {
var item = this.get(bare_jid), resources, idx;
if (item) {
resources = item.get('resources');
idx = _.indexOf(resources, resource);
if (idx !== -1) {
resources.splice(idx, 1);
item.save({'resources': resources});
return resources.length;
}
}
return 0;
},
subscribeBack: function (bare_jid) {
var contact = this.get(bare_jid);
if (contact instanceof converse.RosterContact) {
@ -3862,65 +3858,45 @@
return count;
},
clearCache: function (items) {
/* The localstorage cache containing roster contacts might contain
* some contacts that aren't actually in our roster anymore. We
* therefore need to remove them now.
*
* TODO: The method is a performance bottleneck.
* Basically we need to chuck out strophe.roster and
* rewrite it with backbone.js and well integrated into
* converse.js. Then we won't need to have this method at all.
onRosterPush: function (iq) {
/* Handle roster updates from the XMPP server.
* See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
* Parameters:
* (XMLElement) IQ - The IQ stanza received from the XMPP server.
*/
_.each(_.difference(this.pluck('jid'), _.pluck(items, 'jid')), $.proxy(function (jid) {
var contact = this.get(jid);
if (contact && !contact.get('requesting')) {
contact.destroy();
var id = iq.getAttribute('id');
var from = iq.getAttribute('from');
if (from && from !== "" && from != converse.bare_jid) {
// Receiving client MUST ignore stanza unless it has no from or from = user's bare JID.
converse.connection.send(
$iq({type: 'error', id: id, from: converse.connection.jid})
.c('error', {'type': 'cancel'})
.c('service-unavailable', {'xmlns': Strophe.NS.ROSTER })
);
return true;
}
}, this));
converse.connection.send($iq({type: 'result', id: id, from: converse.connection.jid}));
$(iq).children('query').find('item').each(function (idx, item) {
this.updateContact(item);
}.bind(this));
return true;
},
rosterHandler: function (items, item) {
converse.emit('roster', items);
this.clearCache(items);
var new_items = item ? [item] : items;
_.each(new_items, function (item, index, items) {
if (this.isSelf(item.jid)) { return; }
var model = this.get(item.jid);
if (!model) {
var is_last = (index === (items.length-1)) ? true : false;
if ((item.subscription === 'none') && (item.ask === null) && !is_last) {
// We're not interested in zombies
// (Hack: except if it's the last item, then we still
// add it so that the roster will be shown).
return;
}
this.create({
ask: item.ask,
fullname: item.name || item.jid,
groups: item.groups,
jid: item.jid,
subscription: item.subscription
}, {sort: false});
} else {
if ((item.subscription === 'none') && (item.ask === null)) {
// This user is no longer in our roster
model.destroy();
} else {
// We only find out about requesting contacts via the
// presence handler, so if we receive a contact
// here, we know they aren't requesting anymore.
// see docs/DEVELOPER.rst
model.save({
subscription: item.subscription,
ask: item.ask,
requesting: null,
groups: item.groups
});
}
}
}, this);
fetchFromServer: function (callback, errback) {
/* Get the roster from the XMPP server */
var iq = $iq({type: 'get', 'id': converse.connection.getUniqueId('roster')})
.c('query', {xmlns: Strophe.NS.ROSTER});
return converse.connection.sendIQ(iq, this.onReceivedFromServer.bind(this));
},
onReceivedFromServer: function (iq) {
/* An IQ stanza containing the roster has been received from
* the XMPP server.
*/
converse.emit('roster', iq);
$(iq).children('query').find('item').each(function (idx, item) {
this.updateContact(item);
}.bind(this));
if (!converse.initial_presence_sent) {
/* Once we've sent out our initial presence stanza, we'll
* start receiving presence stanzas from our contacts.
@ -3933,6 +3909,45 @@
}
},
updateContact: function (item) {
/* Update or create RosterContact models based on items
* received in the IQ from the server.
*/
var jid = item.getAttribute('jid');
if (this.isSelf(jid)) { return; }
var groups = [],
contact = this.get(jid),
ask = item.getAttribute("ask"),
subscription = item.getAttribute("subscription");
$.map(item.getElementsByTagName('group'), function (group) {
groups.push(Strophe.getText(group));
});
if (!contact) {
this.create({
ask: ask,
fullname: item.getAttribute("name") || jid,
groups: groups,
jid: jid,
subscription: subscription
}, {sort: false});
} else {
if ((subscription === 'none') && (ask === null)) {
contact.destroy(); // This user is no longer in our roster
} else {
// We only find out about requesting contacts via the
// presence handler, so if we receive a contact
// here, we know they aren't requesting anymore.
// see docs/DEVELOPER.rst
contact.save({
subscription: subscription,
ask: ask,
requesting: null,
groups: groups
});
}
}
},
createContactFromVCard: function (iq, jid, fullname, img, img_type, url) {
var bare_jid = Strophe.getBareJidFromJid(jid);
this.create({
@ -3977,17 +3992,15 @@
presenceHandler: function (presence) {
var $presence = $(presence),
presence_type = $presence.attr('type');
if (presence_type === 'error') {
return true;
}
var jid = $presence.attr('from'),
presence_type = presence.getAttribute('type');
if (presence_type === 'error') { return true; }
var jid = presence.getAttribute('from'),
bare_jid = Strophe.getBareJidFromJid(jid),
resource = Strophe.getResourceFromJid(jid),
$show = $presence.find('show'),
chat_status = $show.text() || 'online',
status_message = $presence.find('status'),
contact;
contact = this.get(bare_jid);
if (this.isSelf(bare_jid)) {
if ((converse.connection.jid !== jid)&&(presence_type !== 'unavailable')) {
@ -3998,7 +4011,6 @@
} else if (($presence.find('x').attr('xmlns') || '').indexOf(Strophe.NS.MUC) === 0) {
return true; // Ignore MUC
}
contact = this.get(bare_jid);
if (contact && (status_message.text() != contact.get('status'))) {
contact.save({'status': status_message.text()});
}
@ -4009,12 +4021,14 @@
} else if (presence_type === 'unsubscribed') {
this.unsubscribe(bare_jid);
} else if (presence_type === 'unavailable') {
if (this.removeResource(bare_jid, resource) === 0) {
if (contact) {
if (contact.removeResource(bare_jid, resource) === 0) {
chat_status = "offline";
}
if (contact && chat_status) {
if (chat_status) {
contact.save({'chat_status': chat_status});
}
}
} else if (contact) {
// presence_type is undefined
this.addResource(bare_jid, resource);
@ -4277,32 +4291,17 @@
converse.roster.fetch({
add: true,
success: function (collection) {
// XXX: Bit of a hack.
// strophe.roster expects .get to be called for
// every page load so that its "items" attr
// gets populated.
// This is very inefficient for large rosters,
// and we already have the roster cached in
// sessionStorage.
// Therefore we manually populate the "items"
// attr.
// Ideally we should eventually replace
// strophe.roster with something better.
if (collection.length > 0) {
collection.each(function (item) {
converse.connection.roster.items.push({
name : item.get('fullname'),
jid : item.get('jid'),
subscription : item.get('subscription'),
ask : item.get('ask'),
groups : item.get('groups'),
resources : item.get('resources')
});
});
converse.initial_presence_sent = 1;
// XXX: Should we actually send the presence here?
// This means the presence is sent on every
// page load.
converse.xmppstatus.sendPresence();
} else {
converse.connection.roster.get();
// We don't have any roster contacts stored
// in sessionStorage, so lets fetch the
// roster from the XMPP server.
converse.roster.fetchFromServer();
}
}
});
@ -4397,9 +4396,9 @@
},
registerRosterHandler: function () {
// Register handlers that depend on the roster
converse.connection.roster.registerCallback(
$.proxy(converse.roster.rosterHandler, converse.roster)
converse.connection.addHandler(
converse.roster.onRosterPush.bind(converse.roster),
Strophe.NS.ROSTER, 'iq', "set"
);
},
@ -4417,7 +4416,8 @@
t += $(msg).find('item').length*250;
return true;
},
'http://jabber.org/protocol/rosterx', 'message', null);
'http://jabber.org/protocol/rosterx', 'message', null
);
},
registerPresenceHandler: function () {
@ -4897,7 +4897,6 @@
getRegistrationFields: function (req, _callback, raw) {
/* Send an IQ stanza to the XMPP server asking for the
* registration fields.
*
* Parameters:
* (Strophe.Request) req - The current request
* (Function) callback
@ -5031,7 +5030,7 @@
converse.connection.connect(
that.fields.username+'@'+that.domain,
that.fields.password,
converse.onConnect
converse.onConnStatusChanged
);
converse.chatboxviews.get('controlbox')
.switchTab({target: that.$tabs.find('.current')})
@ -5333,7 +5332,7 @@
jid += '/converse.js-' + Math.floor(Math.random()*139749825).toString();
}
}
converse.connection.connect(jid, password, converse.onConnect);
converse.connection.connect(jid, password, converse.onConnStatusChanged);
},
remove: function () {
@ -5432,7 +5431,7 @@
response.jid,
response.sid,
response.rid,
this.onConnect
this.onConnStatusChanged
);
}.bind(this),
error: function (response) {
@ -5470,7 +5469,7 @@
}
if (rid && sid && jid && Strophe.getBareJidFromJid(jid) === Strophe.getBareJidFromJid(this.jid)) {
this.session.save({rid: rid}); // The RID needs to be increased with each request.
this.connection.attach(jid, sid, rid, this.onConnect);
this.connection.attach(jid, sid, rid, this.onConnStatusChanged);
} else if (this.prebind_url) {
this.startNewBOSHSession();
} else {
@ -5481,26 +5480,26 @@
// Non-prebind case.
if (rid && sid && jid) {
this.session.save({rid: rid}); // The RID needs to be increased with each request.
this.connection.attach(jid, sid, rid, this.onConnect);
this.connection.attach(jid, sid, rid, this.onConnStatusChanged);
} else if (this.auto_login) {
if (!this.jid) {
throw new Error("initConnection: If you use auto_login, you also need to provide a jid value");
}
if (this.authentication === ANONYMOUS) {
this.connection.connect(this.jid, null, this.onConnect);
this.connection.connect(this.jid, null, this.onConnStatusChanged);
} else if (this.authentication === LOGIN) {
if (!this.password) {
throw new Error("initConnection: If you use auto_login and "+
"authentication='login' then you also need to provide a password.");
}
this.connection.connect(this.jid, this.password, this.onConnect);
this.connection.connect(this.jid, this.password, this.onConnStatusChanged);
}
}
}
} else if (this.authentication == "prebind") {
// prebind is used without keepalive
if (this.jid && this.sid && this.rid) {
this.connection.attach(this.jid, this.sid, this.rid, this.onConnect);
this.connection.attach(this.jid, this.sid, this.rid, this.onConnStatusChanged);
} else {
throw new Error("initConnection: If you use prebind and not keepalive, "+
"then you MUST supply JID, RID and SID values");
@ -5517,7 +5516,6 @@
if (this.roster) {
this.roster.off().reset(); // Removes roster contacts
}
this.connection.roster._callbacks = []; // Remove all Roster handlers (e.g. rosterHandler)
if (this.rosterview) {
this.rosterview.model.off().reset(); // Removes roster groups
this.rosterview.undelegateEvents().remove();

View File

@ -52,11 +52,8 @@
<script>
require(['converse'], function (converse) {
converse.initialize({
auto_login: true,
allow_registration: false,
allow_logout: false,
authentication: "anonymous",
jid: "opkode.im",
allow_logout: true,
bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes
i18n: locales['en'], // Refer to ./locale/locales.js to see which locales are supported
keepalive: true,

View File

@ -925,7 +925,6 @@
* https://github.com/jcbrand/converse.js/issues/262
*/
this.rosterview.model.reset();
spyOn(this.roster, 'clearCache').andCallThrough();
expect(this.roster.pluck('jid').length).toBe(0);
var stanza = $pres({from: 'data@enterprise/resource', type: 'subscribe'});
@ -956,8 +955,7 @@
name: 'Benvolio',
subscription:'both'
}).c('group').t('Friends');
this.connection.roster._onReceiveRosterSuccess(null, stanza.tree());
expect(this.roster.clearCache).toHaveBeenCalled();
this.roster.onReceivedFromServer(stanza.tree());
expect(_.contains(this.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
}, converse));

View File

@ -12,7 +12,8 @@
describe("Profiling", function() {
beforeEach(function() {
converse.connection.roster.items = [];
test_utils.clearBrowserStorage();
converse.rosterview.model.reset();
converse.connection._changeConnectStatus(Strophe.Status.CONNECTED);
});
@ -36,7 +37,7 @@
}).c('group').t(group).up().up();
}
});
this.connection.roster._onReceiveRosterSuccess(null, stanza.tree());
this.roster.onReceivedFromServer(stanza.tree());
expect(this.roster.clearCache).toHaveBeenCalled();
expect(this.roster.pluck('jid').length).toBe(400);
}, converse));
@ -62,7 +63,7 @@
}).c('group').t(group).up().up();
}
});
this.connection.roster._onReceiveRosterSuccess(null, stanza.tree());
this.roster.onReceivedFromServer(stanza.tree());
expect(this.roster.clearCache).toHaveBeenCalled();
//expect(this.roster.pluck('jid').length).toBe(400);
}, converse));

View File

@ -4,7 +4,6 @@ define("converse-dependencies", [
"otr",
"moment",
"strophe",
"strophe.roster",
"strophe.vcard",
"strophe.disco",
"backbone.browserStorage",

View File

@ -3,7 +3,6 @@ define("converse-dependencies", [
"utils",
"moment",
"strophe",
"strophe.roster",
"strophe.vcard",
"strophe.disco",
"backbone.browserStorage",

View File

@ -3,7 +3,6 @@ define("converse-dependencies", [
"utils",
"moment",
"strophe",
"strophe.roster",
"strophe.vcard",
"strophe.disco",
"bootstrapJS", // XXX: Can be removed, only for https://conversejs.org

View File

@ -5,7 +5,6 @@ define("converse-dependencies", [
"otr",
"moment",
"strophe",
"strophe.roster",
"strophe.vcard",
"strophe.disco",
"bootstrapJS", // XXX: Only for https://conversejs.org