浏览代码

Move MUC functions to utils

JC Brand 3 年之前
父节点
当前提交
7e88ffd098
共有 2 个文件被更改,包括 249 次插入227 次删除
  1. 35 226
      src/headless/plugins/muc/index.js
  2. 214 1
      src/headless/plugins/muc/utils.js

+ 35 - 226
src/headless/plugins/muc/index.js

@@ -11,12 +11,23 @@ import ChatRoomMixin from './muc.js';
 import ChatRoomOccupant from './occupant.js';
 import ChatRoomOccupants from './occupants.js';
 import affiliations_api from './affiliations/api.js';
-import isObject from 'lodash-es/isObject';
-import log from '@converse/headless/log';
 import muc_api from './api.js';
-import u from '../../utils/form';
 import { Collection } from '@converse/skeletor/src/collection';
 import { _converse, api, converse } from '../../core.js';
+import {
+    autoJoinRooms,
+    disconnectChatRooms,
+    getDefaultMUCNickname,
+    isInfoVisible,
+    onAddClientFeatures,
+    onBeforeResourceBinding,
+    onBeforeTearDown,
+    onDirectMUCInvitation,
+    onStatusInitialized,
+    onWindowStateChanged,
+    registerDirectInvitationHandler,
+    routeToRoom,
+} from './utils.js';
 import { computeAffiliationsDelta } from './affiliations/utils.js';
 
 export const ROLES = ['moderator', 'participant', 'visitor'];
@@ -29,7 +40,7 @@ converse.AFFILIATION_CHANGES = {
     EXADMIN: 'exadmin',
     EXOWNER: 'exowner',
     EXOUTCAST: 'exoutcast',
-    EXMEMBER: 'exmember'
+    EXMEMBER: 'exmember',
 };
 converse.AFFILIATION_CHANGES_LIST = Object.values(converse.AFFILIATION_CHANGES);
 converse.MUC_TRAFFIC_STATES = { ENTERED: 'entered', EXITED: 'exited' };
@@ -48,10 +59,10 @@ converse.MUC.INFO_CODES = {
     'disconnected': ['301', '307', '321', '322', '332', '333'],
     'affiliation_changes': [...converse.AFFILIATION_CHANGES_LIST],
     'join_leave_events': [...converse.MUC_TRAFFIC_STATES_LIST],
-    'role_changes': [...converse.MUC_ROLE_CHANGES_LIST]
+    'role_changes': [...converse.MUC_ROLE_CHANGES_LIST],
 };
 
-const { Strophe, sizzle } = converse.env;
+const { Strophe } = converse.env;
 
 // Add Strophe Namespaces
 Strophe.addNamespace('MUC_ADMIN', Strophe.NS.MUC + '#admin');
@@ -60,6 +71,7 @@ Strophe.addNamespace('MUC_REGISTER', 'jabber:iq:register');
 Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + '#roomconfig');
 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.MUC_NICK_CHANGED_CODE = '303';
 
@@ -76,7 +88,7 @@ converse.ROOM_FEATURES = [
     'semianonymous',
     'moderated',
     'unmoderated',
-    'mam_enabled'
+    'mam_enabled',
 ];
 
 // No longer used in code, but useful as reference.
@@ -104,92 +116,9 @@ converse.ROOMSTATUS = {
     DISCONNECTED: 4,
     ENTERED: 5,
     DESTROYED: 6,
-    BANNED: 7
+    BANNED: 7,
 };
 
-
-function registerDirectInvitationHandler () {
-    _converse.connection.addHandler(
-        message => {
-            _converse.onDirectMUCInvitation(message);
-            return true;
-        },
-        'jabber:x:conference',
-        'message'
-    );
-}
-
-function disconnectChatRooms () {
-    /* When disconnecting, mark all groupchats as
-     * disconnected, so that they will be properly entered again
-     * when fetched from session storage.
-     */
-    return _converse.chatboxes
-        .filter(m => m.get('type') === _converse.CHATROOMS_TYPE)
-        .forEach(m => m.session.save({ 'connection_status': converse.ROOMSTATUS.DISCONNECTED }));
-}
-
-async function onWindowStateChanged (data) {
-    if (data.state === 'visible' && api.connection.connected()) {
-        const rooms = await api.rooms.get();
-        rooms.forEach(room => room.rejoinIfNecessary());
-    }
-}
-
-async function routeToRoom (jid) {
-    if (!u.isValidMUCJID(jid)) {
-        return log.warn(`invalid jid "${jid}" provided in url fragment`);
-    }
-    await api.waitUntil('roomsAutoJoined');
-    if (api.settings.get('allow_bookmarks')) {
-        await api.waitUntil('bookmarksInitialized');
-    }
-    api.rooms.open(jid);
-}
-
-/* Opens a groupchat, making sure that certain attributes
- * are correct, for example that the "type" is set to
- * "chatroom".
- */
-async function openChatRoom (jid, settings) {
-    settings.type = _converse.CHATROOMS_TYPE;
-    settings.id = jid;
-    const chatbox = await api.rooms.get(jid, settings, true);
-    chatbox.maybeShow(true);
-    return chatbox;
-}
-
-/* Automatically join groupchats, based on the
- * "auto_join_rooms" configuration setting, which is an array
- * of strings (groupchat JIDs) or objects (with groupchat JID and other settings).
- */
-async function autoJoinRooms () {
-    await Promise.all(
-        api.settings.get('auto_join_rooms').map(muc => {
-            if (typeof muc === 'string') {
-                if (_converse.chatboxes.where({ 'jid': muc }).length) {
-                    return Promise.resolve();
-                }
-                return api.rooms.open(muc);
-            } else if (isObject(muc)) {
-                return api.rooms.open(muc.jid, { ...muc });
-            } else {
-                log.error('Invalid muc criteria specified for "auto_join_rooms"');
-                return Promise.resolve();
-            }
-        })
-    );
-    /**
-     * Triggered once any rooms that have been configured to be automatically joined,
-     * specified via the _`auto_join_rooms` setting, have been entered.
-     * @event _converse#roomsAutoJoined
-     * @example _converse.api.listen.on('roomsAutoJoined', () => { ... });
-     * @example _converse.api.waitUntil('roomsAutoJoined').then(() => { ... });
-     */
-    api.trigger('roomsAutoJoined');
-}
-
-
 converse.plugins.add('converse-muc', {
     /* Optional dependencies are other plugins which might be
      * overridden or relied upon, and therefore need to be loaded before
@@ -214,8 +143,8 @@ converse.plugins.add('converse-muc', {
                 } else {
                     return this.__super__.model.apply(this, arguments);
                 }
-            }
-        }
+            },
+        },
     },
 
     initialize () {
@@ -253,7 +182,7 @@ converse.plugins.add('converse-muc', {
                 ...converse.MUC.INFO_CODES.disconnected,
                 ...converse.MUC.INFO_CODES.affiliation_changes,
                 ...converse.MUC.INFO_CODES.join_leave_events,
-                ...converse.MUC.INFO_CODES.role_changes
+                ...converse.MUC.INFO_CODES.role_changes,
             ],
             'muc_show_logs_before_join': false,
             'muc_show_ogp_unfurls': true,
@@ -305,13 +234,13 @@ converse.plugins.add('converse-muc', {
                 172: __('This groupchat is now no longer anonymous'),
                 173: __('This groupchat is now semi-anonymous'),
                 174: __('This groupchat is now fully-anonymous'),
-                201: __('A new groupchat has been created')
+                201: __('A new groupchat has been created'),
             },
 
             new_nickname_messages: {
                 // XXX: Note the triple underscore function and not double underscore.
                 210: ___('Your nickname has been automatically set to %1$s'),
-                303: ___('Your nickname has been changed to %1$s')
+                303: ___('Your nickname has been changed to %1$s'),
             },
 
             disconnect_messages: {
@@ -322,22 +251,8 @@ converse.plugins.add('converse-muc', {
                 322: __(
                     "You have been removed from this groupchat because the groupchat has changed to members-only and you're not a member"
                 ),
-                332: __('You have been removed from this groupchat because the service hosting it is being shut down')
-            }
-        };
-
-        /**
-         * Determines info message visibility based on
-         * muc_show_info_messages configuration setting
-         * @param {*} code
-         * @memberOf _converse
-         */
-        _converse.isInfoVisible = function (code) {
-            const info_messages = api.settings.get('muc_show_info_messages');
-            if (info_messages.includes(code)) {
-                return true;
-            }
-            return false;
+                332: __('You have been removed from this groupchat because the service hosting it is being shut down'),
+            },
         };
 
         _converse.router.route('converse/room?jid=:jid', routeToRoom);
@@ -347,22 +262,6 @@ converse.plugins.add('converse-muc', {
         _converse.ChatRoomOccupants = ChatRoomOccupants;
         _converse.ChatRoomOccupant = ChatRoomOccupant;
 
-        _converse.getDefaultMUCNickname = function () {
-            // XXX: if anything changes here, update the docs for the
-            // locked_muc_nickname setting.
-            if (!_converse.xmppstatus) {
-                throw new Error(
-                    "Can't call _converse.getDefaultMUCNickname before the statusInitialized has been fired."
-                );
-            }
-            const nick = _converse.xmppstatus.getNickname();
-            if (nick) {
-                return nick;
-            } else if (api.settings.get('muc_nickname_from_jid')) {
-                return Strophe.unescapeNode(Strophe.getNodeFromJid(_converse.bare_jid));
-            }
-        };
-
         /**
          * Collection which stores MUC messages
          * @class
@@ -371,113 +270,23 @@ converse.plugins.add('converse-muc', {
          */
         _converse.ChatRoomMessages = Collection.extend({
             model: _converse.ChatRoomMessage,
-            comparator: 'time'
+            comparator: 'time',
         });
 
+        Object.assign(_converse, { getDefaultMUCNickname, isInfoVisible, onDirectMUCInvitation });
 
-        /**
-         * A direct MUC invitation to join a groupchat has been received
-         * See XEP-0249: Direct MUC invitations.
-         * @private
-         * @method _converse.ChatRoom#onDirectMUCInvitation
-         * @param { XMLElement } message - The message stanza containing the invitation.
-         */
-        _converse.onDirectMUCInvitation = async function (message) {
-            const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
-                from = Strophe.getBareJidFromJid(message.getAttribute('from')),
-                room_jid = x_el.getAttribute('jid'),
-                reason = x_el.getAttribute('reason');
-
-            let result;
-            if (api.settings.get('auto_join_on_invite')) {
-                result = true;
-            } else {
-                // Invite request might come from someone not your roster list
-                let contact = _converse.roster.get(from);
-                contact = contact ? contact.getDisplayName() : from;
-                if (!reason) {
-                    result = confirm(__('%1$s has invited you to join a groupchat: %2$s', contact, room_jid));
-                } else {
-                    result = confirm(
-                        __(
-                            '%1$s has invited you to join a groupchat: %2$s, and left the following reason: "%3$s"',
-                            contact,
-                            room_jid,
-                            reason
-                        )
-                    );
-                }
-            }
-            if (result === true) {
-                const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') });
-                if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
-                    _converse.chatboxes.get(room_jid).rejoin();
-                }
-            }
-        };
 
         /************************ BEGIN Event Handlers ************************/
-
         if (api.settings.get('allow_muc_invitations')) {
             api.listen.on('connected', registerDirectInvitationHandler);
             api.listen.on('reconnected', registerDirectInvitationHandler);
         }
-
-        api.listen.on('beforeTearDown', () => {
-            const groupchats = _converse.chatboxes.where({ 'type': _converse.CHATROOMS_TYPE });
-            groupchats.forEach(muc =>
-                u.safeSave(muc.session, { 'connection_status': converse.ROOMSTATUS.DISCONNECTED })
-            );
-        });
-
-        api.listen.on('windowStateChanged', onWindowStateChanged);
-
-        api.listen.on('addClientFeatures', () => {
-            if (api.settings.get('allow_muc')) {
-                api.disco.own.features.add(Strophe.NS.MUC);
-            }
-            if (api.settings.get('allow_muc_invitations')) {
-                api.disco.own.features.add('jabber:x:conference'); // Invites
-            }
-        });
+        api.listen.on('addClientFeatures', onAddClientFeatures);
+        api.listen.on('beforeResourceBinding', onBeforeResourceBinding);
+        api.listen.on('beforeTearDown', onBeforeTearDown);
         api.listen.on('chatBoxesFetched', autoJoinRooms);
-
-        api.listen.on('beforeResourceBinding', () => {
-            _converse.connection.addHandler(
-                stanza => {
-                    const muc_jid = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
-                    if (!_converse.chatboxes.get(muc_jid)) {
-                        api.waitUntil('chatBoxesFetched').then(async () => {
-                            const muc = _converse.chatboxes.get(muc_jid);
-                            if (muc) {
-                                await muc.initialized;
-                                muc.message_handler.run(stanza);
-                            }
-                        });
-                    }
-                    return true;
-                },
-                null,
-                'message',
-                'groupchat'
-            );
-        });
-
         api.listen.on('disconnected', disconnectChatRooms);
-
-        api.listen.on('statusInitialized', () => {
-            window.addEventListener(_converse.unloadevent, () => {
-                const using_websocket = api.connection.isType('websocket');
-                if (
-                    using_websocket &&
-                    (!api.settings.get('enable_smacks') || !_converse.session.get('smacks_stream_id'))
-                ) {
-                    // For non-SMACKS websocket connections, or non-resumeable
-                    // connections, we disconnect all chatrooms when the page unloads.
-                    // See issue #1111
-                    disconnectChatRooms();
-                }
-            });
-        });
-    }
+        api.listen.on('statusInitialized', onStatusInitialized);
+        api.listen.on('windowStateChanged', onWindowStateChanged);
+    },
 });

