Move methods to utils
So that they can also be used independently of the components
This commit is contained in:
parent
d335a11119
commit
d8cab006c8
|
@ -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();
|
||||
|
|
|
@ -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,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 () {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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, $iq, sizzle, u } = converse.env;
|
||||
const { Strophe, $pres, $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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user