Browse Source

Move methods to utils

So that they can also be used independently of the components
JC Brand 4 years ago
parent
commit
d8cab006c8

+ 5 - 12
spec/chatbox.js

@@ -911,7 +911,7 @@ describe("Chatboxes", function () {
 
     describe("Special Messages", function () {
 
-        it("'/clear' can be used to clear messages in a conversation",
+        fit("'/clear' can be used to clear messages in a conversation",
                 mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
 
             await mock.waitForRoster(_converse, 'current');
@@ -925,28 +925,21 @@ describe("Chatboxes", function () {
             await mock.sendMessage(view, message);
 
             expect(view.model.messages.length === 1).toBeTruthy();
-            let stored_messages = await view.model.messages.browserStorage.findAll();
+            const stored_messages = await view.model.messages.browserStorage.findAll();
             expect(stored_messages.length).toBe(1);
             await u.waitUntil(() => view.querySelector('.chat-msg'));
 
             message = '/clear';
             const bottom_panel = view.querySelector('converse-chat-bottom-panel');
-            spyOn(bottom_panel, 'clearMessages').and.callThrough();
-            spyOn(window, 'confirm').and.callFake(function () {
-                return true;
-            });
+            spyOn(window, 'confirm').and.callFake(() => true);
             view.querySelector('.chat-textarea').value = message;
             bottom_panel.onKeyDown({
                 target: view.querySelector('textarea.chat-textarea'),
                 preventDefault: function preventDefault () {},
                 keyCode: 13
             });
-            expect(bottom_panel.clearMessages.calls.all().length).toBe(1);
-            await bottom_panel.clearMessages.calls.all()[0].returnValue;
-            expect(window.confirm).toHaveBeenCalled();
-            expect(view.model.messages.length, 0); // The messages must be removed from the chatbox
-            stored_messages = await view.model.messages.browserStorage.findAll();
-            expect(stored_messages.length).toBe(0);
+            expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to clear the messages from this conversation?');
+            await u.waitUntil(() => view.model.messages.length === 0);
             expect(_converse.api.trigger.calls.count(), 1);
             expect(_converse.api.trigger.calls.mostRecent().args, ['messageSend', message]);
             done();

+ 2 - 2
spec/muc.js

@@ -3294,13 +3294,13 @@ describe("Groupchats", function () {
             const textarea = await u.waitUntil(() => view.querySelector('.chat-textarea'));
             textarea.value = '/clear';
             const bottom_panel = view.querySelector('converse-muc-bottom-panel');
-            spyOn(bottom_panel, 'clearMessages');
+            spyOn(window, 'confirm').and.callFake(() => false);
             bottom_panel.onKeyDown({
                 target: textarea,
                 preventDefault: function preventDefault () {},
                 keyCode: 13
             });
-            expect(bottom_panel.clearMessages).toHaveBeenCalled();
+            expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to clear the messages from this conversation?');
             done();
         }));
 

+ 4 - 19
src/plugins/chatview/bottom_panel.js

@@ -4,6 +4,7 @@ import { ElementView } from '@converse/skeletor/src/element.js';
 import { __ } from 'i18n';
 import { _converse, api, converse } from "@converse/headless/core";
 import { html, render } from 'lit-html';
+import { clearMessages, parseMessageForCommands } from './utils.js';
 
 const { u } = converse.env;
 
@@ -109,29 +110,13 @@ export default class ChatBottomPanel extends ElementView {
         ev.preventDefault();
     }
 
-    async clearMessages (ev) {
+    clearMessages (ev) {
         ev?.preventDefault?.();
-        const result = confirm(__('Are you sure you want to clear the messages from this conversation?'));
-        if (result === true) {
-            await this.model.clearMessages();
-        }
-        return this;
+        clearMessages(this.model);
     }
 
     parseMessageForCommands (text) {
-        const match = text.replace(/^\s*/, '').match(/^\/(.*)\s*$/);
-        if (match) {
-            if (match[1] === 'clear') {
-                this.clearMessages();
-                return true;
-            } else if (match[1] === 'close') {
-                _converse.chatboxviews.get(this.getAttribute('jid'))?.close();
-                return true;
-            } else if (match[1] === 'help') {
-                this.model.set({ 'show_help_messages': true });
-                return true;
-            }
-        }
+        return parseMessageForCommands(this.model, text);
     }
 
     async onFormSubmitted () {

+ 26 - 0
src/plugins/chatview/utils.js

@@ -1,3 +1,5 @@
+import { __ } from 'i18n';
+import { _converse } from "@converse/headless/core";
 import { html } from 'lit-html';
 
 
@@ -21,3 +23,27 @@ export async function getHeadingStandaloneButton (promise_or_data) {
         ></a>
     `;
 }
+
+async function clearMessages (chat) {
+    const result = confirm(__('Are you sure you want to clear the messages from this conversation?'));
+    if (result === true) {
+        await chat.clearMessages();
+    }
+}
+
+
+export function parseMessageForCommands (chat, text) {
+    const match = text.replace(/^\s*/, '').match(/^\/(.*)\s*$/);
+    if (match) {
+        if (match[1] === 'clear') {
+            clearMessages(chat);
+            return true;
+        } else if (match[1] === 'close') {
+            _converse.chatboxviews.get(chat.get('jid'))?.close();
+            return true;
+        } else if (match[1] === 'help') {
+            chat.set({ 'show_help_messages': true });
+            return true;
+        }
+    }
+}

+ 2 - 208
src/plugins/muc-views/bottom_panel.js

@@ -3,29 +3,9 @@ import debounce from 'lodash/debounce';
 import tpl_muc_bottom_panel from './templates/muc-bottom-panel.js';
 import { __ } from 'i18n';
 import { _converse, api, converse } from "@converse/headless/core";
-import { getAutoCompleteListItem } from './utils.js';
+import { getAutoCompleteListItem, parseMessageForMUCCommands } from './utils.js';
 import { render } from 'lit-html';
 
-const { Strophe, $pres } = converse.env;
-
-
-const COMMAND_TO_AFFILIATION = {
-    'admin': 'admin',
-    'ban': 'outcast',
-    'member': 'member',
-    'owner': 'owner',
-    'revoke': 'none'
-};
-const COMMAND_TO_ROLE = {
-    'deop': 'participant',
-    'kick': 'none',
-    'mute': 'visitor',
-    'op': 'moderator',
-    'voice': 'participant'
-};
-
-const u = converse.env.utils;
-
 
 export default class MUCBottomPanel extends BottomPanel {
 
@@ -117,194 +97,8 @@ export default class MUCBottomPanel extends BottomPanel {
         super.onKeyUp(ev);
     }
 
-    setRole (command, args, required_affiliations = [], required_roles = []) {
-        /* Check that a command to change a groupchat user's role or
-         * affiliation has anough arguments.
-         */
-        const role = COMMAND_TO_ROLE[command];
-        if (!role) {
-            throw Error(`ChatRoomView#setRole called with invalid command: ${command}`);
-        }
-        if (!this.model.verifyAffiliations(required_affiliations) || !this.model.verifyRoles(required_roles)) {
-            return false;
-        }
-        if (!this.model.validateRoleOrAffiliationChangeArgs(command, args)) {
-            return false;
-        }
-        const nick_or_jid = this.model.getNickOrJIDFromCommandArgs(args);
-        if (!nick_or_jid) {
-            return false;
-        }
-        const reason = args.split(nick_or_jid, 2)[1].trim();
-        // We're guaranteed to have an occupant due to getNickOrJIDFromCommandArgs
-        const occupant = this.model.getOccupant(nick_or_jid);
-        this.model.setRole(occupant, role, reason, undefined, this.model.onCommandError.bind(this));
-        return true;
-    }
-
-    setAffiliation (command, args, required_affiliations) {
-        const affiliation = COMMAND_TO_AFFILIATION[command];
-        if (!affiliation) {
-            throw Error(`ChatRoomView#setAffiliation called with invalid command: ${command}`);
-        }
-        if (!this.model.verifyAffiliations(required_affiliations)) {
-            return false;
-        }
-        if (!this.model.validateRoleOrAffiliationChangeArgs(command, args)) {
-            return false;
-        }
-        const nick_or_jid = this.model.getNickOrJIDFromCommandArgs(args);
-        if (!nick_or_jid) {
-            return false;
-        }
-
-        let jid;
-        const reason = args.split(nick_or_jid, 2)[1].trim();
-        const occupant = this.model.getOccupant(nick_or_jid);
-        if (occupant) {
-            jid = occupant.get('jid');
-        } else {
-            if (u.isValidJID(nick_or_jid)) {
-                jid = nick_or_jid;
-            } else {
-                const message = __(
-                    "Couldn't find a participant with that nickname. " + 'They might have left the groupchat.'
-                );
-                this.model.createMessage({ message, 'type': 'error' });
-                return;
-            }
-        }
-        const attrs = { jid, reason };
-        if (occupant && api.settings.get('auto_register_muc_nickname')) {
-            attrs['nick'] = occupant.get('nick');
-        }
-        this.model
-            .setAffiliation(affiliation, [attrs])
-            .then(() => this.model.occupants.fetchMembers())
-            .catch(err => this.model.onCommandError(err));
-    }
-
-
     parseMessageForCommands (text) {
-        if (
-            api.settings.get('muc_disable_slash_commands') &&
-            !Array.isArray(api.settings.get('muc_disable_slash_commands'))
-        ) {
-            return super.parseMessageForCommands(text);
-        }
-        text = text.replace(/^\s*/, '');
-        const command = (text.match(/^\/([a-zA-Z]*) ?/) || ['']).pop().toLowerCase();
-        if (!command) {
-            return false;
-        }
-        const args = text.slice(('/' + command).length + 1).trim();
-        if (!this.model.getAllowedCommands().includes(command)) {
-            return false;
-        }
-
-        switch (command) {
-            case 'admin': {
-                this.setAffiliation(command, args, ['owner']);
-                break;
-            }
-            case 'ban': {
-                this.setAffiliation(command, args, ['admin', 'owner']);
-                break;
-            }
-            case 'modtools': {
-                const chatview = _converse.chatboxviews.get(this.getAttribute('jid'));
-                chatview.showModeratorToolsModal(args);
-                break;
-            }
-            case 'deop': {
-                // FIXME: /deop only applies to setting a moderators
-                // role to "participant" (which only admin/owner can
-                // do). Moderators can however set non-moderator's role
-                // to participant (e.g. visitor => participant).
-                // Currently we don't distinguish between these two
-                // cases.
-                this.setRole(command, args, ['admin', 'owner']);
-                break;
-            }
-            case 'destroy': {
-                if (!this.model.verifyAffiliations(['owner'])) {
-                    break;
-                }
-                const chatview = _converse.chatboxviews.get(this.getAttribute('jid'));
-                chatview.destroy().catch(e => this.model.onCommandError(e));
-                break;
-            }
-            case 'help': {
-                this.model.set({ 'show_help_messages': true });
-                break;
-            }
-            case 'kick': {
-                this.setRole(command, args, [], ['moderator']);
-                break;
-            }
-            case 'mute': {
-                this.setRole(command, args, [], ['moderator']);
-                break;
-            }
-            case 'member': {
-                this.setAffiliation(command, args, ['admin', 'owner']);
-                break;
-            }
-            case 'nick': {
-                if (!this.model.verifyRoles(['visitor', 'participant', 'moderator'])) {
-                    break;
-                } else if (args.length === 0) {
-                    // e.g. Your nickname is "coolguy69"
-                    const message = __('Your nickname is "%1$s"', this.model.get('nick'));
-                    this.model.createMessage({ message, 'type': 'error' });
-                } else {
-                    const jid = Strophe.getBareJidFromJid(this.model.get('jid'));
-                    api.send(
-                        $pres({
-                            from: _converse.connection.jid,
-                            to: `${jid}/${args}`,
-                            id: u.getUniqueId()
-                        }).tree()
-                    );
-                }
-                break;
-            }
-            case 'owner':
-                this.setAffiliation(command, args, ['owner']);
-                break;
-            case 'op': {
-                this.setRole(command, args, ['admin', 'owner']);
-                break;
-            }
-            case 'register': {
-                if (args.length > 1) {
-                    this.model.createMessage({
-                        'message': __('Error: invalid number of arguments'),
-                        'type': 'error'
-                    });
-                } else {
-                    this.model.registerNickname().then(err_msg => {
-                        err_msg && this.model.createMessage({ 'message': err_msg, 'type': 'error' });
-                    });
-                }
-                break;
-            }
-            case 'revoke': {
-                this.setAffiliation(command, args, ['admin', 'owner']);
-                break;
-            }
-            case 'topic':
-            case 'subject':
-                this.model.setSubject(args);
-                break;
-            case 'voice': {
-                this.setRole(command, args, [], ['moderator']);
-                break;
-            }
-            default:
-                return super.parseMessageForCommands(text);
-        }
-        return true;
+        return parseMessageForMUCCommands(this.model, text);
     }
 }
 

+ 208 - 2
src/plugins/muc-views/utils.js

@@ -1,7 +1,24 @@
-import { _converse, api, converse } from "@converse/headless/core";
 import log from "@converse/headless/log";
+import { __ } from 'i18n';
+import { _converse, api, converse } from "@converse/headless/core";
+import { parseMessageForCommands } from 'plugins/chatview/utils.js';
+
+const { Strophe, $pres, $iq, sizzle, u } = converse.env;
 
-const { Strophe, $iq, sizzle, u } = converse.env;
+const COMMAND_TO_AFFILIATION = {
+    'admin': 'admin',
+    'ban': 'outcast',
+    'member': 'member',
+    'owner': 'owner',
+    'revoke': 'none'
+};
+const COMMAND_TO_ROLE = {
+    'deop': 'participant',
+    'kick': 'none',
+    'mute': 'visitor',
+    'op': 'moderator',
+    'voice': 'participant'
+};
 
 
 export function getAutoCompleteListItem (text, input) {
@@ -75,3 +92,192 @@ export async function fetchCommandForm (command) {
         command.fields = [];
     }
 }
+
+
+function setRole (muc, command, args, required_affiliations = [], required_roles = []) {
+    const role = COMMAND_TO_ROLE[command];
+    if (!role) {
+        throw Error(`ChatRoomView#setRole called with invalid command: ${command}`);
+    }
+    if (!muc.verifyAffiliations(required_affiliations) || !muc.verifyRoles(required_roles)) {
+        return false;
+    }
+    if (!muc.validateRoleOrAffiliationChangeArgs(command, args)) {
+        return false;
+    }
+    const nick_or_jid = muc.getNickOrJIDFromCommandArgs(args);
+    if (!nick_or_jid) {
+        return false;
+    }
+    const reason = args.split(nick_or_jid, 2)[1].trim();
+    // We're guaranteed to have an occupant due to getNickOrJIDFromCommandArgs
+    const occupant = muc.getOccupant(nick_or_jid);
+    muc.setRole(occupant, role, reason, undefined, e => muc.onCommandError(e));
+    return true;
+}
+
+
+function setAffiliation (muc, command, args, required_affiliations) {
+    const affiliation = COMMAND_TO_AFFILIATION[command];
+    if (!affiliation) {
+        throw Error(`ChatRoomView#setAffiliation called with invalid command: ${command}`);
+    }
+    if (!muc.verifyAffiliations(required_affiliations)) {
+        return false;
+    }
+    if (!muc.validateRoleOrAffiliationChangeArgs(command, args)) {
+        return false;
+    }
+    const nick_or_jid = muc.getNickOrJIDFromCommandArgs(args);
+    if (!nick_or_jid) {
+        return false;
+    }
+
+    let jid;
+    const reason = args.split(nick_or_jid, 2)[1].trim();
+    const occupant = muc.getOccupant(nick_or_jid);
+    if (occupant) {
+        jid = occupant.get('jid');
+    } else {
+        if (u.isValidJID(nick_or_jid)) {
+            jid = nick_or_jid;
+        } else {
+            const message = __(
+                "Couldn't find a participant with that nickname. " + 'They might have left the groupchat.'
+            );
+            muc.createMessage({ message, 'type': 'error' });
+            return;
+        }
+    }
+    const attrs = { jid, reason };
+    if (occupant && api.settings.get('auto_register_muc_nickname')) {
+        attrs['nick'] = occupant.get('nick');
+    }
+    muc
+        .setAffiliation(affiliation, [attrs])
+        .then(() => muc.occupants.fetchMembers())
+        .catch(err => muc.onCommandError(err));
+}
+
+
+export function parseMessageForMUCCommands (muc, text) {
+    if (
+        api.settings.get('muc_disable_slash_commands') &&
+        !Array.isArray(api.settings.get('muc_disable_slash_commands'))
+    ) {
+        return parseMessageForCommands(muc, text);
+    }
+    text = text.replace(/^\s*/, '');
+    const command = (text.match(/^\/([a-zA-Z]*) ?/) || ['']).pop().toLowerCase();
+    if (!command) {
+        return false;
+    }
+    const args = text.slice(('/' + command).length + 1).trim();
+    if (!muc.getAllowedCommands().includes(command)) {
+        return false;
+    }
+
+    switch (command) {
+        case 'admin': {
+            setAffiliation(muc, command, args, ['owner']);
+            break;
+        }
+        case 'ban': {
+            setAffiliation(muc, command, args, ['admin', 'owner']);
+            break;
+        }
+        case 'modtools': {
+            const chatview = _converse.chatboxviews.get(muc.get('jid'));
+            chatview.showModeratorToolsModal(args);
+            break;
+        }
+        case 'deop': {
+            // FIXME: /deop only applies to setting a moderators
+            // role to "participant" (which only admin/owner can
+            // do). Moderators can however set non-moderator's role
+            // to participant (e.g. visitor => participant).
+            // Currently we don't distinguish between these two
+            // cases.
+            setRole(muc, command, args, ['admin', 'owner']);
+            break;
+        }
+        case 'destroy': {
+            if (!muc.verifyAffiliations(['owner'])) {
+                break;
+            }
+            const chatview = _converse.chatboxviews.get(muc.get('jid'));
+            chatview.destroy().catch(e => muc.onCommandError(e));
+            break;
+        }
+        case 'help': {
+            muc.set({ 'show_help_messages': true });
+            break;
+        }
+        case 'kick': {
+            setRole(muc, command, args, [], ['moderator']);
+            break;
+        }
+        case 'mute': {
+            setRole(muc, command, args, [], ['moderator']);
+            break;
+        }
+        case 'member': {
+            setAffiliation(muc, command, args, ['admin', 'owner']);
+            break;
+        }
+        case 'nick': {
+            if (!muc.verifyRoles(['visitor', 'participant', 'moderator'])) {
+                break;
+            } else if (args.length === 0) {
+                // e.g. Your nickname is "coolguy69"
+                const message = __('Your nickname is "%1$s"', muc.get('nick'));
+                muc.createMessage({ message, 'type': 'error' });
+            } else {
+                const jid = Strophe.getBareJidFromJid(muc.get('jid'));
+                api.send(
+                    $pres({
+                        from: _converse.connection.jid,
+                        to: `${jid}/${args}`,
+                        id: u.getUniqueId()
+                    }).tree()
+                );
+            }
+            break;
+        }
+        case 'owner':
+            setAffiliation(muc, command, args, ['owner']);
+            break;
+        case 'op': {
+            setRole(muc, command, args, ['admin', 'owner']);
+            break;
+        }
+        case 'register': {
+            if (args.length > 1) {
+                muc.createMessage({
+                    'message': __('Error: invalid number of arguments'),
+                    'type': 'error'
+                });
+            } else {
+                muc.registerNickname().then(err_msg => {
+                    err_msg && muc.createMessage({ 'message': err_msg, 'type': 'error' });
+                });
+            }
+            break;
+        }
+        case 'revoke': {
+            setAffiliation(muc, command, args, ['admin', 'owner']);
+            break;
+        }
+        case 'topic':
+        case 'subject':
+            muc.setSubject(args);
+            break;
+        case 'voice': {
+            setRole(muc, command, args, [], ['moderator']);
+            break;
+        }
+        default:
+            return parseMessageForCommands(muc, text);
+    }
+    return true;
+}