Parcourir la source

Stop using `Collection.extend`

JC Brand il y a 2 ans
Parent
commit
ee5271647f

+ 1 - 1
.eslintrc.json

@@ -37,7 +37,7 @@
         "callback-return": "off",
         "camelcase": "off",
         "capitalized-comments": "off",
-        "class-methods-use-this": "error",
+        "class-methods-use-this": "off",
         "comma-dangle": "off",
         "comma-spacing": "off",
         "comma-style": "off",

+ 1 - 0
package-lock.json

@@ -11332,6 +11332,7 @@
       }
     },
     "src/headless": {
+      "name": "@converse/headless",
       "version": "10.1.5",
       "license": "MPL-2.0",
       "dependencies": {

+ 26 - 15
src/headless/plugins/bookmarks/collection.js

@@ -3,16 +3,24 @@ import Bookmark from './model.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import log from "../../log.js";
+import { Collection } from "@converse/skeletor/src/collection.js";
 import { getOpenPromise } from '@converse/openpromise';
 import { initStorage } from '../../utils/storage.js';
 
 const { Strophe, $iq, sizzle } = converse.env;
 
 
-const Bookmarks = {
+class Bookmarks extends Collection {
 
-    model: Bookmark,
-    comparator: (item) => item.get('name').toLowerCase(),
+    constructor () {
+        super();
+        this.model = Bookmark;
+    }
+
+    // eslint-disable-next-line class-methods-use-this
+    comparator (item) {
+        return item.get('name').toLowerCase();
+    }
 
     async initialize () {
         this.on('add', bm => this.openBookmarkedRoom(bm)
@@ -37,8 +45,9 @@ const Bookmarks = {
          * @example _converse.api.listen.on('bookmarksInitialized', (bookmarks) => { ... });
          */
         api.trigger('bookmarksInitialized', this);
-    },
+    }
 
+    // eslint-disable-next-line class-methods-use-this
     async openBookmarkedRoom (bookmark) {
         if ( api.settings.get('muc_respect_autojoin') && bookmark.get('autojoin')) {
             const groupchat = await api.rooms.create(
@@ -48,7 +57,7 @@ const Bookmarks = {
             groupchat.maybeShow();
         }
         return bookmark;
-    },
+    }
 
     fetchBookmarks () {
         const deferred = getOpenPromise();
@@ -61,12 +70,12 @@ const Bookmarks = {
             this.fetchBookmarksFromServer(deferred);
         }
         return deferred;
-    },
+    }
 
     createBookmark (options) {
         this.create(options);
         this.sendBookmarkStanza().catch(iq => this.onBookmarkError(iq, options));
-    },
+    }
 
     sendBookmarkStanza () {
         const stanza = $iq({
@@ -94,7 +103,7 @@ const Bookmarks = {
                 .c('field', {'var':'pubsub#access_model'})
                     .c('value').t('whitelist');
         return api.sendIQ(stanza);
-    },
+    }
 
     onBookmarkError (iq, options) {
         const { __ } = _converse;
@@ -104,7 +113,7 @@ const Bookmarks = {
             'error', __('Error'), [__("Sorry, something went wrong while trying to save your bookmark.")]
         );
         this.get(options.jid)?.destroy();
-    },
+    }
 
     fetchBookmarksFromServer (deferred) {
         const stanza = $iq({
@@ -116,17 +125,19 @@ const Bookmarks = {
             .then(iq => this.onBookmarksReceived(deferred, iq))
             .catch(iq => this.onBookmarksReceivedError(deferred, iq)
         );
-    },
+    }
 
+    // eslint-disable-next-line class-methods-use-this
     markRoomAsBookmarked (bookmark) {
         const groupchat = _converse.chatboxes.get(bookmark.get('jid'));
         groupchat?.save('bookmarked', true);
-    },
+    }
 
+    // eslint-disable-next-line class-methods-use-this
     markRoomAsUnbookmarked (bookmark) {
         const groupchat = _converse.chatboxes.get(bookmark.get('jid'));
         groupchat?.save('bookmarked', false);
-    },
+    }
 
     createBookmarksFromStanza (stanza) {
         const xmlns = Strophe.NS.BOOKMARKS;
@@ -142,7 +153,7 @@ const Bookmarks = {
             }
             bookmark ? bookmark.save(attrs) : this.create(attrs);
         });
-    },
+    }
 
     onBookmarksReceived (deferred, iq) {
         this.createBookmarksFromStanza(iq);
@@ -150,7 +161,7 @@ const Bookmarks = {
         if (deferred !== undefined) {
             return deferred.resolve();
         }
-    },
+    }
 
     onBookmarksReceivedError (deferred, iq) {
         const { __ } = _converse;
@@ -174,7 +185,7 @@ const Bookmarks = {
             log.error('Error while fetching bookmarks');
             log.error(iq);
         }
-    },
+    }
 
     async getUnopenedBookmarks () {
         await api.waitUntil('bookmarksInitialized')

+ 1 - 2
src/headless/plugins/bookmarks/index.js

@@ -9,7 +9,6 @@ import Bookmark from './model.js';
 import Bookmarks from './collection.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
-import { Collection } from "@converse/skeletor/src/collection.js";
 import { initBookmarks, getNicknameFromBookmark, handleBookmarksPush } from './utils.js';
 
 const { Strophe } = converse.env;
@@ -55,7 +54,7 @@ converse.plugins.add('converse-bookmarks', {
         api.promises.add('bookmarksInitialized');
 
         _converse.Bookmark = Bookmark;
-        _converse.Bookmarks = Collection.extend(Bookmarks);
+        _converse.Bookmarks = Bookmarks;
 
         api.listen.on('addClientFeatures', () => {
             if (api.settings.get('allow_bookmarks')) {

+ 3 - 10
src/headless/plugins/chat/index.js

@@ -3,12 +3,11 @@
  * @license Mozilla Public License (MPLv2)
  */
 import ChatBox from './model.js';
-import MessageMixin from './message.js';
-import ModelWithContact from './model-with-contact.js';
+import Message from './message.js';
+import Messages from './messages.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import chat_api from './api.js';
-import { Collection } from '@converse/skeletor/src/collection';
 import {
     autoJoinChats,
     enableCarbons,
@@ -39,13 +38,7 @@ converse.plugins.add('converse-chat', {
             'send_chat_state_notifications': true,
         });
 
-        _converse.Message = ModelWithContact.extend(MessageMixin);
-        _converse.Messages = Collection.extend({
-            model: _converse.Message,
-            comparator: 'time',
-        });
-
-        Object.assign(_converse, { ChatBox, handleMessageStanza });
+        Object.assign(_converse, { ChatBox, Message, Messages, handleMessageStanza });
         Object.assign(api, chat_api);
 
         _converse.router.route('converse/chat?jid=:jid', openChat);

+ 24 - 24
src/headless/plugins/chat/message.js

@@ -8,24 +8,25 @@ import { getOpenPromise } from '@converse/openpromise';
 const { Strophe, sizzle, u } = converse.env;
 
 /**
- * Mixin which turns a `ModelWithContact` model into a non-MUC message.
+ * Represents a (non-MUC) message.
  * These can be either `chat`, `normal` or `headline` messages.
- * @mixin
  * @namespace _converse.Message
  * @memberOf _converse
- * @example const msg = new _converse.Message({'message': 'hello world!'});
+ * @example const msg = new Message({'message': 'hello world!'});
  */
-const MessageMixin = {
+class Message extends ModelWithContact {
 
-    defaults () {
+    defaults () { // eslint-disable-line class-methods-use-this
         return {
             'msgid': u.getUniqueId(),
             'time': new Date().toISOString(),
             'is_ephemeral': false
         };
-    },
+    }
 
     async initialize () {
+        super.initialize();
+
         if (!this.checkValidity()) {
             return;
         }
@@ -47,14 +48,14 @@ const MessageMixin = {
          */
         await api.trigger('messageInitialized', this, { 'Synchronous': true });
         this.initialized.resolve();
-    },
+    }
 
     setContact () {
         if (['chat', 'normal'].includes(this.get('type'))) {
             ModelWithContact.prototype.initialize.apply(this, arguments);
             this.setRosterContact(Strophe.getBareJidFromJid(this.get('from')));
         }
-    },
+    }
 
     /**
      * Sets an auto-destruct timer for this message, if it's is_ephemeral.
@@ -70,7 +71,7 @@ const MessageMixin = {
             const timeout = typeof is_ephemeral === "number" ? is_ephemeral : 10000;
             this.ephemeral_timer = window.setTimeout(() => this.safeDestroy(), timeout);
         }
-    },
+    }
 
     checkValidity () {
         if (Object.keys(this.attributes).length === 3) {
@@ -84,11 +85,10 @@ const MessageMixin = {
             return false;
         }
         return true;
-    },
+    }
 
     /**
      * Determines whether this messsage may be retracted by the current user.
-     * @private
      * @method _converse.Messages#mayBeRetracted
      * @returns { Boolean }
      */
@@ -96,7 +96,7 @@ const MessageMixin = {
         const is_own_message = this.get('sender') === 'me';
         const not_canceled = this.get('error_type') !== 'cancel';
         return is_own_message && not_canceled && ['all', 'own'].includes(api.settings.get('allow_message_retraction'));
-    },
+    }
 
     safeDestroy () {
         try {
@@ -104,7 +104,7 @@ const MessageMixin = {
         } catch (e) {
             log.warn(`safeDestroy: ${e}`);
         }
-    },
+    }
 
     /**
      * Returns a boolean indicating whether this message is ephemeral,
@@ -113,7 +113,7 @@ const MessageMixin = {
      */
     isEphemeral () {
         return this.get('is_ephemeral');
-    },
+    }
 
     /**
      * Returns a boolean indicating whether this message is a XEP-0245 /me command.
@@ -125,7 +125,7 @@ const MessageMixin = {
             return false;
         }
         return text.startsWith('/me ');
-    },
+    }
 
     /**
      * Returns a boolean indicating whether this message is considered a followup
@@ -149,7 +149,7 @@ const MessageMixin = {
             this.get('type') === prev_model.get('type') && this.get('type') !== 'info' &&
             date.isBefore(dayjs(prev_model.get('time')).add(10, 'minutes')) &&
             (this.get('type') === 'groupchat' ? this.get('occupant_id') === prev_model.get('occupant_id') : true);
-    },
+    }
 
     getDisplayName () {
         if (this.contact) {
@@ -159,7 +159,7 @@ const MessageMixin = {
         } else {
             return this.get('from');
         }
-    },
+    }
 
     getMessageText () {
         if (this.get('is_encrypted')) {
@@ -170,7 +170,7 @@ const MessageMixin = {
         } else {
             return this.get('message');
         }
-    },
+    }
 
     /**
      * Send out an IQ stanza to request a file upload slot.
@@ -195,9 +195,9 @@ const MessageMixin = {
                 'content-type': this.file.type
             });
         return api.sendIQ(iq);
-    },
+    }
 
-    getUploadRequestMetadata (stanza) {
+    getUploadRequestMetadata (stanza) { // eslint-disable-line class-methods-use-this
         const headers = sizzle(`slot[xmlns="${Strophe.NS.HTTPUPLOAD}"] put header`, stanza);
         // https://xmpp.org/extensions/xep-0363.html#request
         // TODO: Can't set the Cookie header in JavaScipt, instead cookies need
@@ -207,7 +207,7 @@ const MessageMixin = {
                 .map(h => ({ 'name': h.getAttribute('name'), 'value': h.textContent }))
                 .filter(h => ['Authorization', 'Expires'].includes(h.name))
         }
-    },
+    }
 
     async getRequestSlotURL () {
         const { __ } = _converse;
@@ -236,7 +236,7 @@ const MessageMixin = {
                 'is_ephemeral': true
             });
         }
-    },
+    }
 
     uploadFile () {
         const xhr = new XMLHttpRequest();
@@ -297,6 +297,6 @@ const MessageMixin = {
         this.upload_metadata.headers?.forEach(h => xhr.setRequestHeader(h.name, h.value));
         xhr.send(this.file);
     }
-};
+}
 
-export default MessageMixin;
+export default Message;

+ 17 - 0
src/headless/plugins/chat/messages.js

@@ -0,0 +1,17 @@
+import Message from './message.js';
+import { Collection } from '@converse/skeletor/src/collection';
+
+class Messages extends Collection {
+
+    // eslint-disable-next-line class-methods-use-this
+    get comparator () {
+        return 'time';
+    }
+
+    constructor () {
+        super();
+        this.model = Message;
+    }
+}
+
+export default Messages;

+ 4 - 3
src/headless/plugins/chat/model-with-contact.js

@@ -2,11 +2,12 @@ import api from "../../shared/api/index.js";
 import { Model } from '@converse/skeletor/src/model.js';
 import { getOpenPromise } from '@converse/openpromise';
 
-const ModelWithContact = Model.extend({
+class ModelWithContact extends Model {
 
     initialize () {
+        super.initialize();
         this.rosterContactAdded = getOpenPromise();
-    },
+    }
 
     async setRosterContact (jid) {
         const contact = await api.contacts.get(jid);
@@ -16,6 +17,6 @@ const ModelWithContact = Model.extend({
             this.rosterContactAdded.resolve();
         }
     }
-});
+}
 
 export default ModelWithContact;

+ 64 - 66
src/headless/plugins/chat/model.js

@@ -23,12 +23,10 @@ const u = converse.env.utils;
 
 /**
  * Represents an open/ongoing chat conversation.
- *
- * @class
  * @namespace _converse.ChatBox
  * @memberOf _converse
  */
-const ChatBox = ModelWithContact.extend({
+class ChatBox extends ModelWithContact {
 
     defaults () {
         return {
@@ -43,11 +41,11 @@ const ChatBox = ModelWithContact.extend({
             'type': _converse.PRIVATE_CHAT_TYPE,
             'url': ''
         }
-    },
+    }
 
     async initialize () {
+        super.initialize();
         this.initialized = getOpenPromise();
-        ModelWithContact.prototype.initialize.apply(this, arguments);
 
         const jid = this.get('jid');
         if (!jid) {
@@ -81,15 +79,15 @@ const ChatBox = ModelWithContact.extend({
          */
         await api.trigger('chatBoxInitialized', this, {'Synchronous': true});
         this.initialized.resolve();
-    },
+    }
 
     getMessagesCollection () {
         return new _converse.Messages();
-    },
+    }
 
     getMessagesCacheKey () {
         return `converse.messages-${this.get('jid')}-${_converse.bare_jid}`;
-    },
+    }
 
     initMessages () {
         this.messages = this.getMessagesCollection();
@@ -99,15 +97,15 @@ const ChatBox = ModelWithContact.extend({
 
         this.listenTo(this.messages, 'change:upload', this.onMessageUploadChanged, this);
         this.listenTo(this.messages, 'add', this.onMessageAdded, this);
-    },
+    }
 
     initUI () {
         this.ui = new Model();
-    },
+    }
 
     initNotifications () {
         this.notifications = new Model();
-    },
+    }
 
     getNotificationsText () {
         const { __ } = _converse;
@@ -120,7 +118,7 @@ const ChatBox = ModelWithContact.extend({
         } else {
             return '';
         }
-    },
+    }
 
     afterMessagesFetched () {
         this.pruneHistoryWhenScrolledDown();
@@ -132,7 +130,7 @@ const ChatBox = ModelWithContact.extend({
          * @example _converse.api.listen.on('afterMessagesFetched', (chat) => { ... });
          */
         api.trigger('afterMessagesFetched', this);
-    },
+    }
 
     fetchMessages () {
         if (this.messages.fetched_flag) {
@@ -147,7 +145,7 @@ const ChatBox = ModelWithContact.extend({
             'error': () => { this.afterMessagesFetched(); resolve() }
         });
         return this.messages.fetched;
-    },
+    }
 
     async handleErrorMessageStanza (stanza) {
         const { __ } = _converse;
@@ -185,7 +183,7 @@ const ChatBox = ModelWithContact.extend({
         } else {
             this.createMessage(attrs);
         }
-    },
+    }
 
     /**
      * Queue an incoming `chat` message stanza for processing.
@@ -199,7 +197,7 @@ const ChatBox = ModelWithContact.extend({
             .then(() => this.onMessage(attrs))
             .catch(e => log.error(e));
         return this.msg_chain;
-    },
+    }
 
     /**
      * @async
@@ -232,7 +230,7 @@ const ChatBox = ModelWithContact.extend({
                 this.handleUnreadMessage(msg);
             }
         }
-    },
+    }
 
     async onMessageUploadChanged (message) {
         if (message.get('upload') === _converse.SUCCESS) {
@@ -245,7 +243,7 @@ const ChatBox = ModelWithContact.extend({
             await this.sendMessage(attrs);
             message.destroy();
         }
-    },
+    }
 
     onMessageAdded (message) {
         if (api.settings.get('prune_messages_above') &&
@@ -254,7 +252,7 @@ const ChatBox = ModelWithContact.extend({
         ) {
             debouncedPruneHistory(this);
         }
-    },
+    }
 
     async clearMessages () {
         try {
@@ -267,7 +265,7 @@ const ChatBox = ModelWithContact.extend({
             // Make sure to resolve the fetched promise to avoid freezes.
             this.messages.fetched.resolve();
         }
-    },
+    }
 
     async close () {
         if (api.connection.connected()) {
@@ -294,7 +292,7 @@ const ChatBox = ModelWithContact.extend({
          * @example _converse.api.listen.on('chatBoxClosed', chat => { ... });
          */
         api.trigger('chatBoxClosed', this);
-    },
+    }
 
     announceReconnection () {
         /**
@@ -304,14 +302,14 @@ const ChatBox = ModelWithContact.extend({
          * @example _converse.api.listen.on('onChatReconnected', chat => { ... });
          */
         api.trigger('chatReconnected', this);
-    },
+    }
 
     async onReconnection () {
         if (api.settings.get('clear_messages_on_reconnection')) {
             await this.clearMessages();
         }
         this.announceReconnection();
-    },
+    }
 
     onPresenceChanged (item) {
         const { __ } = _converse;
@@ -328,14 +326,14 @@ const ChatBox = ModelWithContact.extend({
             text = __('%1$s is online', fullname);
         }
         text && this.createMessage({ 'message': text, 'type': 'info' });
-    },
+    }
 
     onScrolledChanged () {
         if (!this.ui.get('scrolled')) {
             this.clearUnreadMsgCounter();
             this.pruneHistoryWhenScrolledDown();
         }
-    },
+    }
 
     pruneHistoryWhenScrolledDown () {
         if (
@@ -345,7 +343,7 @@ const ChatBox = ModelWithContact.extend({
         ) {
             debouncedPruneHistory(this);
         }
-    },
+    }
 
     validate (attrs) {
         if (!attrs.jid) {
@@ -358,7 +356,7 @@ const ChatBox = ModelWithContact.extend({
             log.warn(msg);
             return msg;
         }
-    },
+    }
 
     getDisplayName () {
         if (this.contact) {
@@ -368,7 +366,7 @@ const ChatBox = ModelWithContact.extend({
         } else {
             return this.get('jid');
         }
-    },
+    }
 
     async createMessageFromError (error) {
         if (error instanceof TimeoutError) {
@@ -380,7 +378,7 @@ const ChatBox = ModelWithContact.extend({
             });
             msg.error = error;
         }
-    },
+    }
 
     editEarlierMessage () {
         let message;
@@ -404,7 +402,7 @@ const ChatBox = ModelWithContact.extend({
         if (message) {
             message.save('correcting', true);
         }
-    },
+    }
 
     editLaterMessage () {
         let message;
@@ -422,7 +420,7 @@ const ChatBox = ModelWithContact.extend({
             }
         }
         return message;
-    },
+    }
 
     getOldestMessage () {
         for (let i=0; i<this.messages.length; i++) {
@@ -431,7 +429,7 @@ const ChatBox = ModelWithContact.extend({
                 return message;
             }
         }
-    },
+    }
 
     getMostRecentMessage () {
         for (let i=this.messages.length-1; i>=0; i--) {
@@ -440,7 +438,7 @@ const ChatBox = ModelWithContact.extend({
                 return message;
             }
         }
-    },
+    }
 
     getUpdatedMessageAttributes (message, attrs) {
         if (!attrs.error_type && message.get('error_type') === 'Decryption') {
@@ -459,12 +457,12 @@ const ChatBox = ModelWithContact.extend({
         } else {
             return { is_archived: attrs.is_archived };
         }
-    },
+    }
 
     updateMessage (message, attrs) {
         const new_attrs = this.getUpdatedMessageAttributes(message, attrs);
         new_attrs && message.save(new_attrs);
-    },
+    }
 
     /**
      * Mutator for setting the chat state of this chat session.
@@ -497,7 +495,7 @@ const ChatBox = ModelWithContact.extend({
         }
         this.set('chat_state', state, options);
         return this;
-    },
+    }
 
     /**
      * Given an error `<message>` stanza's attributes, find the saved message model which is
@@ -507,7 +505,7 @@ const ChatBox = ModelWithContact.extend({
     getMessageReferencedByError (attrs) {
         const id = attrs.msgid;
         return id && this.messages.models.find(m => [m.get('msgid'), m.get('retraction_id')].includes(id));
-    },
+    }
 
     /**
      * @private
@@ -525,11 +523,11 @@ const ChatBox = ModelWithContact.extend({
         }
         // Gets overridden in ChatRoom
         return true;
-    },
+    }
 
     isSameUser (jid1, jid2) {
         return u.isSameBareJID(jid1, jid2);
-    },
+    }
 
     /**
      * Looks whether we already have a retraction for this
@@ -559,7 +557,7 @@ const ChatBox = ModelWithContact.extend({
                     !attributes.moderated_by
             );
         }
-    },
+    }
 
     /**
      * Handles message retraction based on the passed in attributes.
@@ -596,7 +594,7 @@ const ChatBox = ModelWithContact.extend({
             }
         }
         return false;
-    },
+    }
 
     /**
      * Returns an already cached message (if it exists) based on the
@@ -615,11 +613,11 @@ const ChatBox = ModelWithContact.extend({
             ].filter(s => s);
         const msgs = this.messages.models;
         return msgs.find(m => queries.reduce((out, q) => (out || isMatch(m.attributes, q)), false));
-    },
+    }
 
     getOriginIdQueryAttrs (attrs) {
         return attrs.origin_id && {'origin_id': attrs.origin_id, 'from': attrs.from};
-    },
+    }
 
     getStanzaIdQueryAttrs (attrs) {
         const keys = Object.keys(attrs).filter(k => k.startsWith('stanza_id '));
@@ -629,7 +627,7 @@ const ChatBox = ModelWithContact.extend({
             query[`stanza_id ${by_jid}`] = attrs[key];
             return query;
         });
-    },
+    }
 
     getMessageBodyQueryAttrs (attrs) {
         if (attrs.msgid) {
@@ -645,7 +643,7 @@ const ChatBox = ModelWithContact.extend({
             }
             return query;
         }
-    },
+    }
 
     /**
      * Retract one of your messages in this chat
@@ -653,7 +651,7 @@ const ChatBox = ModelWithContact.extend({
      * @method _converse.ChatBoxView#retractOwnMessage
      * @param { _converse.Message } message - The message which we're retracting.
      */
-    retractOwnMessage(message) {
+    retractOwnMessage (message) {
         this.sendRetractionMessage(message)
         message.save({
             'retracted': (new Date()).toISOString(),
@@ -662,7 +660,7 @@ const ChatBox = ModelWithContact.extend({
             'is_ephemeral': true,
             'editable': false
         });
-    },
+    }
 
     /**
      * Sends a message stanza to retract a message in this chat
@@ -686,7 +684,7 @@ const ChatBox = ModelWithContact.extend({
                 'xmlns': Strophe.NS.FASTEN
             }).c('retract', {xmlns: Strophe.NS.RETRACT})
         return api.connection.get().send(msg);
-    },
+    }
 
     /**
      * Finds the last eligible message and then sends a XEP-0333 chat marker for it.
@@ -699,7 +697,7 @@ const ChatBox = ModelWithContact.extend({
         msgs.reverse();
         const msg = msgs.find(m => m.get('sender') === 'them' && (force || m.get('is_markable')));
         msg && this.sendMarkerForMessage(msg, type, force);
-    },
+    }
 
     /**
      * Given the passed in message object, send a XEP-0333 chat marker.
@@ -716,7 +714,7 @@ const ChatBox = ModelWithContact.extend({
             const from_jid = Strophe.getBareJidFromJid(msg.get('from'));
             sendMarker(from_jid, msg.get('msgid'), type, msg.get('type'));
         }
-    },
+    }
 
     handleChatMarker (attrs) {
         const to_bare_jid = Strophe.getBareJidFromJid(attrs.to);
@@ -736,7 +734,7 @@ const ChatBox = ModelWithContact.extend({
             }
             return true;
         }
-    },
+    }
 
     sendReceiptStanza (to_jid, id) {
         const receipt_stanza = $msg({
@@ -747,7 +745,7 @@ const ChatBox = ModelWithContact.extend({
         }).c('received', {'xmlns': Strophe.NS.RECEIPTS, 'id': id}).up()
         .c('store', {'xmlns': Strophe.NS.HINTS}).up();
         api.send(receipt_stanza);
-    },
+    }
 
     handleReceipt (attrs) {
         if (attrs.sender === 'them') {
@@ -762,7 +760,7 @@ const ChatBox = ModelWithContact.extend({
             }
         }
         return false;
-    },
+    }
 
     /**
      * Given a {@link _converse.Message} return the XML stanza that represents it.
@@ -835,7 +833,7 @@ const ChatBox = ModelWithContact.extend({
          */
         const data = await api.hook('createMessageStanza', this, { message, stanza });
         return data.stanza;
-    },
+    }
 
     async getOutgoingMessageAttributes (attrs) {
         await api.emojis.initialize();
@@ -872,7 +870,7 @@ const ChatBox = ModelWithContact.extend({
          */
         attrs = await api.hook('getOutgoingMessageAttributes', this, attrs);
         return attrs;
-    },
+    }
 
     /**
      * Responsible for setting the editable attribute of messages.
@@ -895,7 +893,7 @@ const ChatBox = ModelWithContact.extend({
             this.messages.findWhere({'editable': true})?.save({'editable': false});
             attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
         }
-    },
+    }
 
     /**
      * Queue the creation of a message, to make sure that we don't run
@@ -910,7 +908,7 @@ const ChatBox = ModelWithContact.extend({
         attrs.time = attrs.time || (new Date()).toISOString();
         await this.messages.fetched;
         return this.messages.create(attrs, options);
-    },
+    }
 
     /**
      * Responsible for sending off a text message inside an ongoing chat conversation.
@@ -968,7 +966,7 @@ const ChatBox = ModelWithContact.extend({
         */
         api.trigger('sendMessage', {'chatbox': this, message});
         return message;
-    },
+    }
 
     /**
      * Sends a message with the current XEP-0085 chat state of the user
@@ -992,7 +990,7 @@ const ChatBox = ModelWithContact.extend({
                 .c('no-permanent-store', {'xmlns': Strophe.NS.HINTS})
             );
         }
-    },
+    }
 
 
     async sendFiles (files) {
@@ -1052,7 +1050,7 @@ const ChatBox = ModelWithContact.extend({
                 message.getRequestSlotURL();
             }
         });
-    },
+    }
 
     maybeShow (force) {
         if (isUniView()) {
@@ -1071,7 +1069,7 @@ const ChatBox = ModelWithContact.extend({
         u.safeSave(this, {'hidden': false});
         this.trigger('show');
         return this;
-    },
+    }
 
     /**
      * Indicates whether the chat is hidden and therefore
@@ -1082,7 +1080,7 @@ const ChatBox = ModelWithContact.extend({
     isHidden () {
         // Note: This methods gets overridden by converse-minimize
         return this.get('hidden') || this.isScrolledUp() || _converse.windowState === 'hidden';
-    },
+    }
 
     /**
      * Given a newly received {@link _converse.Message} instance,
@@ -1108,7 +1106,7 @@ const ChatBox = ModelWithContact.extend({
                 this.sendMarkerForMessage(message);
             }
         }
-    },
+    }
 
     incrementUnreadMsgsCounter (message) {
         const settings = {
@@ -1118,18 +1116,18 @@ const ChatBox = ModelWithContact.extend({
             settings['first_unread_id'] = message.get('id');
         }
         this.save(settings);
-    },
+    }
 
     clearUnreadMsgCounter () {
         if (this.get('num_unread') > 0) {
             this.sendMarkerForMessage(this.messages.last());
         }
         u.safeSave(this, {'num_unread': 0});
-    },
+    }
 
     isScrolledUp () {
         return this.ui.get('scrolled');
     }
-});
+}
 
 export default ChatBox;

+ 7 - 10
src/headless/plugins/chatboxes/chatboxes.js

@@ -3,14 +3,12 @@ import api from '../../shared/api/index.js';
 import { Collection } from "@converse/skeletor/src/collection";
 import { initStorage } from '../../utils/storage.js';
 
-const ChatBoxes = Collection.extend({
-    comparator: 'time_opened',
-
-    model (attrs, options) {
-        return new _converse.ChatBox(attrs, options);
-    },
+class ChatBoxes extends Collection {
+    get comparator () { // eslint-disable-line class-methods-use-this
+        return 'time_opened';
+    }
 
-    onChatBoxesFetched (collection) {
+    onChatBoxesFetched (collection) { // eslint-disable-line class-methods-use-this
         collection.filter(c => !c.isValid()).forEach(c => c.destroy());
         /**
          * Triggered once all chat boxes have been recreated from the browser cache
@@ -22,7 +20,7 @@ const ChatBoxes = Collection.extend({
          * @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
          */
         api.trigger('chatBoxesFetched');
-    },
+    }
 
     onConnected (reconnecting) {
         if (reconnecting) { return; }
@@ -32,7 +30,6 @@ const ChatBoxes = Collection.extend({
             'success': c => this.onChatBoxesFetched(c)
         });
     }
-});
-
+}
 
 export default ChatBoxes;

+ 7 - 3
src/headless/plugins/disco/entities.js

@@ -3,8 +3,12 @@ import log from "../../log.js";
 import { Collection } from "@converse/skeletor/src/collection";
 
 
-const DiscoEntities = Collection.extend({
-    model: DiscoEntity,
+class DiscoEntities extends Collection {
+
+    constructor () {
+        super();
+        this.model = DiscoEntity;
+    }
 
     fetchEntities () {
         return new Promise((resolve, reject) => {
@@ -18,6 +22,6 @@ const DiscoEntities = Collection.extend({
             });
         });
     }
-});
+}
 
 export default DiscoEntities;

+ 3 - 2
src/headless/plugins/headlines/feed.js

@@ -5,7 +5,7 @@ import api from "../../shared/api/index.js";
 
 export default class HeadlinesFeed extends ChatBox {
 
-    defaults () {
+    defaults () { // eslint-disable-line class-methods-use-this
         return {
             'bookmarked': false,
             'hidden': ['mobile', 'fullscreen'].includes(api.settings.get("view_mode")),
@@ -17,6 +17,7 @@ export default class HeadlinesFeed extends ChatBox {
     }
 
     async initialize () {
+        super.initialize();
         this.set({'box_id': `box-${this.get('jid')}`});
         this.initUI();
         this.initMessages();
@@ -24,7 +25,7 @@ export default class HeadlinesFeed extends ChatBox {
         /**
          * Triggered once a { @link _converse.HeadlinesFeed } has been created and initialized.
          * @event _converse#headlinesFeedInitialized
-         * @type { _converse.HeadlinesFeed }
+         * @type {HeadlinesFeed}
          * @example _converse.api.listen.on('headlinesFeedInitialized', model => { ... });
          */
         api.trigger('headlinesFeedInitialized', this);

+ 0 - 19
src/headless/plugins/headlines/index.js

@@ -8,28 +8,9 @@ import api, { converse } from '../../shared/api/index.js';
 import headlines_api from './api.js';
 import { onHeadlineMessage } from './utils.js';
 
-
 converse.plugins.add('converse-headlines', {
     dependencies: ["converse-chat"],
 
-    overrides: {
-        // Overrides mentioned here will be picked up by converse.js's
-        // plugin architecture they will replace existing methods on the
-        // relevant objects or classes.
-
-        ChatBoxes: {
-            model (attrs, options) {
-                const { _converse } = this.__super__;
-                if (attrs.type == _converse.HEADLINES_TYPE) {
-                    return new _converse.HeadlinesFeed(attrs, options);
-                } else {
-                    return this.__super__.model.apply(this, arguments);
-                }
-            },
-        }
-    },
-
-
     initialize () {
         /**
          * Shows headline messages

+ 5 - 2
src/headless/plugins/headlines/utils.js

@@ -1,3 +1,4 @@
+import HeadlinesFeed from './feed';
 import _converse from '../../shared/_converse.js';
 import api from '../../shared/api/index.js';
 import { HEADLINES_TYPE } from '../../shared/constants.js';
@@ -22,12 +23,14 @@ export async function onHeadlineMessage (stanza) {
             // Avoid creating a chat box if we have nothing to show inside it.
             return;
         }
-        const chatbox = _converse.chatboxes.create({
+
+        const chatbox = await api.chatboxes.create(from_jid, {
             'id': from_jid,
             'jid': from_jid,
             'type': HEADLINES_TYPE,
             'from': from_jid
-        });
+        }, HeadlinesFeed);
+
         const attrs = await parseMessage(stanza);
         await chatbox.createMessage(attrs);
         api.trigger('message', {chatbox, stanza, attrs});

+ 11 - 32
src/headless/plugins/muc/index.js

@@ -6,13 +6,13 @@
 import '../chat/index.js';
 import '../disco/index.js';
 import '../emoji/index.js';
-import ChatRoomMessageMixin from './message.js';
-import ChatRoomMixin from './muc.js';
+import MUCMessage from './message.js';
+import MUCMessages from './messages.js';
+import MUC from './muc.js';
 import ChatRoomOccupant from './occupant.js';
 import ChatRoomOccupants from './occupants.js';
 import affiliations_api from './affiliations/api.js';
 import muc_api from './api.js';
-import { Collection } from '@converse/skeletor/src/collection';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import {
@@ -45,7 +45,7 @@ import {
 
 converse.AFFILIATION_CHANGES = AFFILIATION_CHANGES;
 converse.AFFILIATION_CHANGES_LIST = AFFILIATION_CHANGES_LIST;
-converse.MUC_TRAFFIC_STATES =  MUC_TRAFFIC_STATES;
+converse.MUC_TRAFFIC_STATES = MUC_TRAFFIC_STATES;
 converse.MUC_TRAFFIC_STATES_LIST = MUC_TRAFFIC_STATES_LIST;
 converse.MUC_ROLE_CHANGES = MUC_ROLE_CHANGES;
 converse.MUC_ROLE_CHANGES_LIST = MUC_ROLE_CHANGES_LIST;
@@ -67,23 +67,9 @@ Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + '#user');
 Strophe.addNamespace('MUC_HATS', 'xmpp:prosody.im/protocol/hats:1');
 Strophe.addNamespace('CONFINFO', 'urn:ietf:params:xml:ns:conference-info');
 
-
 converse.plugins.add('converse-muc', {
     dependencies: ['converse-chatboxes', 'converse-chat', 'converse-disco'],
 
-    overrides: {
-        ChatBoxes: {
-            model (attrs, options) {
-                const { _converse } = this.__super__;
-                if (attrs && attrs.type == _converse.CHATROOMS_TYPE) {
-                    return new _converse.ChatRoom(attrs, options);
-                } else {
-                    return this.__super__.model.apply(this, arguments);
-                }
-            },
-        },
-    },
-
     initialize () {
         /* The initialize function gets called as soon as the plugin is
          * loaded by converse.js's plugin machinery.
@@ -192,25 +178,18 @@ converse.plugins.add('converse-muc', {
 
         _converse.router.route('converse/room?jid=:jid', routeToRoom);
 
-        _converse.ChatRoom = _converse.ChatBox.extend(ChatRoomMixin);
-        _converse.ChatRoomMessage = _converse.Message.extend(ChatRoomMessageMixin);
+        _converse.ChatRoom = MUC;
+        _converse.ChatRoomMessage = MUCMessage;
         _converse.ChatRoomOccupants = ChatRoomOccupants;
         _converse.ChatRoomOccupant = ChatRoomOccupant;
 
-        /**
-         * Collection which stores MUC messages
-         * @class
-         * @namespace _converse.ChatRoomMessages
-         * @memberOf _converse
-         */
-        _converse.ChatRoomMessages = Collection.extend({
-            model: _converse.ChatRoomMessage,
-            comparator: 'time',
+        Object.assign(_converse, {
+            getDefaultMUCNickname,
+            isInfoVisible,
+            onDirectMUCInvitation,
+            ChatRoomMessages: MUCMessages,
         });
 
-        Object.assign(_converse, { getDefaultMUCNickname, isInfoVisible, onDirectMUCInvitation });
-
-
         /************************ BEGIN Event Handlers ************************/
 
         if (api.settings.get('allow_muc_invitations')) {

+ 12 - 14
src/headless/plugins/muc/message.js

@@ -1,14 +1,14 @@
+import Message from '../chat/message.js';
 import _converse from '../../shared/_converse.js';
 import api from '../../shared/api/index.js';
 import { Strophe } from 'strophe.js';
 
 /**
- * Mixing that turns a Message model into a ChatRoomMessage model.
- * @class
  * @namespace _converse.ChatRoomMessage
  * @memberOf _converse
  */
-const ChatRoomMessageMixin = {
+class MUCMessage extends Message {
+
     initialize () {
         if (!this.checkValidity()) {
             return;
@@ -30,18 +30,16 @@ const ChatRoomMessageMixin = {
          * @example _converse.api.listen.on('chatRoomMessageInitialized', model => { ... });
          */
         api.trigger('chatRoomMessageInitialized', this);
-    },
-
+    }
 
     getDisplayName () {
         return this.occupant?.getDisplayName() || this.get('nick');
-    },
+    }
 
     /**
      * Determines whether this messsage may be moderated,
      * based on configuration settings and server support.
      * @async
-     * @private
      * @method _converse.ChatRoomMessages#mayBeModerated
      * @returns { Boolean }
      */
@@ -56,19 +54,19 @@ const ChatRoomMessageMixin = {
             this.get(`stanza_id ${this.get('from_muc')}`) &&
             this.chatbox.canModerateMessages()
         );
-    },
+    }
 
     checkValidity () {
         const result = _converse.Message.prototype.checkValidity.call(this);
         !result && this.chatbox.debouncedRejoin();
         return result;
-    },
+    }
 
     onOccupantRemoved () {
         this.stopListening(this.occupant);
         delete this.occupant;
         this.listenTo(this.chatbox.occupants, 'add', this.onOccupantAdded);
-    },
+    }
 
     onOccupantAdded (occupant) {
         if (this.get('occupant_id')) {
@@ -87,14 +85,14 @@ const ChatRoomMessageMixin = {
         this.trigger('occupantAdded');
         this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
         this.stopListening(this.chatbox.occupants, 'add', this.onOccupantAdded);
-    },
+    }
 
     getOccupant() {
         if (this.occupant) return this.occupant;
 
         this.setOccupant();
         return this.occupant;
-    },
+    }
 
     setOccupant () {
         if (this.get('type') !== 'groupchat' || this.isEphemeral() || this.occupant) {
@@ -121,6 +119,6 @@ const ChatRoomMessageMixin = {
 
         this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
     }
-};
+}
 
-export default ChatRoomMessageMixin;
+export default MUCMessage;

+ 22 - 0
src/headless/plugins/muc/messages.js

@@ -0,0 +1,22 @@
+import MUCMessage from './message';
+import { Collection } from '@converse/skeletor/src/collection';
+
+/**
+ * Collection which stores MUC messages
+ * @namespace _converse.ChatRoomMessages
+ * @memberOf _converse
+ */
+class MUCMessages extends Collection {
+
+    // eslint-disable-next-line class-methods-use-this
+    get comparator () {
+        return 'time';
+    }
+
+    constructor () {
+        super();
+        this.model = MUCMessage;
+    }
+}
+
+export default MUCMessages;

Fichier diff supprimé car celui-ci est trop grand
+ 117 - 115
src/headless/plugins/muc/muc.js


+ 89 - 75
src/headless/plugins/roster/contacts.js

@@ -9,16 +9,18 @@ import { rejectPresenceSubscription } from './utils.js';
 
 const { Strophe, $iq, sizzle, u } = converse.env;
 
-
-const RosterContacts = Collection.extend({
-    model: RosterContact,
+class RosterContacts extends Collection {
+    constructor () {
+        super();
+        this.model = RosterContact;
+    }
 
     initialize () {
         const id = `roster.state-${_converse.bare_jid}-${this.get('jid')}`;
         this.state = new Model({ id, 'collapsed_groups': [] });
         initStorage(this.state, id);
         this.state.fetch();
-    },
+    }
 
     onConnected () {
         // Called as soon as the connection has been established
@@ -26,8 +28,13 @@ const RosterContacts = Collection.extend({
         // Use the opportunity to register stanza handlers.
         this.registerRosterHandler();
         this.registerRosterXHandler();
-    },
+    }
 
+    /**
+     * Register a handler for roster IQ "set" stanzas, which update
+     * roster contacts.
+     */
+    // eslint-disable-next-line class-methods-use-this
     registerRosterHandler () {
         // Register a handler for roster IQ "set" stanzas, which update
         // roster contacts.
@@ -35,26 +42,30 @@ const RosterContacts = Collection.extend({
             _converse.roster.onRosterPush(iq);
             return true;
         }, Strophe.NS.ROSTER, 'iq', "set");
-    },
+    }
 
+    /**
+     * Register a handler for RosterX message stanzas, which are
+     * used to suggest roster contacts to a user.
+     */
+    // eslint-disable-next-line class-methods-use-this
     registerRosterXHandler () {
-        // Register a handler for RosterX message stanzas, which are
-        // used to suggest roster contacts to a user.
         let t = 0;
         const connection = api.connection.get();
         connection.addHandler(
             function (msg) {
-                window.setTimeout(
-                    function () {
-                        connection.flush();
-                        _converse.roster.subscribeToSuggestedItems.bind(_converse.roster)(msg);
-                    }, t);
-                t += msg.querySelectorAll('item').length*250;
+                window.setTimeout(function () {
+                    _converse.connection.flush();
+                    _converse.roster.subscribeToSuggestedItems.bind(_converse.roster)(msg);
+                }, t);
+                t += msg.querySelectorAll('item').length * 250;
                 return true;
             },
-            Strophe.NS.ROSTERX, 'message', null
+            Strophe.NS.ROSTERX,
+            'message',
+            null
         );
-    },
+    }
 
     /**
      * Fetches the roster contacts, first by trying the browser cache,
@@ -67,13 +78,13 @@ const RosterContacts = Collection.extend({
                 'add': true,
                 'silent': true,
                 'success': resolve,
-                'error': (_, e) => reject(e)
+                'error': (_, e) => reject(e),
             });
         });
         if (u.isErrorObject(result)) {
             log.error(result);
             // Force a full roster refresh
-            _converse.session.save('roster_cached', false)
+            _converse.session.save('roster_cached', false);
             this.data.save('version', undefined);
         }
 
@@ -81,7 +92,7 @@ const RosterContacts = Collection.extend({
             /**
              * The contacts roster has been retrieved from the local cache (`sessionStorage`).
              * @event _converse#cachedRoster
-             * @type { _converse.RosterContacts }
+             * @type {RosterContacts}
              * @example _converse.api.listen.on('cachedRoster', (items) => { ... });
              * @example _converse.api.waitUntil('cachedRoster').then(items => { ... });
              */
@@ -90,10 +101,11 @@ const RosterContacts = Collection.extend({
             _converse.send_initial_presence = true;
             return _converse.roster.fetchFromServer();
         }
-    },
+    }
 
+    // eslint-disable-next-line class-methods-use-this
     subscribeToSuggestedItems (msg) {
-        Array.from(msg.querySelectorAll('item')).forEach(item => {
+        Array.from(msg.querySelectorAll('item')).forEach((item) => {
             if (item.getAttribute('action') === 'add') {
                 _converse.roster.addAndSubscribe(
                     item.getAttribute('jid'),
@@ -102,11 +114,12 @@ const RosterContacts = Collection.extend({
             }
         });
         return true;
-    },
+    }
 
+    // eslint-disable-next-line class-methods-use-this
     isSelf (jid) {
         return u.isSameBareJID(jid, api.connection.get().jid);
-    },
+    }
 
     /**
      * Add a roster contact and then once we have confirmation from
@@ -123,7 +136,7 @@ const RosterContacts = Collection.extend({
         if (contact instanceof _converse.RosterContact) {
             contact.subscribe(message);
         }
-    },
+    }
 
     /**
      * Send an IQ stanza to the XMPP server to add a new roster contact.
@@ -132,14 +145,13 @@ const RosterContacts = Collection.extend({
      * @param { String } name - The name of that user
      * @param { Array<String> } groups - Any roster groups the user might belong to
      */
+    // eslint-disable-next-line class-methods-use-this
     sendContactAddIQ (jid, name, groups) {
         name = name ? name : null;
-        const iq = $iq({'type': 'set'})
-            .c('query', {'xmlns': Strophe.NS.ROSTER})
-            .c('item', { jid, name });
-        groups.forEach(g => iq.c('group').t(g).up());
+        const iq = $iq({ 'type': 'set' }).c('query', { 'xmlns': Strophe.NS.ROSTER }).c('item', { jid, name });
+        groups.forEach((g) => iq.c('group').t(g).up());
         return api.sendIQ(iq);
-    },
+    }
 
     /**
      * Adds a RosterContact instance to _converse.roster and
@@ -162,15 +174,21 @@ const RosterContacts = Collection.extend({
             alert(__('Sorry, there was an error while trying to add %1$s as a contact.', name || jid));
             return e;
         }
-        return this.create(Object.assign({
-            'ask': undefined,
-            'nickname': name,
-            groups,
-            jid,
-            'requesting': false,
-            'subscription': 'none'
-        }, attributes), {'sort': false});
-    },
+        return this.create(
+            Object.assign(
+                {
+                    'ask': undefined,
+                    'nickname': name,
+                    groups,
+                    jid,
+                    'requesting': false,
+                    'subscription': 'none',
+                },
+                attributes
+            ),
+            { 'sort': false }
+        );
+    }
 
     async subscribeBack (bare_jid, presence) {
         const contact = this.get(bare_jid);
@@ -179,12 +197,12 @@ const RosterContacts = Collection.extend({
         } else {
             // Can happen when a subscription is retried or roster was deleted
             const nickname = sizzle(`nick[xmlns="${Strophe.NS.NICK}"]`, presence).pop()?.textContent || null;
-            const contact = await this.addContactToRoster(bare_jid, nickname, [], {'subscription': 'from'});
+            const contact = await this.addContactToRoster(bare_jid, nickname, [], { 'subscription': 'from' });
             if (contact instanceof _converse.RosterContact) {
                 contact.authorize().subscribe();
             }
         }
-    },
+    }
 
     /**
      * Handle roster updates from the XMPP server.
@@ -202,9 +220,7 @@ const RosterContacts = Collection.extend({
             // attribute (i.e., implicitly from the bare JID of the user's
             // account) or it has a 'from' attribute whose value matches the
             // user's bare JID <user@domainpart>.
-            log.warn(
-                `Ignoring roster illegitimate roster push message from ${iq.getAttribute('from')}`
-            );
+            log.warn(`Ignoring roster illegitimate roster push message from ${iq.getAttribute('from')}`);
             return;
         }
         api.send($iq({type: 'result', id, from: api.connection.get().jid}));
@@ -231,11 +247,11 @@ const RosterContacts = Collection.extend({
          */
         api.trigger('rosterPush', iq);
         return;
-    },
+    }
 
     rosterVersioningSupported () {
         return api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version');
-    },
+    }
 
     /**
      * Fetch the roster from the XMPP server
@@ -245,10 +261,10 @@ const RosterContacts = Collection.extend({
     async fetchFromServer () {
         const stanza = $iq({
             'type': 'get',
-            'id': u.getUniqueId('roster')
-        }).c('query', {xmlns: Strophe.NS.ROSTER});
+            'id': u.getUniqueId('roster'),
+        }).c('query', { xmlns: Strophe.NS.ROSTER });
         if (this.rosterVersioningSupported()) {
-            stanza.attrs({'ver': this.data.get('version')});
+            stanza.attrs({ 'ver': this.data.get('version') });
         }
 
         const iq = await api.sendIQ(stanza, null, false);
@@ -260,16 +276,16 @@ const RosterContacts = Collection.extend({
                 if (!this.data.get('version') && this.models.length) {
                     // We're getting the full roster, so remove all cached
                     // contacts that aren't included in it.
-                    const jids = items.map(item => item.getAttribute('jid'));
-                    this.forEach(m => !m.get('requesting') && !jids.includes(m.get('jid')) && m.destroy());
+                    const jids = items.map((item) => item.getAttribute('jid'));
+                    this.forEach((m) => !m.get('requesting') && !jids.includes(m.get('jid')) && m.destroy());
                 }
-                items.forEach(item => this.updateContact(item));
+                items.forEach((item) => this.updateContact(item));
                 this.data.save('version', query.getAttribute('ver'));
             }
         } else if (!u.isServiceUnavailableError(iq)) {
             // Some unknown error happened, so we will try to fetch again if the page reloads.
             log.error(iq);
-            log.error("Error while trying to fetch roster from the server");
+            log.error('Error while trying to fetch roster from the server');
             return;
         }
 
@@ -284,7 +300,7 @@ const RosterContacts = Collection.extend({
          * @example _converse.api.waitUntil('roster').then(iq => { ... });
          */
         api.trigger('roster', iq);
-    },
+    }
 
     /**
      * Update or create RosterContact models based on the given `item` XML
@@ -294,14 +310,14 @@ const RosterContacts = Collection.extend({
     updateContact (item) {
         const jid = item.getAttribute('jid');
         const contact = this.get(jid);
-        const subscription = item.getAttribute("subscription");
-        if (subscription === "remove") {
+        const subscription = item.getAttribute('subscription');
+        if (subscription === 'remove') {
             return contact?.destroy();
         }
 
-        const ask = item.getAttribute("ask");
+        const ask = item.getAttribute('ask');
         const nickname = item.getAttribute('name');
-        const groups = [...new Set(sizzle('group', item).map(e => e.textContent))];
+        const groups = [...new Set(sizzle('group', item).map((e) => e.textContent))];
 
         if (contact) {
             // We only find out about requesting contacts via the
@@ -309,9 +325,9 @@ const RosterContacts = Collection.extend({
             // here, we know they aren't requesting anymore.
             contact.save({ subscription, ask, nickname, groups, 'requesting': null });
         } else {
-            this.create({ nickname, ask, groups, jid, subscription }, {sort: false});
+            this.create({ nickname, ask, groups, jid, subscription }, { sort: false });
         }
-    },
+    }
 
     createRequestingContact (presence) {
         const bare_jid = Strophe.getBareJidFromJid(presence.getAttribute('from'));
@@ -321,7 +337,7 @@ const RosterContacts = Collection.extend({
             'subscription': 'none',
             'ask': null,
             'requesting': true,
-            'nickname': nickname
+            'nickname': nickname,
         };
         /**
          * Triggered when someone has requested to subscribe to your presence (i.e. to be your contact).
@@ -330,7 +346,7 @@ const RosterContacts = Collection.extend({
          * @example _converse.api.listen.on('contactRequest', contact => { ... });
          */
         api.trigger('contactRequest', this.create(user_data));
-    },
+    }
 
     handleIncomingSubscription (presence) {
         const jid = presence.getAttribute('from'),
@@ -339,34 +355,32 @@ const RosterContacts = Collection.extend({
 
         if (!api.settings.get('allow_contact_requests')) {
             const { __ } = _converse;
-            rejectPresenceSubscription(
-                jid,
-                __("This client does not allow presence subscriptions")
-            );
+            rejectPresenceSubscription(jid, __('This client does not allow presence subscriptions'));
         }
         if (api.settings.get('auto_subscribe')) {
-            if ((!contact) || (contact.get('subscription') !== 'to')) {
+            if (!contact || contact.get('subscription') !== 'to') {
                 this.subscribeBack(bare_jid, presence);
             } else {
                 contact.authorize();
             }
         } else {
             if (contact) {
-                if (contact.get('subscription') !== 'none')  {
+                if (contact.get('subscription') !== 'none') {
                     contact.authorize();
-                } else if (contact.get('ask') === "subscribe") {
+                } else if (contact.get('ask') === 'subscribe') {
                     contact.authorize();
                 }
             } else {
                 this.createRequestingContact(presence);
             }
         }
-    },
+    }
 
+    // eslint-disable-next-line class-methods-use-this
     handleOwnPresence (presence) {
         const jid = presence.getAttribute('from'),
-              resource = Strophe.getResourceFromJid(jid),
-              presence_type = presence.getAttribute('type');
+            resource = Strophe.getResourceFromJid(jid),
+            presence_type = presence.getAttribute('type');
 
         if ((api.connection.get().jid !== jid) &&
                 (presence_type !== 'unavailable') &&
@@ -376,7 +390,7 @@ const RosterContacts = Collection.extend({
             // synchronize_availability option set to update,
             // we'll update ours as well.
             const show = presence.querySelector('show')?.textContent || 'online';
-            _converse.xmppstatus.save({'status': show}, {'silent': true});
+            _converse.xmppstatus.save({ 'status': show }, { 'silent': true });
 
             const status_message = presence.querySelector('status')?.textContent;
             if (status_message) _converse.xmppstatus.save({ status_message });
@@ -398,7 +412,7 @@ const RosterContacts = Collection.extend({
             // otherwise we're treated as offline.
             api.user.presence.send();
         }
-    },
+    }
 
     presenceHandler (presence) {
         const presence_type = presence.getAttribute('type');
@@ -416,7 +430,7 @@ const RosterContacts = Collection.extend({
 
         if (contact) {
             const status = presence.querySelector('status')?.textContent;
-            if (contact.get('status') !== status) contact.save({status});
+            if (contact.get('status') !== status) contact.save({ status });
         }
 
         if (presence_type === 'subscribed' && contact) {
@@ -435,6 +449,6 @@ const RosterContacts = Collection.extend({
             contact.presence.addResource(presence);
         }
     }
-});
+}
 
 export default RosterContacts;

+ 2 - 1
src/headless/plugins/roster/index.js

@@ -8,7 +8,8 @@ import RosterContacts from './contacts.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import roster_api from './api.js';
-import { Presence, Presences } from './presence.js';
+import Presence from './presence.js';
+import Presences from './presences.js';
 import {
     onChatBoxesInitialized,
     onClearSession,

+ 2 - 8
src/headless/plugins/roster/presence.js

@@ -1,14 +1,10 @@
-import { Collection } from "@converse/skeletor/src/collection";
+import Resources from "./resources.js";
 import { Model } from '@converse/skeletor/src/model.js';
 import { converse } from '../../shared/api/index.js';
 import { initStorage } from '../../utils/storage.js';
 
 const { Strophe, dayjs, sizzle } = converse.env;
 
-export const Resource = Model.extend({'idAttribute': 'name'});
-export const Resources = Collection.extend({'model': Resource});
-
-
 class Presence extends Model {
     get idAttribute () { // eslint-disable-line class-methods-use-this
         return 'jid';
@@ -84,6 +80,4 @@ class Presence extends Model {
     }
 }
 
-export { Presence };
-
-export const Presences = Collection.extend({'model': Presence });
+export default Presence;

+ 12 - 0
src/headless/plugins/roster/presences.js

@@ -0,0 +1,12 @@
+import { Collection } from "@converse/skeletor/src/collection";
+import Presence from "./presence.js";
+
+class Presences extends Collection {
+
+    constructor () {
+        super();
+        this.model = Presence;
+    }
+}
+
+export default Presences;

+ 9 - 0
src/headless/plugins/roster/resource.js

@@ -0,0 +1,9 @@
+import { Model } from '@converse/skeletor/src/model.js';
+
+class Resource extends Model {
+    get idAttribute () { // eslint-disable-line class-methods-use-this
+        return 'name';
+    }
+}
+
+export default Resource;

+ 12 - 0
src/headless/plugins/roster/resources.js

@@ -0,0 +1,12 @@
+import { Collection } from "@converse/skeletor/src/collection";
+import Resource from "./resource";
+
+class Resources extends Collection {
+
+    constructor () {
+        super();
+        this.model = Resource;
+    }
+}
+
+export default Resources;

+ 2 - 8
src/headless/plugins/vcard/index.js

@@ -7,7 +7,7 @@ import VCard from './vcard.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import vcard_api from './api.js';
-import { Collection } from "@converse/skeletor/src/collection";
+import VCards from "./vcards";
 import {
     clearVCardsSession,
     initVCardCollection,
@@ -73,13 +73,7 @@ converse.plugins.add('converse-vcard', {
         api.promises.add('VCardsInitialized');
 
         _converse.VCard = VCard;
-
-        _converse.VCards = Collection.extend({
-            model: _converse.VCard,
-            initialize () {
-                this.on('add', v => v.get('jid') && api.vcard.update(v));
-            }
-        });
+        _converse.VCards = VCards;
 
         api.listen.on('chatRoomInitialized', (m) => {
             setVCardOnModel(m)

+ 17 - 0
src/headless/plugins/vcard/vcards.js

@@ -0,0 +1,17 @@
+import VCard from "./vcard";
+import { Collection } from "@converse/skeletor/src/collection";
+import { api } from "../../index.js";
+
+class VCards extends Collection {
+
+    constructor () {
+        super();
+        this.model = VCard;
+    }
+
+    initialize () {
+        this.on('add', v => v.get('jid') && api.vcard.update(v));
+    }
+}
+
+export default VCards;

+ 4 - 1
src/headless/shared/api/index.js

@@ -15,8 +15,9 @@ import { settings_api } from '../settings/api.js';
  * sensitive data which should be kept off-limits to other 3rd-party scripts
  * that might be running in the page.
  *
- * @namespace _converse.api
  * @memberOf _converse
+ * @namespace api
+ * @property {Object} disco
  */
 const api = {
     connection: connection_api,
@@ -25,6 +26,8 @@ const api = {
     ...user_api,
     ...events_api,
     ...promise_api,
+
+    disco: null,
 };
 
 export default api;

+ 0 - 16
src/plugins/controlbox/index.js

@@ -32,22 +32,6 @@ converse.plugins.add('converse-controlbox', {
         return !_converse.api.settings.get('singleton');
     },
 
-    // Overrides mentioned here will be picked up by converse.js's
-    // plugin architecture they will replace existing methods on the
-    // relevant objects or classes.
-    // New functions which don't exist yet can also be added.
-    overrides: {
-        ChatBoxes: {
-            model (attrs, options) {
-                if (attrs && attrs.id == 'controlbox') {
-                    return new ControlBox(attrs, options);
-                } else {
-                    return this.__super__.model.apply(this, arguments);
-                }
-            }
-        }
-    },
-
     initialize () {
         api.settings.extend({
             allow_logout: true,

+ 1 - 1
src/plugins/muc-views/tests/http-file-upload.js

@@ -106,7 +106,7 @@ describe("XEP-0363: HTTP File Upload", function () {
                         const el = await u.waitUntil(() => view.querySelector('.chat-content progress'));
                         expect(el.getAttribute('value')).toBe('0');
                         message.set('progress', 0.5);
-                        await u.waitUntil(() => view.querySelector('.chat-content progress').getAttribute('value') === '0.5')
+                        await u.waitUntil(() => view.querySelector('.chat-content progress')?.getAttribute('value') === '0.5')
                         message.set('progress', 1);
                         await u.waitUntil(() => view.querySelector('.chat-content progress')?.getAttribute('value') === '1')
                         message.save({

+ 8 - 6
src/plugins/omemo/devicelists.js

@@ -1,11 +1,13 @@
 import DeviceList from './devicelist.js';
 import { Collection } from '@converse/skeletor/src/collection';
 
-/**
- * @class
- * @namespace _converse.DeviceLists
- * @memberOf _converse
- */
-const DeviceLists = Collection.extend({ model: DeviceList });
+class DeviceLists extends Collection {
+
+    constructor () {
+        super();
+        this.model = DeviceList;
+    }
+
+}
 
 export default DeviceLists;

+ 9 - 1
src/plugins/omemo/devices.js

@@ -1,4 +1,12 @@
 import Device from './device.js';
 import { Collection } from '@converse/skeletor/src/collection';
 
-export default Collection.extend({ model: Device });
+class Devices extends Collection {
+
+    constructor () {
+        super();
+        this.model = Device;
+    }
+}
+
+export default Devices;

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff