瀏覽代碼

Add an `exports` attribute on the `_converse` object.

JC Brand 1 年之前
父節點
當前提交
4c4101dbe0

+ 4 - 0
CHANGES.md

@@ -10,6 +10,10 @@
 
 - Remove the old `_converse.BootstrapModal` in favor of `_converse.BaseModal` which is a web component.
 - The connection is no longer available on the `_converse` object. Instead, use `api.connection.get()`.
+- Add a new `exports` attribute on the `_converse` object which is meant for
+  providing access for 3rd party plugins to code (e.g. classes) from
+  converse.js. Some classes that were on the `_converse` object, like
+  `CustomElement` are not on `_converse.exports`.
 
 ## 10.1.6 (2023-08-31)
 

+ 4 - 1
src/headless/plugins/chat/index.js

@@ -39,7 +39,10 @@ converse.plugins.add('converse-chat', {
             'send_chat_state_notifications': true,
         });
 
-        Object.assign(_converse, { ChatBox, Message, Messages, handleMessageStanza });
+        const exports = { ChatBox, Message, Messages, handleMessageStanza };
+        Object.assign(_converse, exports); // TODO: DEPRECATED
+        Object.assign(_converse.exports, exports);
+
         Object.assign(api, chat_api);
 
         api.chatboxes.registry.add(PRIVATE_CHAT_TYPE, ChatBox);

+ 5 - 8
src/headless/plugins/chat/model.js

