diff --git a/.eslintrc.json b/.eslintrc.json index 9c2f8092d..f9c59d290 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -59,7 +59,7 @@ "dot-notation": [ "error", { - "allowKeywords": false + "allowKeywords": true } ], "eol-last": "error", diff --git a/CHANGES.md b/CHANGES.md index 8cc69437b..2a050ac52 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 Emojione). [jcbrand] - Add JID validation in the contact add form. [jcbrand] diff --git a/spec/converse.js b/spec/converse.js index 6fbd5b832..bb90c581f 100644 --- a/spec/converse.js +++ b/spec/converse.js @@ -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( "" ); - // 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(); })); }); diff --git a/spec/disco.js b/spec/disco.js index 2aa318a78..fa8146cec 100644 --- a/spec/disco.js +++ b/spec/disco.js @@ -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(); })); }); }); diff --git a/spec/mam.js b/spec/mam.js index 530ab7755..240f45635 100644 --- a/spec/mam.js +++ b/spec/mam.js @@ -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( ""); + 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'; diff --git a/src/config.js b/src/config.js index e958c044f..6623f952f 100644 --- a/src/config.js +++ b/src/config.js @@ -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", diff --git a/src/converse-core.js b/src/converse-core.js index d232fbb55..3901102ee 100755 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -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; }; diff --git a/src/converse-disco.js b/src/converse-disco.js index 3194e334a..645a30a46 100644 --- a/src/converse-disco.js +++ b/src/converse-disco.js @@ -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) { diff --git a/src/converse-mam.js b/src/converse-mam.js index 30fcf9e3d..4549acc6c 100644 --- a/src/converse-mam.js +++ b/src/converse-mam.js @@ -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); + }); } }); })); diff --git a/src/converse-muc.js b/src/converse-muc.js index fa65728ed..8a87fe9d2 100755 --- a/src/converse-muc.js +++ b/src/converse-muc.js @@ -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 diff --git a/src/converse-ping.js b/src/converse-ping.js index a3e1c1a14..9482f46e5 100644 --- a/src/converse-ping.js +++ b/src/converse-ping.js @@ -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); diff --git a/src/converse-vcard.js b/src/converse-vcard.js index 3eca64404..290e7f60f 100644 --- a/src/converse-vcard.js +++ b/src/converse-vcard.js @@ -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); + }); } }); }));