disco: Create new plugin `converse-disco`.

We can now support feature discovery for multiple entities (although we
currently still only query for the user's own XMPP server).
This commit is contained in:
JC Brand 2017-07-21 12:41:16 +02:00
parent 00484280c2
commit 7850c38faa
12 changed files with 142 additions and 199 deletions

View File

@ -59,7 +59,7 @@
"dot-notation": [
"error",
{
"allowKeywords": false
"allowKeywords": true
}
],
"eol-last": "error",

View File

@ -2,6 +2,11 @@
## 3.2.0 (Unreleased)
### New Plugins
- New plugin `converse-disco` which replaces the original support for
[XEP-0030](https://xmpp.org/extensions/xep-0030.html) and which has been
refactored to allow features for multiple entities to be stored. [jcbrand]
### New features and improvements
- Add support for Emojis (either native, or via <a href="https://www.emojione.com/">Emojione</a>). [jcbrand]
- Add JID validation in the contact add form. [jcbrand]

View File

@ -53,7 +53,11 @@
describe("A chat state indication", function () {
it("are sent out when the client becomes or stops being idle", mock.initConverse(function (_converse) {
it("are sent out when the client becomes or stops being idle",
mock.initConverseWithPromises(
null, ['discoInitialized'], {},
function (done, _converse) {
spyOn(_converse, 'sendCSI').and.callThrough();
var sent_stanza;
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
@ -61,7 +65,7 @@
});
var i = 0;
_converse.idle_seconds = 0; // Usually initialized by registerIntervalHandler
_converse.features['urn:xmpp:csi:0'] = true; // Mock that the server supports CSI
_converse.disco_entities.get(_converse.domain).features['urn:xmpp:csi:0'] = true; // Mock that the server supports CSI
_converse.csi_waiting_time = 3; // The relevant config option
while (i <= _converse.csi_waiting_time) {
@ -78,10 +82,10 @@
expect(sent_stanza.toLocaleString()).toBe(
"<active xmlns='urn:xmpp:csi:0'/>"
);
// Reset values
_converse.csi_waiting_time = 0;
_converse.features['urn:xmpp:csi:0'] = false;
_converse.disco_entities.get(_converse.domain).features['urn:xmpp:csi:0'] = false;
done();
}));
});

View File

@ -11,11 +11,16 @@
describe("Service Discovery", function () {
describe("Whenever converse.js discovers a new server feature", function () {
it("emits the serviceDiscovered event", mock.initConverse(function (_converse) {
it("emits the serviceDiscovered event",
mock.initConverseWithPromises(
null, ['discoInitialized'], {},
function (done, _converse) {
sinon.spy(_converse, 'emit');
_converse.features.create({'var': Strophe.NS.MAM});
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
expect(_converse.emit.called).toBe(true);
expect(_converse.emit.args[0][1].get('var')).toBe(Strophe.NS.MAM);
done();
}));
});
});

View File

@ -15,20 +15,25 @@
describe("The archive.query API", function () {
it("can be used to query for all archived messages", mock.initConverse(function (_converse) {
it("can be used to query for all archived messages",
mock.initConverseWithPromises(
null, ['discoInitialized'], {},
function (done, _converse) {
var sent_stanza, IQ_id;
var sendIQ = _converse.connection.sendIQ;
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
_converse.api.archive.query();
var queryid = $(sent_stanza.toString()).find('query').attr('queryid');
expect(sent_stanza.toString()).toBe(
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'><query xmlns='urn:xmpp:mam:2' queryid='"+queryid+"'/></iq>");
done();
}));
it("can be used to query for all messages to/from a particular JID", mock.initConverse(function (_converse) {
@ -38,8 +43,8 @@
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
_converse.api.archive.query({'with':'juliet@capulet.lit'});
var queryid = $(sent_stanza.toString()).find('query').attr('queryid');
@ -66,8 +71,8 @@
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
var start = '2010-06-07T00:00:00Z';
var end = '2010-07-07T13:23:54Z';
@ -97,8 +102,8 @@
}));
it("throws a TypeError if an invalid date is provided", mock.initConverse(function (_converse) {
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
expect(_.partial(_converse.api.archive.query, {'start': 'not a real date'})).toThrow(
new TypeError('archive.query: invalid date provided for: start')
@ -112,8 +117,8 @@
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
var start = '2010-06-07T00:00:00Z';
_converse.api.archive.query({'start': start});
@ -141,8 +146,8 @@
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
var start = '2010-06-07T00:00:00Z';
_converse.api.archive.query({'start': start, 'max':10});
@ -173,8 +178,8 @@
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
var start = '2010-06-07T00:00:00Z';
_converse.api.archive.query({
@ -210,8 +215,8 @@
sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
_converse.api.archive.query({'before': '', 'max':10});
var queryid = $(sent_stanza.toString()).find('query').attr('queryid');
@ -237,8 +242,8 @@
// and pass it in. However, in the callback method an RSM object is
// returned which can be reused for easy paging. This test is
// more for that usecase.
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
var sent_stanza, IQ_id;
var sendIQ = _converse.connection.sendIQ;
@ -247,7 +252,7 @@
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
var rsm = new Strophe.RSM({'max': '10'});
rsm['with'] = 'romeo@montague.lit';
rsm['with'] = 'romeo@montague.lit'; // eslint-disable-line dot-notation
rsm.start = '2010-06-07T00:00:00Z';
_converse.api.archive.query(rsm);
@ -275,8 +280,8 @@
}));
it("accepts a callback function, which it passes the messages and a Strophe.RSM object", mock.initConverse(function (_converse) {
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.features.create({'var': Strophe.NS.MAM});
if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) {
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
}
var sent_stanza, IQ_id;
var sendIQ = _converse.connection.sendIQ;
@ -351,7 +356,7 @@
expect(args[0].length).toBe(2);
expect(args[0][0].outerHTML).toBe(msg1.nodeTree.outerHTML);
expect(args[0][1].outerHTML).toBe(msg2.nodeTree.outerHTML);
expect(args[1]['with']).toBe('romeo@capulet.lit');
expect(args[1]['with']).toBe('romeo@capulet.lit'); // eslint-disable-line dot-notation
expect(args[1].max).toBe('10');
expect(args[1].count).toBe('16');
expect(args[1].first).toBe('23452-4534-1');
@ -376,7 +381,7 @@
'var': Strophe.NS.MAM
});
spyOn(feature, 'save').and.callFake(feature.set); // Save will complain about a url not being set
_converse.features.onFeatureAdded(feature);
_converse.disco_entities.get(_converse.domain).features.onFeatureAdded(feature);
expect(_converse.connection.sendIQ).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
@ -430,7 +435,7 @@
.c('never').up();
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(feature.save).toHaveBeenCalled();
expect(feature.get('preferences')['default']).toBe('never');
expect(feature.get('preferences')['default']).toBe('never'); // eslint-disable-line dot-notation
// Restore
_converse.message_archiving = 'never';

View File

@ -55,6 +55,7 @@ require.config({
"converse-chatview": "src/converse-chatview",
"converse-controlbox": "src/converse-controlbox",
"converse-core": "src/converse-core",
"converse-disco": "src/converse-disco",
"converse-dragresize": "src/converse-dragresize",
"converse-headline": "src/converse-headline",
"converse-inverse": "src/converse-inverse",

View File

@ -16,7 +16,6 @@
"strophe",
"pluggable",
"backbone.noconflict",
"strophe.disco",
"backbone.browserStorage",
"backbone.overview",
], factory);
@ -59,6 +58,7 @@
'converse-chatview',
'converse-controlbox',
'converse-core',
'converse-disco',
'converse-dragresize',
'converse-headline',
'converse-mam',
@ -794,16 +794,11 @@
// If there's no xmppstatus obj, then we were never connected to
// begin with, so we set reconnecting to false.
reconnecting = _.isUndefined(_converse.xmppstatus) ? false : reconnecting;
if (reconnecting) {
_converse.onStatusInitialized(true);
_converse.emit('reconnected');
} else {
// There might be some open chat boxes. We don't
// know whether these boxes are of the same account or not, so we
// close them now.
_converse.chatboxviews.closeAllChatBoxes();
_converse.features = new _converse.Features();
_converse.initStatus()
.then(
_.partial(_converse.onStatusInitialized, false),
@ -1866,88 +1861,6 @@
}
});
this.Features = Backbone.Collection.extend({
/* Service Discovery
* -----------------
* This collection stores Feature Models, representing features
* provided by available XMPP entities (e.g. servers)
* See XEP-0030 for more details: http://xmpp.org/extensions/xep-0030.html
* All features are shown here: http://xmpp.org/registrar/disco-features.html
*/
model: Backbone.Model,
initialize () {
this.addClientIdentities().addClientFeatures();
this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.features${_converse.bare_jid}`)
);
this.on('add', this.onFeatureAdded, this);
this.fetchFeatures();
},
fetchFeatures () {
if (this.browserStorage.records.length === 0) {
// browserStorage is empty, so we've likely never queried this
// domain for features yet
_converse.connection.disco.info(_converse.domain, null, this.onInfo.bind(this));
_converse.connection.disco.items(_converse.domain, null, this.onItems.bind(this));
} else {
this.fetch({add:true});
}
},
onFeatureAdded (feature) {
_converse.emit('serviceDiscovered', feature);
},
addClientIdentities () {
/* See http://xmpp.org/registrar/disco-categories.html
*/
_converse.connection.disco.addIdentity('client', 'web', 'Converse.js');
return this;
},
addClientFeatures () {
/* The strophe.disco.js plugin keeps a list of features which
* it will advertise to any #info queries made to it.
*
* See: http://xmpp.org/extensions/xep-0030.html#info
*/
_converse.connection.disco.addFeature(Strophe.NS.BOSH);
_converse.connection.disco.addFeature(Strophe.NS.CHATSTATES);
_converse.connection.disco.addFeature(Strophe.NS.DISCO_INFO);
_converse.connection.disco.addFeature(Strophe.NS.ROSTERX); // Limited support
if (_converse.message_carbons) {
_converse.connection.disco.addFeature(Strophe.NS.CARBONS);
}
return this;
},
onItems (stanza) {
_.each(stanza.querySelectorAll('query item'), (item) => {
_converse.connection.disco.info(
item.getAttribute('jid'),
null,
this.onInfo.bind(this));
});
},
onInfo (stanza) {
if ((sizzle('identity[category=server][type=im]', stanza).length === 0) &&
(sizzle('identity[category=conference][type=text]', stanza).length === 0)) {
// This isn't an IM server component
return;
}
_.forEach(stanza.querySelectorAll('feature'), (feature) => {
const namespace = feature.getAttribute('var');
this[namespace] = true;
this.create({
'var': namespace,
'from': stanza.getAttribute('from')
});
});
}
});
this.setUpXMLLogging = function () {
Strophe.log = function (level, msg) {
_converse.log(msg, level);
@ -2165,16 +2078,13 @@
/* Remove those views which are only allowed with a valid
* connection.
*/
_converse.emit('beforeTearDown');
this.unregisterPresenceHandler();
if (this.roster) {
this.roster.off().reset(); // Removes roster contacts
}
this.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect.
delete this.chatboxes.browserStorage;
if (this.features) {
this.features.reset();
this.features.browserStorage._clear();
}
this.session.destroy();
window.removeEventListener('click', _converse.onUserActivity);
window.removeEventListener('focus', _converse.onUserActivity);
@ -2182,6 +2092,7 @@
window.removeEventListener('mousemove', _converse.onUserActivity);
window.removeEventListener(unloadevent, _converse.onUserActivity);
window.clearInterval(_converse.everySecondTrigger);
_converse.emit('afterTearDown');
return this;
};

View File

@ -84,7 +84,12 @@
* All features are shown here: http://xmpp.org/registrar/disco-features.html
*/
model: Backbone.Model,
initialize (jid) {
initialize (settings) {
const jid = settings.jid;
if (_.isNil(jid)) {
throw new Error('DiscoEntity must be instantiated with a JID');
}
this.addClientIdentities().addClientFeatures();
this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.features-${jid}`)
@ -158,9 +163,11 @@
}
});
_converse.api.waitUntil('connected').then(() => {
function initializeDisco () {
_converse.disco_entities = new _converse.DiscoEntities();
});
}
_converse.api.listen.on('reconnected', initializeDisco);
_converse.api.listen.on('connected', initializeDisco);
_converse.api.listen.on('beforeTearDown', () => {
if (_converse.disco_entities) {

View File

@ -11,6 +11,7 @@
(function (root, factory) {
define(["jquery.noconflict",
"converse-core",
"converse-disco",
"converse-chatview", // Could be made a soft dependency
"converse-muc", // Could be made a soft dependency
"strophe.rsm"
@ -31,15 +32,6 @@
// relevant objects or classes.
//
// New functions which don't exist yet can also be added.
Features: {
addClientFeatures () {
const { _converse } = this.__super__;
_converse.connection.disco.addFeature(Strophe.NS.MAM);
return this.__super__.addClientFeatures.apply(this, arguments);
}
},
ChatBox: {
getMessageAttributes ($message, $delay, original_stanza) {
const attrs = this.__super__.getMessageAttributes.apply(this, arguments);
@ -60,7 +52,8 @@
afterMessagesFetched () {
const { _converse } = this.__super__;
if (this.disable_mam ||
!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
!_converse.disco_entities.get(_converse.domain)
.features.findWhere({'var': Strophe.NS.MAM})) {
return this.__super__.afterMessagesFetched.apply(this, arguments);
}
if (!this.model.get('mam_initialized') &&
@ -83,7 +76,9 @@
* box, so that they are displayed inside it.
*/
const { _converse } = this.__super__;
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
if (!_converse.disco_entities.get(_converse.domain)
.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.log(
"Attempted to fetch archived messages but this "+
"user's server doesn't support XEP-0313",
@ -167,7 +162,9 @@
* so that they are displayed inside it.
*/
const { _converse } = this.__super__;
if (!_converse.features.findWhere({'var': Strophe.NS.MAM})) {
if (!_converse.disco_entities.get(_converse.domain)
.features.findWhere({'var': Strophe.NS.MAM})) {
_converse.log(
"Attempted to fetch archived messages but this "+
"user's server doesn't support XEP-0313",
@ -237,12 +234,12 @@
const queryid = _converse.connection.getUniqueId();
const attrs = {'type':'set'};
if (!_.isUndefined(options) && options.groupchat) {
if (!options['with']) {
if (!options['with']) { // eslint-disable-line dot-notation
throw new Error(
'You need to specify a "with" value containing '+
'the chat room JID, when querying groupchat messages.');
}
attrs.to = options['with'];
attrs.to = options['with']; // eslint-disable-line dot-notation
}
const stanza = $iq(attrs).c('query', {'xmlns':Strophe.NS.MAM, 'queryid':queryid});
if (!_.isUndefined(options)) {
@ -250,8 +247,9 @@
.c('field', {'var':'FORM_TYPE', 'type': 'hidden'})
.c('value').t(Strophe.NS.MAM).up().up();
if (options['with'] && !options.groupchat) {
stanza.c('field', {'var':'with'}).c('value').t(options['with']).up().up();
if (options['with'] && !options.groupchat) { // eslint-disable-line dot-notation
stanza.c('field', {'var':'with'}).c('value')
.t(options['with']).up().up(); // eslint-disable-line dot-notation
}
_.each(['start', 'end'], function (t) {
if (options[t]) {
@ -352,11 +350,11 @@
}
};
const onFeatureAdded = function (feature) {
/* Event handlers */
_converse.on('serviceDiscovered', (feature) => {
const prefs = feature.get('preferences') || {};
if (feature.get('var') === Strophe.NS.MAM &&
prefs['default'] !== _converse.message_archiving &&
prefs['default'] !== _converse.message_archiving && // eslint-disable-line dot-notation
!_.isUndefined(_converse.message_archiving) ) {
// Ask the server for archiving preferences
_converse.connection.sendIQ(
@ -365,8 +363,11 @@
_.partial(_converse.onMAMError, feature)
);
}
};
_converse.on('serviceDiscovered', onFeatureAdded.bind(_converse.features));
});
_converse.on('addClientFeatures', () => {
_converse.connection.disco.addFeature(Strophe.NS.MAM);
});
}
});
}));

View File

@ -32,7 +32,8 @@
"tpl!room_panel",
"tpl!spinner",
"awesomplete",
"converse-chatview"
"converse-chatview",
"converse-disco"
], factory);
}(this, function (
$,
@ -130,19 +131,6 @@
this.__super__._tearDown.call(this, arguments);
},
Features: {
addClientFeatures () {
const { _converse } = this.__super__;
this.__super__.addClientFeatures.apply(this, arguments);
if (_converse.allow_muc_invitations) {
_converse.connection.disco.addFeature('jabber:x:conference'); // Invites
}
if (_converse.allow_muc) {
_converse.connection.disco.addFeature(Strophe.NS.MUC);
}
}
},
ChatBoxes: {
model (attrs, options) {
const { _converse } = this.__super__;
@ -182,6 +170,33 @@
}
},
featureAdded (feature) {
const { _converse } = this.__super__;
if ((feature.get('var') === Strophe.NS.MUC) && (_converse.allow_muc)) {
this.setMUCDomain(feature.get('from'));
}
},
getMUCDomainFromDisco () {
/* Check whether service discovery for the user's domain
* returned MUC information and use that to automatically
* set the MUC domain for the "Rooms" panel of the
* controlbox.
*/
const { _converse } = this.__super__;
_converse.api.waitUntil('discoInitialized').then(() => {
_converse.api.listen.on('serviceDiscovered', this.featureAdded, this);
// Features could have been added before the controlbox was
// initialized. We're only interested in MUC
const feature = _converse.disco_entities[_converse.domain].features.findWhere({
'var': Strophe.NS.MUC
});
if (feature) {
this.featureAdded(feature);
}
});
},
onConnected () {
const { _converse } = this.__super__;
this.__super__.onConnected.apply(this, arguments);
@ -189,34 +204,20 @@
return;
}
if (_.isUndefined(_converse.muc_domain)) {
_converse.features.off('add', this.featureAdded, this);
_converse.features.on('add', this.featureAdded, this);
// Features could have been added before the controlbox was
// initialized. We're only interested in MUC
const feature = _converse.features.findWhere({
'var': Strophe.NS.MUC
});
if (feature) {
this.featureAdded(feature);
}
this.getMUCDomainFromDisco();
} else {
this.setMUCDomain(_converse.muc_domain);
}
},
setMUCDomain (domain) {
const { _converse } = this.__super__;
_converse.muc_domain = domain;
this.roomspanel.model.save({'muc_domain': domain});
const $server= this.$el.find('input.new-chatroom-server');
if (!$server.is(':focus')) {
$server.val(this.roomspanel.model.get('muc_domain'));
}
},
featureAdded (feature) {
const { _converse } = this.__super__;
if ((feature.get('var') === Strophe.NS.MUC) && (_converse.allow_muc)) {
this.setMUCDomain(feature.get('from'));
}
}
},
@ -1974,7 +1975,7 @@
return false;
} else {
return this.model.messages.where({
'sender': this.model.get('nick'),
'sender': 'me',
'message': this.model.getMessageBody(message)
}).filter(
(msg) => Math.abs(moment(msg.get('time')).diff(moment.unix(ts))) < 2000
@ -2778,7 +2779,17 @@
}
});
function reconnectToChatRooms () {
/* Event handlers */
_converse.on('addClientFeatures', () => {
if (_converse.allow_muc) {
_converse.connection.disco.addFeature(Strophe.NS.MUC);
}
if (_converse.allow_muc_invitations) {
_converse.connection.disco.addFeature('jabber:x:conference'); // Invites
}
});
_converse.on('reconnected', function reconnectToChatRooms () {
/* Upon a reconnection event from converse, join again
* all the open chat rooms.
*/
@ -2790,8 +2801,7 @@
view.fetchMessages();
}
});
}
_converse.on('reconnected', reconnectToChatRooms);
});
function disconnectChatRooms () {
/* When disconnecting, or reconnecting, mark all chat rooms as

View File

@ -34,7 +34,7 @@
// However, some servers don't advertise while still keeping the
// connection option due to pings.
//
// var feature = _converse.features.findWhere({'var': Strophe.NS.PING});
// var feature = _converse.disco_entities[_converse.domain].features.findWhere({'var': Strophe.NS.PING});
_converse.lastStanzaDate = new Date();
if (_.isNil(jid)) {
jid = Strophe.getDomainFromJid(_converse.bare_jid);

View File

@ -21,16 +21,6 @@
//
// New functions which don't exist yet can also be added.
Features: {
addClientFeatures () {
const { _converse } = this.__super__;
this.__super__.addClientFeatures.apply(this, arguments);
if (_converse.use_vcards) {
_converse.connection.disco.addFeature(Strophe.NS.VCARD);
}
}
},
RosterContacts: {
createRequestingContact (presence) {
const { _converse } = this.__super__;
@ -50,7 +40,6 @@
}
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
@ -135,6 +124,13 @@
}
};
/* Event handlers */
_converse.on('addClientFeatures', () => {
if (_converse.use_vcards) {
_converse.connection.disco.addFeature(Strophe.NS.VCARD);
}
});
const updateVCardForChatBox = function (chatbox) {
if (!_converse.use_vcards) { return; }
const jid = chatbox.model.get('jid'),
@ -161,7 +157,6 @@
};
_converse.on('chatBoxInitialized', updateVCardForChatBox);
const onContactAdd = function (contact) {
if (!contact.get('vcard_updated')) {
// This will update the vcard, which triggers a change
@ -173,7 +168,7 @@
_converse.roster.on("add", onContactAdd);
});
const fetchOwnVCard = function () {
_converse.on('statusInitialized', function fetchOwnVCard () {
if (_converse.xmppstatus.get('fullname') === undefined) {
_converse.getVCard(
null, // No 'to' attr when getting one's own vCard
@ -182,8 +177,7 @@
}
);
}
};
_converse.on('statusInitialized', fetchOwnVCard);
});
}
});
}));