+ 214 - 1
src/headless/plugins/muc/utils.js

@@ -1,5 +1,10 @@
+import isObject from 'lodash-es/isObject';
+import log from "@converse/headless/log.js";
 import { ROLES } from '@converse/headless/plugins/muc/index.js';
-import { _converse, api } from '@converse/headless/core.js';
+import { _converse, api, converse } from '@converse/headless/core.js';
+import { safeSave } from '@converse/headless/utils/core.js';
+
+const { Strophe, sizzle, u } = converse.env;
 
 /**
  * Given an occupant model, see which roles may be assigned to that user.
@@ -18,4 +23,212 @@ export function getAssignableRoles (occupant) {
     }
 }
 
+export function registerDirectInvitationHandler () {
+    _converse.connection.addHandler(
+        message => {
+            _converse.onDirectMUCInvitation(message);
+            return true;
+        },
+        'jabber:x:conference',
+        'message'
+    );
+}
+
+export function disconnectChatRooms () {
+    /* When disconnecting, mark all groupchats as
+     * disconnected, so that they will be properly entered again
+     * when fetched from session storage.
+     */
+    return _converse.chatboxes
+        .filter(m => m.get('type') === _converse.CHATROOMS_TYPE)
+        .forEach(m => m.session.save({ 'connection_status': converse.ROOMSTATUS.DISCONNECTED }));
+}
+
+export async function onWindowStateChanged (data) {
+    if (data.state === 'visible' && api.connection.connected()) {
+        const rooms = await api.rooms.get();
+        rooms.forEach(room => room.rejoinIfNecessary());
+    }
+}
+
+export async function routeToRoom (jid) {
+    if (!u.isValidMUCJID(jid)) {
+        return log.warn(`invalid jid "${jid}" provided in url fragment`);
+    }
+    await api.waitUntil('roomsAutoJoined');
+    if (api.settings.get('allow_bookmarks')) {
+        await api.waitUntil('bookmarksInitialized');
+    }
+    api.rooms.open(jid);
+}
+
+/* Opens a groupchat, making sure that certain attributes
+ * are correct, for example that the "type" is set to
+ * "chatroom".
+ */
+export async function openChatRoom (jid, settings) {
+    settings.type = _converse.CHATROOMS_TYPE;
+    settings.id = jid;
+    const chatbox = await api.rooms.get(jid, settings, true);
+    chatbox.maybeShow(true);
+    return chatbox;
+}
+
+
+/**
+ * A direct MUC invitation to join a groupchat has been received
+ * See XEP-0249: Direct MUC invitations.
+ * @private
+ * @method _converse.ChatRoom#onDirectMUCInvitation
+ * @param { XMLElement } message - The message stanza containing the invitation.
+ */
+export async function onDirectMUCInvitation (message) {
+    const { __ } = _converse;
+    const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
+        from = Strophe.getBareJidFromJid(message.getAttribute('from')),
+        room_jid = x_el.getAttribute('jid'),
+        reason = x_el.getAttribute('reason');
+
+    let result;
+    if (api.settings.get('auto_join_on_invite')) {
+        result = true;
+    } else {
+        // Invite request might come from someone not your roster list
+        let contact = _converse.roster.get(from);
+        contact = contact ? contact.getDisplayName() : from;
+        if (!reason) {
+            result = confirm(__('%1$s has invited you to join a groupchat: %2$s', contact, room_jid));
+        } else {
+            result = confirm(
+                __(
+                    '%1$s has invited you to join a groupchat: %2$s, and left the following reason: "%3$s"',
+                    contact,
+                    room_jid,
+                    reason
+                )
+            );
+        }
+    }
+    if (result === true) {
+        const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') });
+        if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
+            _converse.chatboxes.get(room_jid).rejoin();
+        }
+    }
+}
+
+export function getDefaultMUCNickname () {
+    // XXX: if anything changes here, update the docs for the
+    // locked_muc_nickname setting.
+    if (!_converse.xmppstatus) {
+        throw new Error(
+            "Can't call _converse.getDefaultMUCNickname before the statusInitialized has been fired."
+        );
+    }
+    const nick = _converse.xmppstatus.getNickname();
+    if (nick) {
+        return nick;
+    } else if (api.settings.get('muc_nickname_from_jid')) {
+        return Strophe.unescapeNode(Strophe.getNodeFromJid(_converse.bare_jid));
+    }
+}
+
+/**
+ * Determines info message visibility based on
+ * muc_show_info_messages configuration setting
+ * @param {*} code
+ * @memberOf _converse
+ */
+export function isInfoVisible (code) {
+    const info_messages = api.settings.get('muc_show_info_messages');
+    if (info_messages.includes(code)) {
+        return true;
+    }
+    return false;
+}
+
+
+/* Automatically join groupchats, based on the
+ * "auto_join_rooms" configuration setting, which is an array
+ * of strings (groupchat JIDs) or objects (with groupchat JID and other settings).
+ */
+export async function autoJoinRooms () {
+    await Promise.all(
+        api.settings.get('auto_join_rooms').map(muc => {
+            if (typeof muc === 'string') {
+                if (_converse.chatboxes.where({ 'jid': muc }).length) {
+                    return Promise.resolve();
+                }
+                return api.rooms.open(muc);
+            } else if (isObject(muc)) {
+                return api.rooms.open(muc.jid, { ...muc });
+            } else {
+                log.error('Invalid muc criteria specified for "auto_join_rooms"');
+                return Promise.resolve();
+            }
+        })
+    );
+    /**
+     * Triggered once any rooms that have been configured to be automatically joined,
+     * specified via the _`auto_join_rooms` setting, have been entered.
+     * @event _converse#roomsAutoJoined
+     * @example _converse.api.listen.on('roomsAutoJoined', () => { ... });
+     * @example _converse.api.waitUntil('roomsAutoJoined').then(() => { ... });
+     */
+    api.trigger('roomsAutoJoined');
+}
+
+
+export function onAddClientFeatures () {
+    if (api.settings.get('allow_muc')) {
+        api.disco.own.features.add(Strophe.NS.MUC);
+    }
+    if (api.settings.get('allow_muc_invitations')) {
+        api.disco.own.features.add('jabber:x:conference'); // Invites
+    }
+}
+
+export function onBeforeTearDown () {
+    _converse.chatboxes
+        .where({ 'type': _converse.CHATROOMS_TYPE })
+        .forEach(muc => safeSave(muc.session, { 'connection_status': converse.ROOMSTATUS.DISCONNECTED }));
+}
+
+export function onStatusInitialized () {
+    window.addEventListener(_converse.unloadevent, () => {
+        const using_websocket = api.connection.isType('websocket');
+        if (
+            using_websocket &&
+            (!api.settings.get('enable_smacks') || !_converse.session.get('smacks_stream_id'))
+        ) {
+            // For non-SMACKS websocket connections, or non-resumeable
+            // connections, we disconnect all chatrooms when the page unloads.
+            // See issue #1111
+            disconnectChatRooms();
+        }
+    });
+}
+
+export function onBeforeResourceBinding () {
+    _converse.connection.addHandler(
+        stanza => {
+            const muc_jid = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
+            if (!_converse.chatboxes.get(muc_jid)) {
+                api.waitUntil('chatBoxesFetched').then(async () => {
+                    const muc = _converse.chatboxes.get(muc_jid);
+                    if (muc) {
+                        await muc.initialized;
+                        muc.message_handler.run(stanza);
+                    }
+                });
+            }
+            return true;
+        },
+        null,
+        'message',
+        'groupchat'
+    );
+}
+
+
 Object.assign(_converse, { getAssignableRoles });