@@ -886,8 +886,8 @@ class ChatBox extends ModelWithContact {
      * will be editable. Otherwise no messages will be editable.
      * @method ChatBox#setEditable
      * @memberOf ChatBox
-     * @param { Object } attrs An object containing message attributes.
-     * @param { String } send_time - time when the message was sent
+     * @param {Object} attrs An object containing message attributes.
+     * @param {String} send_time - time when the message was sent
      */
     setEditable (attrs, send_time) {
         if (attrs.is_headline || isEmptyMessage(attrs) || attrs.sender !== 'me') {
@@ -906,10 +906,8 @@ class ChatBox extends ModelWithContact {
      * Queue the creation of a message, to make sure that we don't run
      * into a race condition whereby we're creating a new message
      * before the collection has been fetched.
-     * @async
-     * @private
      * @method ChatBox#createMessage
-     * @param { Object } attrs
+     * @param {Object} attrs
      */
     async createMessage (attrs, options) {
         attrs.time = attrs.time || (new Date()).toISOString();
@@ -919,11 +917,10 @@ class ChatBox extends ModelWithContact {
 
     /**
      * Responsible for sending off a text message inside an ongoing chat conversation.
-     * @private
      * @method ChatBox#sendMessage
      * @memberOf ChatBox
-     * @param { Object } [attrs] - A map of attributes to be saved on the message
-     * @returns { Promise<Message> }
+     * @param {Object} [attrs] - A map of attributes to be saved on the message
+     * @returns {Promise<Message>}
      * @example
      * const chat = api.chats.get('buddy1@example.org');
      * chat.sendMessage({'body': 'hello world'});

+ 5 - 1
src/headless/plugins/muc/index.js

@@ -180,12 +180,16 @@ converse.plugins.add('converse-muc', {
         routeToRoom();
         addEventListener('hashchange', routeToRoom);
 
+        // TODO: DEPRECATED
         _converse.ChatRoom = MUC;
         _converse.ChatRoomMessage = MUCMessage;
         _converse.ChatRoomOccupants = ChatRoomOccupants;
         _converse.ChatRoomOccupant = ChatRoomOccupant;
 
-        api.chatboxes.registry.add(CHATROOMS_TYPE, MUC);
+        const exports = { MUC, MUCMessage, ChatRoomOccupants, ChatRoomOccupant };
+        Object.assign(_converse.exports, exports);
+
+        /** @type {module:shared-api.APIEndpoint} */(api.chatboxes.registry).add(CHATROOMS_TYPE, MUC);
 
         Object.assign(_converse, {
             getDefaultMUCNickname,

+ 17 - 17
src/headless/plugins/muc/muc.js

@@ -25,7 +25,7 @@ import { getUniqueId, safeSave } from '../../utils/index.js';
 import { isUniView } from '../../utils/session.js';
 import { parseMUCMessage, parseMUCPresence } from './parsers.js';
 import { sendMarker } from '../../shared/actions.js';
-import { shouldCreateGroupchatMessage } from './utils.js';
+import { shouldCreateGroupchatMessage, isInfoVisible } from './utils.js';
 
 const { u } = converse.env;
 
@@ -316,7 +316,7 @@ class MUC extends ChatBox {
 
     onOccupantAdded (occupant) {
         if (
-            _converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.ENTERED) &&
+            isInfoVisible(converse.MUC_TRAFFIC_STATES.ENTERED) &&
             this.session.get('connection_status') === ROOMSTATUS.ENTERED &&
             occupant.get('show') === 'online'
         ) {
@@ -326,7 +326,7 @@ class MUC extends ChatBox {
 
     onOccupantRemoved (occupant) {
         if (
-            _converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.EXITED) &&
+            isInfoVisible(converse.MUC_TRAFFIC_STATES.EXITED) &&
             this.isEntered() &&
             occupant.get('show') === 'online'
         ) {
@@ -338,9 +338,9 @@ class MUC extends ChatBox {
         if (occupant.get('states').includes('303')) {
             return;
         }
-        if (occupant.get('show') === 'offline' && _converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.EXITED)) {
+        if (occupant.get('show') === 'offline' && isInfoVisible(converse.MUC_TRAFFIC_STATES.EXITED)) {
             this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.EXITED);
-        } else if (occupant.get('show') === 'online' && _converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.ENTERED)) {
+        } else if (occupant.get('show') === 'online' && isInfoVisible(converse.MUC_TRAFFIC_STATES.ENTERED)) {
             this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.ENTERED);
         }
     }
@@ -2390,19 +2390,19 @@ class MUC extends ChatBox {
         }
 
         const current_affiliation = occupant.get('affiliation');
-        if (previous_affiliation === 'admin' && _converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXADMIN)) {
+        if (previous_affiliation === 'admin' && isInfoVisible(converse.AFFILIATION_CHANGES.EXADMIN)) {
             this.createMessage({
                 'type': 'info',
                 'message': __('%1$s is no longer an admin of this groupchat', occupant.get('nick'))
             });
-        } else if (previous_affiliation === 'owner' && _converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXOWNER)) {
+        } else if (previous_affiliation === 'owner' && isInfoVisible(converse.AFFILIATION_CHANGES.EXOWNER)) {
             this.createMessage({
                 'type': 'info',
                 'message': __('%1$s is no longer an owner of this groupchat', occupant.get('nick'))
             });
         } else if (
             previous_affiliation === 'outcast' &&
-            _converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXOUTCAST)
+            isInfoVisible(converse.AFFILIATION_CHANGES.EXOUTCAST)
         ) {
             this.createMessage({
                 'type': 'info',
@@ -2413,7 +2413,7 @@ class MUC extends ChatBox {
         if (
             current_affiliation === 'none' &&
             previous_affiliation === 'member' &&
-            _converse.isInfoVisible(converse.AFFILIATION_CHANGES.EXMEMBER)
+            isInfoVisible(converse.AFFILIATION_CHANGES.EXMEMBER)
         ) {
             this.createMessage({
                 'type': 'info',
@@ -2421,14 +2421,14 @@ class MUC extends ChatBox {
             });
         }
 
-        if (current_affiliation === 'member' && _converse.isInfoVisible(converse.AFFILIATION_CHANGES.MEMBER)) {
+        if (current_affiliation === 'member' && isInfoVisible(converse.AFFILIATION_CHANGES.MEMBER)) {
             this.createMessage({
                 'type': 'info',
                 'message': __('%1$s is now a member of this groupchat', occupant.get('nick'))
             });
         } else if (
-            (current_affiliation === 'admin' && _converse.isInfoVisible(converse.AFFILIATION_CHANGES.ADMIN)) ||
-            (current_affiliation == 'owner' && _converse.isInfoVisible(converse.AFFILIATION_CHANGES.OWNER))
+            (current_affiliation === 'admin' && isInfoVisible(converse.AFFILIATION_CHANGES.ADMIN)) ||
+            (current_affiliation == 'owner' && isInfoVisible(converse.AFFILIATION_CHANGES.OWNER))
         ) {
             // For example: AppleJack is now an (admin|owner) of this groupchat
             this.createMessage({
@@ -2444,17 +2444,17 @@ class MUC extends ChatBox {
             return;
         }
         const previous_role = occupant._previousAttributes.role;
-        if (previous_role === 'moderator' && _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.DEOP)) {
+        if (previous_role === 'moderator' && isInfoVisible(converse.MUC_ROLE_CHANGES.DEOP)) {
             this.updateNotifications(occupant.get('nick'), converse.MUC_ROLE_CHANGES.DEOP);
-        } else if (previous_role === 'visitor' && _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.VOICE)) {
+        } else if (previous_role === 'visitor' && isInfoVisible(converse.MUC_ROLE_CHANGES.VOICE)) {
             this.updateNotifications(occupant.get('nick'), converse.MUC_ROLE_CHANGES.VOICE);
         }
-        if (occupant.get('role') === 'visitor' && _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.MUTE)) {
+        if (occupant.get('role') === 'visitor' && isInfoVisible(converse.MUC_ROLE_CHANGES.MUTE)) {
             this.updateNotifications(occupant.get('nick'), converse.MUC_ROLE_CHANGES.MUTE);
         } else if (occupant.get('role') === 'moderator') {
             if (
                 !['owner', 'admin'].includes(occupant.get('affiliation')) &&
-                _converse.isInfoVisible(converse.MUC_ROLE_CHANGES.OP)
+                isInfoVisible(converse.MUC_ROLE_CHANGES.OP)
             ) {
                 // Oly show this message if the user isn't already
                 // an admin or owner, otherwise this isn't new information.
@@ -2474,7 +2474,7 @@ class MUC extends ChatBox {
     createInfoMessage (code, stanza, is_self) {
         const __ = _converse.__;
         const data = { 'type': 'info', 'is_ephemeral': true };
-        if (!_converse.isInfoVisible(code)) {
+        if (!isInfoVisible(code)) {
             return;
         }
         if (code === '110' || (code === '100' && !is_self)) {

+ 9 - 1
src/headless/shared/_converse.js

@@ -70,9 +70,17 @@ class ConversePrivateGlobal extends EventEmitter(Object) {
         this.TIMEOUTS =  {
             PAUSED: 10000,
             INACTIVE: 90000
-        }
+        };
 
         this.chatboxes = null;
+
+        /**
+         * Namespace for storing code that might be useful to 3rd party
+         * plugins. We want to make it possible for 3rd party plugins to have
+         * access to code (e.g. classes) from converse.js without having to add
+         * converse.js as a dependency.
+         */
+        this.exports = /** @type { Record<string, Object> } */{};
     }
 
     /**

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

@@ -1,3 +1,7 @@
+/**
+ * @typedef {import('../_converse.js').default} _converse
+ * @typedef {module:shared-api.APIEndpoint} APIEndpoint
+ */
 import connection_api from '../connection/api.js';
 import events_api from './events.js';
 import promise_api from './promise.js';
@@ -17,7 +21,10 @@ import { settings_api } from '../settings/api.js';
  *
  * @memberOf _converse
  * @namespace api
- * @property {Object} disco
+ * @typedef {Record<string, Function>} module:shared-api.APIEndpoint
+ * @typedef {Record<string, APIEndpoint|Function>} APINamespace
+ * @typedef {Record<string, APINamespace|APIEndpoint|Function>} API
+ * @type {API}
  */
 const api = {
     connection: connection_api,

+ 1 - 2
src/index.js

@@ -38,8 +38,7 @@ import "./plugins/dragresize/index.js";     // Allows chat boxes to be resized b
 import "./plugins/fullscreen/index.js";
 /* END: Removable components */
 
-
-_converse.CustomElement = CustomElement;
+_converse.exports.CustomElement = CustomElement;
 
 const initialize = converse.initialize;
 

+ 8 - 7
src/utils/html.js

@@ -19,6 +19,7 @@ import u from '../headless/utils/index.js';
 import { converse, log } from '@converse/headless';
 import { getURI, isAudioURL, isImageURL, isVideoURL } from '@converse/headless/utils/url.js';
 import { render } from 'lit';
+import { queryChildren } from '@converse/headless/utils/html.js';
 
 const { sizzle, Strophe } = converse.env;
 
@@ -527,15 +528,15 @@ u.fadeIn = function (el, callback) {
  * Takes an XML field in XMPP XForm (XEP-004: Data Forms) format returns a
  * [TemplateResult](https://lit.polymer-project.org/api/classes/_lit_html_.templateresult.html).
  * @method u#xForm2TemplateResult
- * @param { Element } field - the field to convert
- * @param { Element } stanza - the containing stanza
- * @param { Object } options
- * @returns { TemplateResult }
+ * @param {HTMLElement} field - the field to convert
+ * @param {Element} stanza - the containing stanza
+ * @param {Object} options
+ * @returns {import('lit').TemplateResult}
  */
-u.xForm2TemplateResult = function (field, stanza, options={}) {
+export function xForm2TemplateResult (field, stanza, options={}) {
     if (field.getAttribute('type') === 'list-single' || field.getAttribute('type') === 'list-multi') {
-        const values = u.queryChildren(field, 'value').map(el => el?.textContent);
-        const options = u.queryChildren(field, 'option').map(option => {
+        const values = queryChildren(field, 'value').map(el => el?.textContent);
+        const options = queryChildren(field, 'option').map(/** @type {HTMLElement} */(option) => {
             const value = option.querySelector('value')?.textContent;
             return {
                 'value': value,