Forráskód Böngészése

Change `api.disco.supports` to resolve to a Boolean

Also add a new API method `api.disco.features.get` for the use-case
where you still want the feature object to be returned.
JC Brand 6 éve
szülő
commit
2526d80464

+ 3 - 0
CHANGES.md

@@ -11,6 +11,7 @@
 - Message deduplication bugfixes and improvements
 - Message deduplication bugfixes and improvements
 - Continuously retry (in 2s intervals) to fetch login credentials (via [credentials_url](https://conversejs.org/docs/html/configuration.html#credentials-url)) in case of failure
 - Continuously retry (in 2s intervals) to fetch login credentials (via [credentials_url](https://conversejs.org/docs/html/configuration.html#credentials-url)) in case of failure
 - Replace `moment` with [DayJS](https://github.com/iamkun/dayjs).
 - Replace `moment` with [DayJS](https://github.com/iamkun/dayjs).
+- New API method [\_converse.api.disco.features.get](https://conversejs.org/docs/html/api/-_converse.api.disco.features.html#.get)
 - #1296: `embedded` view mode shows `chatbox-navback` arrow in header
 - #1296: `embedded` view mode shows `chatbox-navback` arrow in header
 - #1532: Converse reloads on enter pressed in the filter box
 - #1532: Converse reloads on enter pressed in the filter box
 - #1550: Legitimate carbons being blocked due to erroneous forgery check
 - #1550: Legitimate carbons being blocked due to erroneous forgery check
@@ -19,6 +20,8 @@
 - **Breaking changes**:
 - **Breaking changes**:
 - Rename `muc_disable_moderator_commands` to [muc_disable_slash_commands](https://conversejs.org/docs/html/configuration.html#muc-disable-slash-commands).
 - Rename `muc_disable_moderator_commands` to [muc_disable_slash_commands](https://conversejs.org/docs/html/configuration.html#muc-disable-slash-commands).
 - `_converse.api.archive.query` now returns a Promise instead of accepting a callback functions.
 - `_converse.api.archive.query` now returns a Promise instead of accepting a callback functions.
+- `_converse.api.disco.supports` now returns a Promise which resolves to a Boolean instead of an Array.
+
 
 
 ### API changes
 ### API changes
 
 

+ 65 - 59
dist/converse.js

@@ -41350,7 +41350,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
       onBookmarksReceivedError(deferred, iq) {
       onBookmarksReceivedError(deferred, iq) {
         window.sessionStorage.setItem(this.fetched_flag, true);
         window.sessionStorage.setItem(this.fetched_flag, true);
 
 
-        _converse.log('Error while fetching bookmarks', Strophe.LogLevel.WARN);
+        _converse.log('Error while fetching bookmarks', Strophe.LogLevel.ERROR);
 
 
         _converse.log(iq.outerHTML, Strophe.LogLevel.DEBUG);
         _converse.log(iq.outerHTML, Strophe.LogLevel.DEBUG);
 
 
@@ -41572,8 +41572,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
       if (_converse.allow_public_bookmarks) {
       if (_converse.allow_public_bookmarks) {
         return !!identity;
         return !!identity;
       } else {
       } else {
-        const supported = await _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', _converse.bare_jid);
-        return !!supported.length;
+        return _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', _converse.bare_jid);
       }
       }
     };
     };
 
 
@@ -42354,9 +42353,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
       },
       },
 
 
       async addFileUploadButton(options) {
       async addFileUploadButton(options) {
-        const result = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain);
-
-        if (result.length) {
+        if (await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain)) {
           this.el.querySelector('.chat-toolbar').insertAdjacentHTML('beforeend', templates_toolbar_fileupload_html__WEBPACK_IMPORTED_MODULE_19___default()({
           this.el.querySelector('.chat-toolbar').insertAdjacentHTML('beforeend', templates_toolbar_fileupload_html__WEBPACK_IMPORTED_MODULE_19___default()({
             'tooltip_upload_file': __('Choose a file to send')
             'tooltip_upload_file': __('Choose a file to send')
           }));
           }));
@@ -42378,9 +42375,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
           return;
           return;
         }
         }
 
 
-        const results = await Promise.all(this.model.presence.resources.map(res => _converse.api.disco.supports(Strophe.NS.SPOILER, `${contact_jid}/${res.get('name')}`)));
+        const results = await Promise.all(this.model.presence.resources.map(r => _converse.api.disco.supports(Strophe.NS.SPOILER, `${contact_jid}/${r.get('name')}`)));
+        const all_resources_support_spolers = results.reduce((acc, val) => acc && val, true);
 
 
-        if (_.filter(results, 'length').length) {
+        if (all_resources_support_spolers) {
           const html = templates_spoiler_button_html__WEBPACK_IMPORTED_MODULE_16___default()(this.model.toJSON());
           const html = templates_spoiler_button_html__WEBPACK_IMPORTED_MODULE_16___default()(this.model.toJSON());
 
 
           if (_converse.visible_toolbar_buttons.emoji) {
           if (_converse.visible_toolbar_buttons.emoji) {
@@ -44972,8 +44970,8 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
           return;
           return;
         }
         }
 
 
-        const _converse = this.__super__._converse,
-              most_recent_msg = u.getMostRecentMessage(this.model);
+        const _converse = this.__super__._converse;
+        const most_recent_msg = u.getMostRecentMessage(this.model);
 
 
         if (_.isNil(most_recent_msg)) {
         if (_.isNil(most_recent_msg)) {
           this.fetchArchivedMessages();
           this.fetchArchivedMessages();
@@ -45037,9 +45035,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
           message_handler = _converse.chatboxes.onMessage.bind(_converse.chatboxes);
           message_handler = _converse.chatboxes.onMessage.bind(_converse.chatboxes);
         }
         }
 
 
-        const supported = await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid);
-
-        if (!supported.length) {
+        if (!(await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid))) {
           return;
           return;
         }
         }
 
 
@@ -51046,9 +51042,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
         return;
         return;
       }
       }
 
 
-      const result = await _converse.api.disco.supports(Strophe.NS.PUSH, domain || _converse.bare_jid);
-
-      if (!result.length) {
+      if (!(await _converse.api.disco.supports(Strophe.NS.PUSH, domain || _converse.bare_jid))) {
         return _converse.log(`Not disabling push app server "${push_app_server.jid}", no disco support from your server.`, Strophe.LogLevel.WARN);
         return _converse.log(`Not disabling push app server "${push_app_server.jid}", no disco support from your server.`, Strophe.LogLevel.WARN);
       }
       }
 
 
@@ -51093,7 +51087,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
 
 
       const result = await Promise.all([_converse.api.disco.supports(Strophe.NS.PUSH, push_app_server.jid), _converse.api.disco.supports(Strophe.NS.PUSH, domain)]);
       const result = await Promise.all([_converse.api.disco.supports(Strophe.NS.PUSH, push_app_server.jid), _converse.api.disco.supports(Strophe.NS.PUSH, domain)]);
 
 
-      if (!result[0].length && !result[1].length) {
+      if (!result[0] && !result[1]) {
         return _converse.log(`Not enabling push app server "${push_app_server.jid}", no disco support from your server.`, Strophe.LogLevel.WARN);
         return _converse.log(`Not enabling push app server "${push_app_server.jid}", no disco support from your server.`, Strophe.LogLevel.WARN);
       }
       }
 
 
@@ -55346,9 +55340,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
         }
         }
 
 
         const by_jid = stanza_id.getAttribute('by');
         const by_jid = stanza_id.getAttribute('by');
-        const result = await _converse.api.disco.supports(Strophe.NS.SID, by_jid);
 
 
-        if (!result.length) {
+        if (!(await _converse.api.disco.supports(Strophe.NS.SID, by_jid))) {
           return false;
           return false;
         }
         }
 
 
@@ -55634,8 +55627,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
       },
       },
 
 
       async sendFiles(files) {
       async sendFiles(files) {
-        const result = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain),
-              item = result.pop();
+        const result = await _converse.api.disco.features.get(Strophe.NS.HTTPUPLOAD, _converse.domain);
+        const item = result.pop();
 
 
         if (!item) {
         if (!item) {
           this.messages.create({
           this.messages.create({
@@ -59057,6 +59050,45 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-dis
 
 
         },
         },
 
 
+        /**
+         * @namespace _converse.api.disco.features
+         * @memberOf _converse.api.disco
+         */
+        'features': {
+          /**
+           * Return a given feature of a disco entity
+           *
+           * @method _converse.api.disco.features.get
+           * @param {string} feature The feature that might be
+           *     supported. In the XML stanza, this is the `var`
+           *     attribute of the `<feature>` element. For
+           *     example: `http://jabber.org/protocol/muc`
+           * @param {string} jid The JID of the entity
+           *     (and its associated items) which should be queried
+           * @returns {promise} A promise which resolves with a list containing
+           *     _converse.Entity instances representing the entity
+           *     itself or those items associated with the entity if
+           *     they support the given feature.
+           * @example
+           * _converse.api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
+           */
+          async 'get'(feature, jid) {
+            if (_.isNil(jid)) {
+              throw new TypeError('You need to provide an entity JID');
+            }
+
+            await _converse.api.waitUntil('discoInitialized');
+            let entity = await _converse.api.disco.entities.get(jid, true);
+            entity = await entity.waitUntilFeaturesDiscovered;
+
+            const promises = _.concat(entity.items.map(item => item.hasFeature(feature)), entity.hasFeature(feature));
+
+            const result = await Promise.all(promises);
+            return f.filter(f.isObject, result);
+          }
+
+        },
+
         /**
         /**
          * Used to determine whether an entity supports a given feature.
          * Used to determine whether an entity supports a given feature.
          *
          *
@@ -59067,40 +59099,17 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-dis
          *     example: `http://jabber.org/protocol/muc`
          *     example: `http://jabber.org/protocol/muc`
          * @param {string} jid The JID of the entity
          * @param {string} jid The JID of the entity
          *     (and its associated items) which should be queried
          *     (and its associated items) which should be queried
-         * @returns {promise} A promise which resolves with a list containing
-         *     _converse.Entity instances representing the entity
-         *     itself or those items associated with the entity if
-         *     they support the given feature.
-         *
+         * @returns {promise} A promise which resolves with `true` or `false`.
          * @example
          * @example
-         * _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)
-         * .then(value => {
-         *     // `value` is a map with two keys, `supported` and `feature`.
-         *     if (value.supported) {
-         *         // The feature is supported
-         *     } else {
-         *         // The feature is not supported
-         *     }
-         * }).catch(() => {
-         *     _converse.log(
-         *         "Error or timeout while checking for feature support",
-         *         Strophe.LogLevel.ERROR
-         *     );
-         * });
+         * if (await _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)) {
+         *     // The feature is supported
+         * } else {
+         *     // The feature is not supported
+         * }
          */
          */
         async 'supports'(feature, jid) {
         async 'supports'(feature, jid) {
-          if (_.isNil(jid)) {
-            throw new TypeError('api.disco.supports: You need to provide an entity JID');
-          }
-
-          await _converse.api.waitUntil('discoInitialized');
-          let entity = await _converse.api.disco.entities.get(jid, true);
-          entity = await entity.waitUntilFeaturesDiscovered;
-
-          const promises = _.concat(entity.items.map(item => item.hasFeature(feature)), entity.hasFeature(feature));
-
-          const result = await Promise.all(promises);
-          return f.filter(f.isObject, result);
+          const features = await _converse.api.disco.features.get(feature, jid);
+          return features.length > 0;
         },
         },
 
 
         /**
         /**
@@ -59271,7 +59280,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
         const by_jid = stanza.getAttribute('from') || this.get('jid');
         const by_jid = stanza.getAttribute('from') || this.get('jid');
         const supported = await _converse.api.disco.supports(Strophe.NS.MAM, by_jid);
         const supported = await _converse.api.disco.supports(Strophe.NS.MAM, by_jid);
 
 
-        if (!supported.length) {
+        if (!supported) {
           return null;
           return null;
         }
         }
 
 
@@ -59594,7 +59603,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
           const jid = attrs.to || _converse.bare_jid;
           const jid = attrs.to || _converse.bare_jid;
           const supported = await _converse.api.disco.supports(Strophe.NS.MAM, jid);
           const supported = await _converse.api.disco.supports(Strophe.NS.MAM, jid);
 
 
-          if (!supported.length) {
+          if (!supported) {
             _converse.log(`Did not fetch MAM archive for ${jid} because it doesn't support ${Strophe.NS.MAM}`);
             _converse.log(`Did not fetch MAM archive for ${jid} because it doesn't support ${Strophe.NS.MAM}`);
 
 
             return {
             return {
@@ -59961,9 +59970,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc
 
 
       async onConnectionStatusChanged() {
       async onConnectionStatusChanged() {
         if (this.get('connection_status') === _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.ENTERED && _converse.auto_register_muc_nickname && !this.get('reserved_nick')) {
         if (this.get('connection_status') === _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.ENTERED && _converse.auto_register_muc_nickname && !this.get('reserved_nick')) {
-          const result = await _converse.api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid'));
-
-          if (result.length) {
+          if (await _converse.api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid'))) {
             this.registerNickname();
             this.registerNickname();
           }
           }
         }
         }
@@ -61825,9 +61832,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins.add('converse-pub
 
 
           if (options) {
           if (options) {
             jid = jid || _converse.bare_jid;
             jid = jid || _converse.bare_jid;
-            const result = await _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', jid);
 
 
-            if (result.length) {
+            if (await _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', jid)) {
               stanza.c('publish-options').c('x', {
               stanza.c('publish-options').c('x', {
                 'xmlns': Strophe.NS.XFORM,
                 'xmlns': Strophe.NS.XFORM,
                 'type': 'submit'
                 'type': 'submit'

+ 12 - 14
spec/http-file-upload.js

@@ -57,7 +57,7 @@
                         'var': 'http://jabber.org/protocol/disco#items'});
                         'var': 'http://jabber.org/protocol/disco#items'});
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
 
 
-                const entities = await _converse.api.disco.entities.get();
+                let entities = await _converse.api.disco.entities.get();
                 expect(entities.length).toBe(2);
                 expect(entities.length).toBe(2);
                 expect(_.includes(entities.pluck('jid'), 'localhost')).toBe(true);
                 expect(_.includes(entities.pluck('jid'), 'localhost')).toBe(true);
                 expect(_.includes(entities.pluck('jid'), 'dummy@localhost')).toBe(true);
                 expect(_.includes(entities.pluck('jid'), 'dummy@localhost')).toBe(true);
@@ -85,7 +85,7 @@
                 stanza = _.find(IQ_stanzas, function (iq) {
                 stanza = _.find(IQ_stanzas, function (iq) {
                     return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#items"]');
                     return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#items"]');
                 });
                 });
-                var items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
+                const items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
                 stanza = $iq({
                 stanza = $iq({
                     'type': 'result',
                     'type': 'result',
                     'from': 'localhost',
                     'from': 'localhost',
@@ -155,17 +155,15 @@
                                 .c('value').t('5242880');
                                 .c('value').t('5242880');
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
 
 
-                _converse.api.disco.entities.get().then(function (entities) {
-                    expect(entities.get('localhost').items.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1);
-                    _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain).then(
-                        function (result) {
-                            expect(result.length).toBe(1);
-                            expect(result[0].get('jid')).toBe('upload.localhost');
-                            expect(result[0].dataforms.where({'FORM_TYPE': {value: "urn:xmpp:http:upload:0", type: "hidden"}}).length).toBe(1);
-                            done();
-                        }
-                    );
-                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
+                entities = await _converse.api.disco.entities.get();
+                expect(entities.get('localhost').items.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1);
+                const supported = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain);
+                expect(supported).toBe(true);
+                const features = await _converse.api.disco.features.get(Strophe.NS.HTTPUPLOAD, _converse.domain);
+                expect(features.length).toBe(1);
+                expect(features[0].get('jid')).toBe('upload.localhost');
+                expect(features[0].dataforms.where({'FORM_TYPE': {value: "urn:xmpp:http:upload:0", type: "hidden"}}).length).toBe(1);
+                done();
             }));
             }));
         });
         });
 
 
@@ -553,7 +551,7 @@
                         _converse.connection._dataRecv(test_utils.createRequest(stanza));
                         _converse.connection._dataRecv(test_utils.createRequest(stanza));
                         entities = await _converse.api.disco.entities.get();
                         entities = await _converse.api.disco.entities.get();
                         expect(entities.get('localhost').items.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1);
                         expect(entities.get('localhost').items.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1);
-                        const result = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain);
+                        await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain);
                         test_utils.createContacts(_converse, 'current');
                         test_utils.createContacts(_converse, 'current');
                         _converse.api.trigger('rosterContactsFetched');
                         _converse.api.trigger('rosterContactsFetched');
 
 

+ 2 - 3
src/converse-bookmarks.js

@@ -356,7 +356,7 @@ converse.plugins.add('converse-bookmarks', {
 
 
             onBookmarksReceivedError (deferred, iq) {
             onBookmarksReceivedError (deferred, iq) {
                 window.sessionStorage.setItem(this.fetched_flag, true);
                 window.sessionStorage.setItem(this.fetched_flag, true);
-                _converse.log('Error while fetching bookmarks', Strophe.LogLevel.WARN);
+                _converse.log('Error while fetching bookmarks', Strophe.LogLevel.ERROR);
                 _converse.log(iq.outerHTML, Strophe.LogLevel.DEBUG);
                 _converse.log(iq.outerHTML, Strophe.LogLevel.DEBUG);
                 if (!_.isNil(deferred)) {
                 if (!_.isNil(deferred)) {
                     if (iq.querySelector('error[type="cancel"] item-not-found')) {
                     if (iq.querySelector('error[type="cancel"] item-not-found')) {
@@ -552,8 +552,7 @@ converse.plugins.add('converse-bookmarks', {
             if (_converse.allow_public_bookmarks) {
             if (_converse.allow_public_bookmarks) {
                 return !!identity;
                 return !!identity;
             } else {
             } else {
-                const supported = await _converse.api.disco.supports(Strophe.NS.PUBSUB+'#publish-options', _converse.bare_jid);
-                return !!supported.length;
+                return _converse.api.disco.supports(Strophe.NS.PUBSUB+'#publish-options', _converse.bare_jid);
             }
             }
         }
         }
 
 

+ 4 - 4
src/converse-chatview.js

@@ -461,8 +461,7 @@ converse.plugins.add('converse-chatview', {
             },
             },
 
 
             async addFileUploadButton (options) {
             async addFileUploadButton (options) {
-                const result = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain);
-                if (result.length) {
+                if (await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain)) {
                     this.el.querySelector('.chat-toolbar').insertAdjacentHTML(
                     this.el.querySelector('.chat-toolbar').insertAdjacentHTML(
                         'beforeend',
                         'beforeend',
                         tpl_toolbar_fileupload({'tooltip_upload_file': __('Choose a file to send')}));
                         tpl_toolbar_fileupload({'tooltip_upload_file': __('Choose a file to send')}));
@@ -483,10 +482,11 @@ converse.plugins.add('converse-chatview', {
                 }
                 }
                 const results = await Promise.all(
                 const results = await Promise.all(
                     this.model.presence.resources.map(
                     this.model.presence.resources.map(
-                        res => _converse.api.disco.supports(Strophe.NS.SPOILER, `${contact_jid}/${res.get('name')}`)
+                        r => _converse.api.disco.supports(Strophe.NS.SPOILER, `${contact_jid}/${r.get('name')}`)
                     )
                     )
                 );
                 );
-                if (_.filter(results, 'length').length) {
+                const all_resources_support_spolers = results.reduce((acc, val) => (acc && val), true);
+                if (all_resources_support_spolers) {
                     const html = tpl_spoiler_button(this.model.toJSON());
                     const html = tpl_spoiler_button(this.model.toJSON());
                     if (_converse.visible_toolbar_buttons.emoji) {
                     if (_converse.visible_toolbar_buttons.emoji) {
                         this.el.querySelector('.toggle-smiley').insertAdjacentHTML('afterEnd', html);
                         this.el.querySelector('.toggle-smiley').insertAdjacentHTML('afterEnd', html);

+ 6 - 5
src/converse-mam-views.js

@@ -39,9 +39,11 @@ converse.plugins.add('converse-mam-views', {
                 /* Fetches messages that might have been archived *after*
                 /* Fetches messages that might have been archived *after*
                  * the last archived message in our local cache.
                  * the last archived message in our local cache.
                  */
                  */
-                if (this.disable_mam) { return; }
-                const { _converse } = this.__super__,
-                      most_recent_msg = u.getMostRecentMessage(this.model);
+                if (this.disable_mam) {
+                    return;
+                }
+                const { _converse } = this.__super__;
+                const most_recent_msg = u.getMostRecentMessage(this.model);
 
 
                 if (_.isNil(most_recent_msg)) {
                 if (_.isNil(most_recent_msg)) {
                     this.fetchArchivedMessages();
                     this.fetchArchivedMessages();
@@ -94,8 +96,7 @@ converse.plugins.add('converse-mam-views', {
                     mam_jid = _converse.bare_jid;
                     mam_jid = _converse.bare_jid;
                     message_handler = _converse.chatboxes.onMessage.bind(_converse.chatboxes)
                     message_handler = _converse.chatboxes.onMessage.bind(_converse.chatboxes)
                 }
                 }
-                const supported = await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid);
-                if (!supported.length) {
+                if (!(await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid))) {
                     return;
                     return;
                 }
                 }
                 this.addSpinner();
                 this.addSpinner();

+ 2 - 3
src/converse-push.js

@@ -34,8 +34,7 @@ converse.plugins.add('converse-push', {
             if (!push_app_server.jid) {
             if (!push_app_server.jid) {
                 return;
                 return;
             }
             }
-            const result = await _converse.api.disco.supports(Strophe.NS.PUSH, domain || _converse.bare_jid)
-            if (!result.length) {
+            if (!(await _converse.api.disco.supports(Strophe.NS.PUSH, domain || _converse.bare_jid))) {
                 return _converse.log(
                 return _converse.log(
                     `Not disabling push app server "${push_app_server.jid}", no disco support from your server.`,
                     `Not disabling push app server "${push_app_server.jid}", no disco support from your server.`,
                     Strophe.LogLevel.WARN
                     Strophe.LogLevel.WARN
@@ -74,7 +73,7 @@ converse.plugins.add('converse-push', {
                 _converse.api.disco.supports(Strophe.NS.PUSH, push_app_server.jid),
                 _converse.api.disco.supports(Strophe.NS.PUSH, push_app_server.jid),
                 _converse.api.disco.supports(Strophe.NS.PUSH, domain)
                 _converse.api.disco.supports(Strophe.NS.PUSH, domain)
             ]);
             ]);
-            if (!result[0].length && !result[1].length) {
+            if (!result[0] && !result[1]) {
                 return _converse.log(
                 return _converse.log(
                     `Not enabling push app server "${push_app_server.jid}", no disco support from your server.`,
                     `Not enabling push app server "${push_app_server.jid}", no disco support from your server.`,
                     Strophe.LogLevel.WARN
                     Strophe.LogLevel.WARN

+ 3 - 6
src/headless/converse-chatboxes.js

@@ -383,8 +383,7 @@ converse.plugins.add('converse-chatboxes', {
                     return false;
                     return false;
                 }
                 }
                 const by_jid = stanza_id.getAttribute('by');
                 const by_jid = stanza_id.getAttribute('by');
-                const result = await _converse.api.disco.supports(Strophe.NS.SID, by_jid);
-                if (!result.length) {
+                if (!(await _converse.api.disco.supports(Strophe.NS.SID, by_jid))) {
                     return false;
                     return false;
                 }
                 }
                 const query = {};
                 const query = {};
@@ -607,9 +606,8 @@ converse.plugins.add('converse-chatboxes', {
 
 
 
 
             async sendFiles (files) {
             async sendFiles (files) {
-                const result = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain),
-                      item = result.pop();
-
+                const result = await _converse.api.disco.features.get(Strophe.NS.HTTPUPLOAD, _converse.domain);
+                const item = result.pop();
                 if (!item) {
                 if (!item) {
                     this.messages.create({
                     this.messages.create({
                         'message': __("Sorry, looks like file upload is not supported by your server."),
                         'message': __("Sorry, looks like file upload is not supported by your server."),
@@ -617,7 +615,6 @@ converse.plugins.add('converse-chatboxes', {
                     });
                     });
                     return;
                     return;
                 }
                 }
-
                 const data = item.dataforms.where({'FORM_TYPE': {'value': Strophe.NS.HTTPUPLOAD, 'type': "hidden"}}).pop(),
                 const data = item.dataforms.where({'FORM_TYPE': {'value': Strophe.NS.HTTPUPLOAD, 'type': "hidden"}}).pop(),
                       max_file_size = window.parseInt(_.get(data, 'attributes.max-file-size.value')),
                       max_file_size = window.parseInt(_.get(data, 'attributes.max-file-size.value')),
                       slot_request_url = _.get(item, 'id');
                       slot_request_url = _.get(item, 'id');

+ 46 - 31
src/headless/converse-disco.js

@@ -550,6 +550,44 @@ converse.plugins.add('converse-disco', {
                     }
                     }
                 },
                 },
 
 
+                /**
+                 * @namespace _converse.api.disco.features
+                 * @memberOf _converse.api.disco
+                 */
+                'features': {
+                    /**
+                     * Return a given feature of a disco entity
+                     *
+                     * @method _converse.api.disco.features.get
+                     * @param {string} feature The feature that might be
+                     *     supported. In the XML stanza, this is the `var`
+                     *     attribute of the `<feature>` element. For
+                     *     example: `http://jabber.org/protocol/muc`
+                     * @param {string} jid The JID of the entity
+                     *     (and its associated items) which should be queried
+                     * @returns {promise} A promise which resolves with a list containing
+                     *     _converse.Entity instances representing the entity
+                     *     itself or those items associated with the entity if
+                     *     they support the given feature.
+                     * @example
+                     * _converse.api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
+                     */
+                    async 'get' (feature, jid) {
+                        if (_.isNil(jid)) {
+                            throw new TypeError('You need to provide an entity JID');
+                        }
+                        await _converse.api.waitUntil('discoInitialized');
+                        let entity = await _converse.api.disco.entities.get(jid, true);
+                        entity = await entity.waitUntilFeaturesDiscovered;
+                        const promises = _.concat(
+                            entity.items.map(item => item.hasFeature(feature)),
+                            entity.hasFeature(feature)
+                        );
+                        const result = await Promise.all(promises);
+                        return f.filter(f.isObject, result);
+                    },
+                },
+
                 /**
                 /**
                  * Used to determine whether an entity supports a given feature.
                  * Used to determine whether an entity supports a given feature.
                  *
                  *
@@ -560,40 +598,17 @@ converse.plugins.add('converse-disco', {
                  *     example: `http://jabber.org/protocol/muc`
                  *     example: `http://jabber.org/protocol/muc`
                  * @param {string} jid The JID of the entity
                  * @param {string} jid The JID of the entity
                  *     (and its associated items) which should be queried
                  *     (and its associated items) which should be queried
-                 * @returns {promise} A promise which resolves with a list containing
-                 *     _converse.Entity instances representing the entity
-                 *     itself or those items associated with the entity if
-                 *     they support the given feature.
-                 *
+                 * @returns {promise} A promise which resolves with `true` or `false`.
                  * @example
                  * @example
-                 * _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)
-                 * .then(value => {
-                 *     // `value` is a map with two keys, `supported` and `feature`.
-                 *     if (value.supported) {
-                 *         // The feature is supported
-                 *     } else {
-                 *         // The feature is not supported
-                 *     }
-                 * }).catch(() => {
-                 *     _converse.log(
-                 *         "Error or timeout while checking for feature support",
-                 *         Strophe.LogLevel.ERROR
-                 *     );
-                 * });
+                 * if (await _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)) {
+                 *     // The feature is supported
+                 * } else {
+                 *     // The feature is not supported
+                 * }
                  */
                  */
                 async 'supports' (feature, jid) {
                 async 'supports' (feature, jid) {
-                    if (_.isNil(jid)) {
-                        throw new TypeError('api.disco.supports: You need to provide an entity JID');
-                    }
-                    await _converse.api.waitUntil('discoInitialized');
-                    let entity = await _converse.api.disco.entities.get(jid, true);
-                    entity = await entity.waitUntilFeaturesDiscovered;
-                    const promises = _.concat(
-                        entity.items.map(item => item.hasFeature(feature)),
-                        entity.hasFeature(feature)
-                    );
-                    const result = await Promise.all(promises);
-                    return f.filter(f.isObject, result);
+                    const features = await _converse.api.disco.features.get(feature, jid);
+                    return features.length > 0;
                 },
                 },
 
 
                 /**
                 /**

+ 3 - 5
src/headless/converse-mam.js

@@ -41,7 +41,7 @@ converse.plugins.add('converse-mam', {
                 }
                 }
                 const by_jid = stanza.getAttribute('from') || this.get('jid');
                 const by_jid = stanza.getAttribute('from') || this.get('jid');
                 const supported = await _converse.api.disco.supports(Strophe.NS.MAM, by_jid);
                 const supported = await _converse.api.disco.supports(Strophe.NS.MAM, by_jid);
-                if (!supported.length) {
+                if (!supported) {
                     return null;
                     return null;
                 }
                 }
                 const query = {};
                 const query = {};
@@ -341,11 +341,9 @@ converse.plugins.add('converse-mam', {
 
 
                     const jid = attrs.to || _converse.bare_jid;
                     const jid = attrs.to || _converse.bare_jid;
                     const supported = await _converse.api.disco.supports(Strophe.NS.MAM, jid);
                     const supported = await _converse.api.disco.supports(Strophe.NS.MAM, jid);
-                    if (!supported.length) {
+                    if (!supported) {
                         _converse.log(`Did not fetch MAM archive for ${jid} because it doesn't support ${Strophe.NS.MAM}`);
                         _converse.log(`Did not fetch MAM archive for ${jid} because it doesn't support ${Strophe.NS.MAM}`);
-                        return {
-                            'messages': []
-                        };
+                        return {'messages': []};
                     }
                     }
 
 
                     const queryid = _converse.connection.getUniqueId();
                     const queryid = _converse.connection.getUniqueId();

+ 1 - 2
src/headless/converse-muc.js

@@ -236,8 +236,7 @@ converse.plugins.add('converse-muc', {
                         _converse.auto_register_muc_nickname &&
                         _converse.auto_register_muc_nickname &&
                         !this.get('reserved_nick')) {
                         !this.get('reserved_nick')) {
 
 
-                    const result = await _converse.api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid'));
-                    if (result.length) {
+                    if (await _converse.api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid'))) {
                         this.registerNickname()
                         this.registerNickname()
                     }
                     }
                 }
                 }

+ 1 - 2
src/headless/converse-pubsub.js

@@ -59,8 +59,7 @@ converse.plugins.add('converse-pubsub', {
 
 
                     if (options) {
                     if (options) {
                         jid = jid || _converse.bare_jid;
                         jid = jid || _converse.bare_jid;
-                        const result = await _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', jid);
-                        if (result.length) {
+                        if (await _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', jid)) {
                             stanza.c('publish-options')
                             stanza.c('publish-options')
                                 .c('x', {'xmlns': Strophe.NS.XFORM, 'type': 'submit'})
                                 .c('x', {'xmlns': Strophe.NS.XFORM, 'type': 'submit'})
                                     .c('field', {'var': 'FORM_TYPE', 'type': 'hidden'})
                                     .c('field', {'var': 'FORM_TYPE', 'type': 'hidden'})

+ 54 - 42
src/headless/dist/converse-headless.js

@@ -33599,9 +33599,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
         }
         }
 
 
         const by_jid = stanza_id.getAttribute('by');
         const by_jid = stanza_id.getAttribute('by');
-        const result = await _converse.api.disco.supports(Strophe.NS.SID, by_jid);
 
 
-        if (!result.length) {
+        if (!(await _converse.api.disco.supports(Strophe.NS.SID, by_jid))) {
           return false;
           return false;
         }
         }
 
 
@@ -33887,8 +33886,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
       },
       },
 
 
       async sendFiles(files) {
       async sendFiles(files) {
-        const result = await _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain),
-              item = result.pop();
+        const result = await _converse.api.disco.features.get(Strophe.NS.HTTPUPLOAD, _converse.domain);
+        const item = result.pop();
 
 
         if (!item) {
         if (!item) {
           this.messages.create({
           this.messages.create({
@@ -37310,6 +37309,45 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-dis
 
 
         },
         },
 
 
+        /**
+         * @namespace _converse.api.disco.features
+         * @memberOf _converse.api.disco
+         */
+        'features': {
+          /**
+           * Return a given feature of a disco entity
+           *
+           * @method _converse.api.disco.features.get
+           * @param {string} feature The feature that might be
+           *     supported. In the XML stanza, this is the `var`
+           *     attribute of the `<feature>` element. For
+           *     example: `http://jabber.org/protocol/muc`
+           * @param {string} jid The JID of the entity
+           *     (and its associated items) which should be queried
+           * @returns {promise} A promise which resolves with a list containing
+           *     _converse.Entity instances representing the entity
+           *     itself or those items associated with the entity if
+           *     they support the given feature.
+           * @example
+           * _converse.api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
+           */
+          async 'get'(feature, jid) {
+            if (_.isNil(jid)) {
+              throw new TypeError('You need to provide an entity JID');
+            }
+
+            await _converse.api.waitUntil('discoInitialized');
+            let entity = await _converse.api.disco.entities.get(jid, true);
+            entity = await entity.waitUntilFeaturesDiscovered;
+
+            const promises = _.concat(entity.items.map(item => item.hasFeature(feature)), entity.hasFeature(feature));
+
+            const result = await Promise.all(promises);
+            return f.filter(f.isObject, result);
+          }
+
+        },
+
         /**
         /**
          * Used to determine whether an entity supports a given feature.
          * Used to determine whether an entity supports a given feature.
          *
          *
@@ -37320,40 +37358,17 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-dis
          *     example: `http://jabber.org/protocol/muc`
          *     example: `http://jabber.org/protocol/muc`
          * @param {string} jid The JID of the entity
          * @param {string} jid The JID of the entity
          *     (and its associated items) which should be queried
          *     (and its associated items) which should be queried
-         * @returns {promise} A promise which resolves with a list containing
-         *     _converse.Entity instances representing the entity
-         *     itself or those items associated with the entity if
-         *     they support the given feature.
-         *
+         * @returns {promise} A promise which resolves with `true` or `false`.
          * @example
          * @example
-         * _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)
-         * .then(value => {
-         *     // `value` is a map with two keys, `supported` and `feature`.
-         *     if (value.supported) {
-         *         // The feature is supported
-         *     } else {
-         *         // The feature is not supported
-         *     }
-         * }).catch(() => {
-         *     _converse.log(
-         *         "Error or timeout while checking for feature support",
-         *         Strophe.LogLevel.ERROR
-         *     );
-         * });
+         * if (await _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)) {
+         *     // The feature is supported
+         * } else {
+         *     // The feature is not supported
+         * }
          */
          */
         async 'supports'(feature, jid) {
         async 'supports'(feature, jid) {
-          if (_.isNil(jid)) {
-            throw new TypeError('api.disco.supports: You need to provide an entity JID');
-          }
-
-          await _converse.api.waitUntil('discoInitialized');
-          let entity = await _converse.api.disco.entities.get(jid, true);
-          entity = await entity.waitUntilFeaturesDiscovered;
-
-          const promises = _.concat(entity.items.map(item => item.hasFeature(feature)), entity.hasFeature(feature));
-
-          const result = await Promise.all(promises);
-          return f.filter(f.isObject, result);
+          const features = await _converse.api.disco.features.get(feature, jid);
+          return features.length > 0;
         },
         },
 
 
         /**
         /**
@@ -37524,7 +37539,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
         const by_jid = stanza.getAttribute('from') || this.get('jid');
         const by_jid = stanza.getAttribute('from') || this.get('jid');
         const supported = await _converse.api.disco.supports(Strophe.NS.MAM, by_jid);
         const supported = await _converse.api.disco.supports(Strophe.NS.MAM, by_jid);
 
 
-        if (!supported.length) {
+        if (!supported) {
           return null;
           return null;
         }
         }
 
 
@@ -37847,7 +37862,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
           const jid = attrs.to || _converse.bare_jid;
           const jid = attrs.to || _converse.bare_jid;
           const supported = await _converse.api.disco.supports(Strophe.NS.MAM, jid);
           const supported = await _converse.api.disco.supports(Strophe.NS.MAM, jid);
 
 
-          if (!supported.length) {
+          if (!supported) {
             _converse.log(`Did not fetch MAM archive for ${jid} because it doesn't support ${Strophe.NS.MAM}`);
             _converse.log(`Did not fetch MAM archive for ${jid} because it doesn't support ${Strophe.NS.MAM}`);
 
 
             return {
             return {
@@ -38214,9 +38229,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins.add('converse-muc
 
 
       async onConnectionStatusChanged() {
       async onConnectionStatusChanged() {
         if (this.get('connection_status') === _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.ENTERED && _converse.auto_register_muc_nickname && !this.get('reserved_nick')) {
         if (this.get('connection_status') === _converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].ROOMSTATUS.ENTERED && _converse.auto_register_muc_nickname && !this.get('reserved_nick')) {
-          const result = await _converse.api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid'));
-
-          if (result.length) {
+          if (await _converse.api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid'))) {
             this.registerNickname();
             this.registerNickname();
           }
           }
         }
         }
@@ -40078,9 +40091,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_1__["default"].plugins.add('converse-pub
 
 
           if (options) {
           if (options) {
             jid = jid || _converse.bare_jid;
             jid = jid || _converse.bare_jid;
-            const result = await _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', jid);
 
 
-            if (result.length) {
+            if (await _converse.api.disco.supports(Strophe.NS.PUBSUB + '#publish-options', jid)) {
               stanza.c('publish-options').c('x', {
               stanza.c('publish-options').c('x', {
                 'xmlns': Strophe.NS.XFORM,
                 'xmlns': Strophe.NS.XFORM,
                 'type': 'submit'
                 'type': 'submit'