|
@@ -1,36 +1,22 @@
|
|
|
-/**
|
|
|
- * @module converse-muc-views
|
|
|
- * @copyright 2020, the Converse.js contributors
|
|
|
- * @description XEP-0045 Multi-User Chat Views
|
|
|
- * @license Mozilla Public License (MPLv2)
|
|
|
- */
|
|
|
-import "../components/muc-sidebar";
|
|
|
-import "./chatview/index.js";
|
|
|
-import "./modal.js";
|
|
|
-import "@converse/headless/utils/muc";
|
|
|
-import AddMUCModal from '../modals/add-muc.js';
|
|
|
-import MUCInviteModal from '../modals/muc-invite.js';
|
|
|
-import MUCListModal from '../modals/muc-list.js';
|
|
|
-import ModeratorToolsModal from "../modals/moderator-tools.js";
|
|
|
-import OccupantModal from '../modals/occupant.js';
|
|
|
-import RoomDetailsModal from '../modals/muc-details.js';
|
|
|
-import log from "@converse/headless/log";
|
|
|
-import tpl_chatroom from "../templates/chatroom.js";
|
|
|
-import tpl_chatroom_head from "../templates/chatroom_head.js";
|
|
|
-import tpl_muc_bottom_panel from "../templates/muc_bottom_panel.js";
|
|
|
-import tpl_muc_config_form from "../templates/muc_config_form.js";
|
|
|
-import tpl_muc_destroyed from "../templates/muc_destroyed.js";
|
|
|
-import tpl_muc_disconnect from "../templates/muc_disconnect.js";
|
|
|
-import tpl_muc_nickname_form from "../templates/muc_nickname_form.js";
|
|
|
-import tpl_muc_password_form from "../templates/muc_password_form.js";
|
|
|
-import tpl_room_panel from "../templates/room_panel.js";
|
|
|
-import tpl_spinner from "../templates/spinner.js";
|
|
|
+import './config-form.js';
|
|
|
+import './password-form.js';
|
|
|
+import MUCInviteModal from 'modals/muc-invite.js';
|
|
|
+import ModeratorToolsModal from 'modals/moderator-tools.js';
|
|
|
+import OccupantModal from 'modals/occupant.js';
|
|
|
+import RoomDetailsModal from 'modals/muc-details.js';
|
|
|
+import log from '@converse/headless/log';
|
|
|
+import tpl_chatroom from 'templates/chatroom.js';
|
|
|
+import tpl_chatroom_head from 'templates/chatroom_head.js';
|
|
|
+import tpl_muc_bottom_panel from 'templates/muc_bottom_panel.js';
|
|
|
+import tpl_muc_destroyed from 'templates/muc_destroyed.js';
|
|
|
+import tpl_muc_disconnect from 'templates/muc_disconnect.js';
|
|
|
+import tpl_muc_nickname_form from 'templates/muc_nickname_form.js';
|
|
|
+import tpl_spinner from 'templates/spinner.js';
|
|
|
import { Model } from '@converse/skeletor/src/model.js';
|
|
|
-import { View } from '@converse/skeletor/src/view.js';
|
|
|
-import { __ } from '../i18n';
|
|
|
-import { _converse, api, converse } from "@converse/headless/core";
|
|
|
-import { debounce } from "lodash-es";
|
|
|
-import { render } from "lit-html";
|
|
|
+import { __ } from 'i18n';
|
|
|
+import { _converse, api, converse } from '@converse/headless/core';
|
|
|
+import { debounce } from 'lodash-es';
|
|
|
+import { render } from 'lit-html';
|
|
|
|
|
|
const { Strophe, sizzle, $pres } = converse.env;
|
|
|
const u = converse.env.utils;
|
|
@@ -46,24 +32,22 @@ const COMMAND_TO_ROLE = {
|
|
|
'mute': 'visitor',
|
|
|
'op': 'moderator',
|
|
|
'voice': 'participant'
|
|
|
-}
|
|
|
+};
|
|
|
const COMMAND_TO_AFFILIATION = {
|
|
|
'admin': 'admin',
|
|
|
'ban': 'outcast',
|
|
|
'member': 'member',
|
|
|
'owner': 'owner',
|
|
|
'revoke': 'none'
|
|
|
-}
|
|
|
-
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
- * NativeView which renders a groupchat, based upon
|
|
|
- * { @link _converse.ChatBoxView } for normal one-on-one chat boxes.
|
|
|
- * @class
|
|
|
+ * Mixin which turns a ChatBoxView into a ChatRoomView
|
|
|
+ * @mixin
|
|
|
* @namespace _converse.ChatRoomView
|
|
|
* @memberOf _converse
|
|
|
*/
|
|
|
-export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
+const ChatRoomViewMixin = {
|
|
|
length: 300,
|
|
|
tagName: 'div',
|
|
|
className: 'chatbox chatroom hidden',
|
|
@@ -73,7 +57,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
'click .hide-occupants': 'hideOccupants',
|
|
|
'click .new-msgs-indicator': 'viewUnreadMessages',
|
|
|
// Arrow functions don't work here because you can't bind a different `this` param to them.
|
|
|
- 'click .occupant-nick': function (ev) {this.insertIntoTextArea(ev.target.textContent) },
|
|
|
+ 'click .occupant-nick': function (ev) {
|
|
|
+ this.insertIntoTextArea(ev.target.textContent);
|
|
|
+ },
|
|
|
'click .send-button': 'onFormSubmitted',
|
|
|
'dragover .chat-textarea': 'onDragOver',
|
|
|
'drop .chat-textarea': 'onDrop',
|
|
@@ -82,15 +68,19 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
'keyup .chat-textarea': 'onKeyUp',
|
|
|
'mousedown .dragresize-occupants-left': 'onStartResizeOccupants',
|
|
|
'paste .chat-textarea': 'onPaste',
|
|
|
- 'submit .muc-nickname-form': 'submitNickname',
|
|
|
+ 'submit .muc-nickname-form': 'submitNickname'
|
|
|
},
|
|
|
|
|
|
async initialize () {
|
|
|
this.initDebounced();
|
|
|
|
|
|
- this.listenTo(this.model, 'change', debounce(() => this.renderHeading(), 250));
|
|
|
+ this.listenTo(
|
|
|
+ this.model,
|
|
|
+ 'change',
|
|
|
+ debounce(() => this.renderHeading(), 250)
|
|
|
+ );
|
|
|
this.listenTo(this.model, 'change:composing_spoiler', this.renderMessageForm);
|
|
|
- this.listenTo(this.model, 'change:hidden', m => m.get('hidden') ? this.hide() : this.show());
|
|
|
+ this.listenTo(this.model, 'change:hidden', m => (m.get('hidden') ? this.hide() : this.show()));
|
|
|
this.listenTo(this.model, 'change:hidden_occupants', this.onSidebarToggle);
|
|
|
this.listenTo(this.model, 'configurationNeeded', this.getAndRenderConfigurationForm);
|
|
|
this.listenTo(this.model, 'destroy', this.hide);
|
|
@@ -101,8 +91,8 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
this.listenTo(this.model.session, 'change:connection_status', this.onConnectionStatusChanged);
|
|
|
|
|
|
// Bind so that we can pass it to addEventListener and removeEventListener
|
|
|
- this.onMouseMove = this.onMouseMove.bind(this);
|
|
|
- this.onMouseUp = this.onMouseUp.bind(this);
|
|
|
+ this.onMouseMove = this.onMouseMove.bind(this);
|
|
|
+ this.onMouseUp = this.onMouseUp.bind(this);
|
|
|
|
|
|
await this.render();
|
|
|
|
|
@@ -142,16 +132,20 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
async render () {
|
|
|
const sidebar_hidden = !this.shouldShowSidebar();
|
|
|
this.el.setAttribute('id', this.model.get('box_id'));
|
|
|
- render(tpl_chatroom({
|
|
|
- sidebar_hidden,
|
|
|
- 'model': this.model,
|
|
|
- 'occupants': this.model.occupants,
|
|
|
- 'show_sidebar': !this.model.get('hidden_occupants') &&
|
|
|
- this.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED,
|
|
|
- 'markScrolled': ev => this.markScrolled(ev),
|
|
|
- 'muc_show_logs_before_join': api.settings.get('muc_show_logs_before_join'),
|
|
|
- 'show_send_button': _converse.show_send_button,
|
|
|
- }), this.el);
|
|
|
+ render(
|
|
|
+ tpl_chatroom({
|
|
|
+ sidebar_hidden,
|
|
|
+ 'model': this.model,
|
|
|
+ 'occupants': this.model.occupants,
|
|
|
+ 'show_sidebar':
|
|
|
+ !this.model.get('hidden_occupants') &&
|
|
|
+ this.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED,
|
|
|
+ 'markScrolled': ev => this.markScrolled(ev),
|
|
|
+ 'muc_show_logs_before_join': api.settings.get('muc_show_logs_before_join'),
|
|
|
+ 'show_send_button': _converse.show_send_button
|
|
|
+ }),
|
|
|
+ this.el
|
|
|
+ );
|
|
|
|
|
|
this.notifications = this.el.querySelector('.chat-content__notifications');
|
|
|
this.content = this.el.querySelector('.chat-content');
|
|
@@ -159,8 +153,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
this.help_container = this.el.querySelector('.chat-content__help');
|
|
|
|
|
|
this.renderBottomPanel();
|
|
|
- if (!api.settings.get('muc_show_logs_before_join') &&
|
|
|
- this.model.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED) {
|
|
|
+ if (
|
|
|
+ !api.settings.get('muc_show_logs_before_join') &&
|
|
|
+ this.model.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED
|
|
|
+ ) {
|
|
|
this.showSpinner();
|
|
|
}
|
|
|
// Render header as late as possible since it's async and we
|
|
@@ -173,17 +169,19 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
getNotifications () {
|
|
|
const actors_per_state = this.model.notifications.toJSON();
|
|
|
|
|
|
- const role_changes = api.settings.get('muc_show_info_messages')
|
|
|
+ const role_changes = api.settings
|
|
|
+ .get('muc_show_info_messages')
|
|
|
.filter(role_change => converse.MUC_ROLE_CHANGES_LIST.includes(role_change));
|
|
|
|
|
|
- const join_leave_events = api.settings.get('muc_show_info_messages')
|
|
|
+ const join_leave_events = api.settings
|
|
|
+ .get('muc_show_info_messages')
|
|
|
.filter(join_leave_event => converse.MUC_TRAFFIC_STATES_LIST.includes(join_leave_event));
|
|
|
|
|
|
const states = [...converse.CHAT_STATES, ...join_leave_events, ...role_changes];
|
|
|
|
|
|
return states.reduce((result, state) => {
|
|
|
const existing_actors = actors_per_state[state];
|
|
|
- if (!(existing_actors?.length)) {
|
|
|
+ if (!existing_actors?.length) {
|
|
|
return result;
|
|
|
}
|
|
|
const actors = existing_actors.map(a => this.model.getOccupant(a)?.getDisplayName() || a);
|
|
@@ -199,18 +197,20 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
} else if (state === 'exited') {
|
|
|
return `${result}${__('%1$s has left the groupchat', actors[0])}\n`;
|
|
|
} else if (state === 'op') {
|
|
|
- return `${result}${__("%1$s is now a moderator", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s is now a moderator', actors[0])}\n`;
|
|
|
} else if (state === 'deop') {
|
|
|
- return `${result}${__("%1$s is no longer a moderator", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s is no longer a moderator', actors[0])}\n`;
|
|
|
} else if (state === 'voice') {
|
|
|
- return `${result}${__("%1$s has been given a voice", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s has been given a voice', actors[0])}\n`;
|
|
|
} else if (state === 'mute') {
|
|
|
- return `${result}${__("%1$s has been muted", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s has been muted', actors[0])}\n`;
|
|
|
}
|
|
|
} else if (actors.length > 1) {
|
|
|
let actors_str;
|
|
|
if (actors.length > 3) {
|
|
|
- actors_str = `${Array.from(actors).slice(0, 2).join(', ')} and others`;
|
|
|
+ actors_str = `${Array.from(actors)
|
|
|
+ .slice(0, 2)
|
|
|
+ .join(', ')} and others`;
|
|
|
} else {
|
|
|
const last_actor = actors.pop();
|
|
|
actors_str = __('%1$s and %2$s', actors.join(', '), last_actor);
|
|
@@ -227,13 +227,13 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
} else if (state === 'exited') {
|
|
|
return `${result}${__('%1$s have left the groupchat', actors_str)}\n`;
|
|
|
} else if (state === 'op') {
|
|
|
- return `${result}${__("%1$s are now moderators", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s are now moderators', actors[0])}\n`;
|
|
|
} else if (state === 'deop') {
|
|
|
- return `${result}${__("%1$s are no longer moderators", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s are no longer moderators', actors[0])}\n`;
|
|
|
} else if (state === 'voice') {
|
|
|
- return `${result}${__("%1$s have been given voices", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s have been given voices', actors[0])}\n`;
|
|
|
} else if (state === 'mute') {
|
|
|
- return `${result}${__("%1$s have been muted", actors[0])}\n`;
|
|
|
+ return `${result}${__('%1$s have been muted', actors[0])}\n`;
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
@@ -241,7 +241,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
},
|
|
|
|
|
|
getHelpMessages () {
|
|
|
- const setting = api.settings.get("muc_disable_slash_commands");
|
|
|
+ const setting = api.settings.get('muc_disable_slash_commands');
|
|
|
const disabled_commands = Array.isArray(setting) ? setting : [];
|
|
|
return [
|
|
|
`<strong>/admin</strong>: ${__("Change user's affiliation to admin")}`,
|
|
@@ -259,13 +259,14 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
`<strong>/nick</strong>: ${__('Change your nickname')}`,
|
|
|
`<strong>/op</strong>: ${__('Grant moderator role to user')}`,
|
|
|
`<strong>/owner</strong>: ${__('Grant ownership of this groupchat')}`,
|
|
|
- `<strong>/register</strong>: ${__("Register your nickname")}`,
|
|
|
+ `<strong>/register</strong>: ${__('Register your nickname')}`,
|
|
|
`<strong>/revoke</strong>: ${__("Revoke the user's current affiliation")}`,
|
|
|
`<strong>/subject</strong>: ${__('Set groupchat subject')}`,
|
|
|
`<strong>/topic</strong>: ${__('Set groupchat subject (alias for /subject)')}`,
|
|
|
`<strong>/voice</strong>: ${__('Allow muted user to post messages')}`
|
|
|
- ].filter(line => disabled_commands.every(c => (!line.startsWith(c+'<', 9))))
|
|
|
- .filter(line => this.getAllowedCommands().some(c => line.startsWith(c+'<', 9)));
|
|
|
+ ]
|
|
|
+ .filter(line => disabled_commands.every(c => !line.startsWith(c + '<', 9)))
|
|
|
+ .filter(line => this.getAllowedCommands().some(c => line.startsWith(c + '<', 9)));
|
|
|
},
|
|
|
|
|
|
/**
|
|
@@ -319,7 +320,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
const sidebar_el = this.el.querySelector('converse-muc-sidebar');
|
|
|
const element_position = sidebar_el.getBoundingClientRect();
|
|
|
const occupants_width = this.calculateSidebarWidth(element_position, 0);
|
|
|
- const attrs = {occupants_width};
|
|
|
+ const attrs = { occupants_width };
|
|
|
_converse.connection.connected ? this.model.save(attrs) : this.model.set(attrs);
|
|
|
}
|
|
|
},
|
|
@@ -333,23 +334,23 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
this.is_maximum = element_position.left > current_mouse_position;
|
|
|
} else {
|
|
|
const occupants_width = this.calculateSidebarWidth(element_position, delta);
|
|
|
- sidebar_el.style.flex = "0 0 " + occupants_width + "px";
|
|
|
+ sidebar_el.style.flex = '0 0 ' + occupants_width + 'px';
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- calculateSidebarWidth(element_position, delta) {
|
|
|
+ calculateSidebarWidth (element_position, delta) {
|
|
|
let occupants_width = element_position.width + delta;
|
|
|
const room_width = this.el.clientWidth;
|
|
|
// keeping display in boundaries
|
|
|
- if (occupants_width < (room_width * 0.20)) {
|
|
|
+ if (occupants_width < room_width * 0.2) {
|
|
|
// set pixel to 20% width
|
|
|
- occupants_width = (room_width * 0.20);
|
|
|
+ occupants_width = room_width * 0.2;
|
|
|
this.is_minimum = true;
|
|
|
- } else if (occupants_width > (room_width * 0.75)) {
|
|
|
+ } else if (occupants_width > room_width * 0.75) {
|
|
|
// set pixel to 75% width
|
|
|
- occupants_width = (room_width * 0.75);
|
|
|
+ occupants_width = room_width * 0.75;
|
|
|
this.is_maximum = true;
|
|
|
- } else if ((room_width - occupants_width) < 250) {
|
|
|
+ } else if (room_width - occupants_width < 250) {
|
|
|
// resize occupants if chat-area becomes smaller than 250px (min-width property set in css)
|
|
|
occupants_width = room_width - 250;
|
|
|
this.is_maximum = true;
|
|
@@ -361,39 +362,39 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
},
|
|
|
|
|
|
getAutoCompleteList () {
|
|
|
- return this.model.getAllKnownNicknames().map(nick => ({'label': nick, 'value': `@${nick}`}));
|
|
|
+ return this.model.getAllKnownNicknames().map(nick => ({ 'label': nick, 'value': `@${nick}` }));
|
|
|
},
|
|
|
|
|
|
- getAutoCompleteListItem(text, input) {
|
|
|
+ getAutoCompleteListItem (text, input) {
|
|
|
input = input.trim();
|
|
|
- const element = document.createElement("li");
|
|
|
- element.setAttribute("aria-selected", "false");
|
|
|
+ const element = document.createElement('li');
|
|
|
+ element.setAttribute('aria-selected', 'false');
|
|
|
|
|
|
if (api.settings.get('muc_mention_autocomplete_show_avatar')) {
|
|
|
- const img = document.createElement("img");
|
|
|
- let dataUri = "data:" + _converse.DEFAULT_IMAGE_TYPE + ";base64," + _converse.DEFAULT_IMAGE;
|
|
|
+ const img = document.createElement('img');
|
|
|
+ let dataUri = 'data:' + _converse.DEFAULT_IMAGE_TYPE + ';base64,' + _converse.DEFAULT_IMAGE;
|
|
|
|
|
|
if (_converse.vcards) {
|
|
|
- const vcard = _converse.vcards.findWhere({'nickname': text});
|
|
|
- if (vcard) dataUri = "data:" + vcard.get('image_type') + ";base64," + vcard.get('image');
|
|
|
+ const vcard = _converse.vcards.findWhere({ 'nickname': text });
|
|
|
+ if (vcard) dataUri = 'data:' + vcard.get('image_type') + ';base64,' + vcard.get('image');
|
|
|
}
|
|
|
|
|
|
- img.setAttribute("src", dataUri);
|
|
|
- img.setAttribute("width", "22");
|
|
|
- img.setAttribute("class", "avatar avatar-autocomplete");
|
|
|
+ img.setAttribute('src', dataUri);
|
|
|
+ img.setAttribute('width', '22');
|
|
|
+ img.setAttribute('class', 'avatar avatar-autocomplete');
|
|
|
element.appendChild(img);
|
|
|
}
|
|
|
|
|
|
- const regex = new RegExp("(" + input + ")", "ig");
|
|
|
+ const regex = new RegExp('(' + input + ')', 'ig');
|
|
|
const parts = input ? text.split(regex) : [text];
|
|
|
|
|
|
parts.forEach(txt => {
|
|
|
if (input && txt.match(regex)) {
|
|
|
- const match = document.createElement("mark");
|
|
|
- match.textContent = txt;
|
|
|
- element.appendChild(match);
|
|
|
+ const match = document.createElement('mark');
|
|
|
+ match.textContent = txt;
|
|
|
+ element.appendChild(match);
|
|
|
} else {
|
|
|
- element.appendChild(document.createTextNode(txt));
|
|
|
+ element.appendChild(document.createTextNode(txt));
|
|
|
}
|
|
|
});
|
|
|
|
|
@@ -407,10 +408,11 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
'min_chars': api.settings.get('muc_mention_autocomplete_min_chars'),
|
|
|
'match_current_word': true,
|
|
|
'list': () => this.getAutoCompleteList(),
|
|
|
- 'filter': api.settings.get('muc_mention_autocomplete_filter') == 'contains' ?
|
|
|
- _converse.FILTER_CONTAINS :
|
|
|
- _converse.FILTER_STARTSWITH,
|
|
|
- 'ac_triggers': ["Tab", "@"],
|
|
|
+ 'filter':
|
|
|
+ api.settings.get('muc_mention_autocomplete_filter') == 'contains'
|
|
|
+ ? _converse.FILTER_CONTAINS
|
|
|
+ : _converse.FILTER_STARTSWITH,
|
|
|
+ 'ac_triggers': ['Tab', '@'],
|
|
|
'include_triggers': [],
|
|
|
'item': this.getAutoCompleteListItem
|
|
|
});
|
|
@@ -442,10 +444,11 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
},
|
|
|
|
|
|
async onMessageRetractButtonClicked (message) {
|
|
|
- const retraction_warning =
|
|
|
- __("Be aware that other XMPP/Jabber clients (and servers) may "+
|
|
|
- "not yet support retractions and that this message may not "+
|
|
|
- "be removed everywhere.");
|
|
|
+ const retraction_warning = __(
|
|
|
+ 'Be aware that other XMPP/Jabber clients (and servers) may ' +
|
|
|
+ 'not yet support retractions and that this message may not ' +
|
|
|
+ 'be removed everywhere.'
|
|
|
+ );
|
|
|
|
|
|
if (message.mayBeRetracted()) {
|
|
|
const messages = [__('Are you sure you want to retract this message?')];
|
|
@@ -457,7 +460,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
if (message.get('sender') === 'me') {
|
|
|
let messages = [__('Are you sure you want to retract this message?')];
|
|
|
if (api.settings.get('show_retraction_warning')) {
|
|
|
- messages = [messages[0], retraction_warning, messages[1]]
|
|
|
+ messages = [messages[0], retraction_warning, messages[1]];
|
|
|
}
|
|
|
!!(await api.confirm(__('Confirm'), messages)) && this.retractOtherMessage(message);
|
|
|
} else {
|
|
@@ -466,14 +469,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
__('You may optionally include a message, explaining the reason for the retraction.')
|
|
|
];
|
|
|
if (api.settings.get('show_retraction_warning')) {
|
|
|
- messages = [messages[0], retraction_warning, messages[1]]
|
|
|
+ messages = [messages[0], retraction_warning, messages[1]];
|
|
|
}
|
|
|
- const reason = await api.prompt(
|
|
|
- __('Message Retraction'),
|
|
|
- messages,
|
|
|
- __('Optional reason')
|
|
|
- );
|
|
|
- (reason !== false) && this.retractOtherMessage(message, reason);
|
|
|
+ const reason = await api.prompt(__('Message Retraction'), messages, __('Optional reason'));
|
|
|
+ reason !== false && this.retractOtherMessage(message, reason);
|
|
|
}
|
|
|
} else {
|
|
|
const err_msg = __(`Sorry, you're not allowed to retract this message`);
|
|
@@ -510,20 +509,20 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
if (modal) {
|
|
|
modal.model.set('affiliation', affiliation);
|
|
|
} else {
|
|
|
- const model = new Model({'affiliation': affiliation});
|
|
|
- modal = api.modal.create(ModeratorToolsModal, {model, _converse, 'chatroomview': this});
|
|
|
+ const model = new Model({ 'affiliation': affiliation });
|
|
|
+ modal = api.modal.create(ModeratorToolsModal, { model, _converse, 'chatroomview': this });
|
|
|
}
|
|
|
modal.show();
|
|
|
},
|
|
|
|
|
|
showRoomDetailsModal (ev) {
|
|
|
ev.preventDefault();
|
|
|
- api.modal.show(RoomDetailsModal, {'model': this.model}, ev);
|
|
|
+ api.modal.show(RoomDetailsModal, { 'model': this.model }, ev);
|
|
|
},
|
|
|
|
|
|
showOccupantDetailsModal (ev, message) {
|
|
|
ev.preventDefault();
|
|
|
- api.modal.show(OccupantModal, {'model': message.occupant}, ev);
|
|
|
+ api.modal.show(OccupantModal, { 'model': message.occupant }, ev);
|
|
|
},
|
|
|
|
|
|
showChatStateNotification (message) {
|
|
@@ -534,8 +533,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
},
|
|
|
|
|
|
shouldShowSidebar () {
|
|
|
- return !this.model.get('hidden_occupants') &&
|
|
|
- this.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED;
|
|
|
+ return (
|
|
|
+ !this.model.get('hidden_occupants') &&
|
|
|
+ this.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED
|
|
|
+ );
|
|
|
},
|
|
|
|
|
|
onSidebarToggle () {
|
|
@@ -598,9 +599,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
if (subject && subject.text) {
|
|
|
buttons.push({
|
|
|
'i18n_text': subject_hidden ? __('Show topic') : __('Hide topic'),
|
|
|
- 'i18n_title': subject_hidden ?
|
|
|
- __('Show the topic message in the heading') :
|
|
|
- __('Hide the topic in the heading'),
|
|
|
+ 'i18n_title': subject_hidden
|
|
|
+ ? __('Show the topic message in the heading')
|
|
|
+ : __('Hide the topic in the heading'),
|
|
|
'handler': ev => this.toggleTopic(ev),
|
|
|
'a_class': 'hide-topic',
|
|
|
'icon_class': 'fa-minus-square',
|
|
@@ -608,7 +609,6 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-
|
|
|
const conn_status = this.model.session.get('connection_status');
|
|
|
if (conn_status === converse.ROOMSTATUS.ENTERED) {
|
|
|
const allowed_commands = this.getAllowedCommands();
|
|
@@ -634,7 +634,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (!api.settings.get("singleton")) {
|
|
|
+ if (!api.settings.get('singleton')) {
|
|
|
buttons.push({
|
|
|
'i18n_text': __('Leave'),
|
|
|
'i18n_title': __('Leave and close this groupchat'),
|
|
@@ -645,7 +645,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
result && this.close(ev);
|
|
|
},
|
|
|
'a_class': 'close-chatbox-button',
|
|
|
- 'standalone': api.settings.get("view_mode") === 'overlayed',
|
|
|
+ 'standalone': api.settings.get('view_mode') === 'overlayed',
|
|
|
'icon_class': 'fa-sign-out-alt',
|
|
|
'name': 'signout'
|
|
|
});
|
|
@@ -669,8 +669,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
subject_hidden,
|
|
|
'dropdown_btns': dropdown_btns.map(b => this.getHeadingDropdownItem(b)),
|
|
|
'standalone_btns': standalone_btns.map(b => this.getHeadingStandaloneButton(b)),
|
|
|
- 'title': this.model.getDisplayName(),
|
|
|
- }));
|
|
|
+ 'title': this.model.getDisplayName()
|
|
|
+ })
|
|
|
+ );
|
|
|
},
|
|
|
|
|
|
toggleTopic () {
|
|
@@ -679,10 +680,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
|
|
|
showInviteModal (ev) {
|
|
|
ev.preventDefault();
|
|
|
- api.modal.show(MUCInviteModal, {'model': new Model(), 'chatroomview': this}, ev);
|
|
|
+ api.modal.show(MUCInviteModal, { 'model': new Model(), 'chatroomview': this }, ev);
|
|
|
},
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* Callback method that gets called after the chat has become visible.
|
|
|
* @private
|
|
@@ -718,13 +718,11 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
},
|
|
|
|
|
|
getToolbarOptions () {
|
|
|
- return Object.assign(
|
|
|
- _converse.ChatBoxView.prototype.getToolbarOptions.apply(this, arguments), {
|
|
|
- 'is_groupchat': true,
|
|
|
- 'label_hide_occupants': __('Hide the list of participants'),
|
|
|
- 'show_occupants_toggle': _converse.visible_toolbar_buttons.toggle_occupants
|
|
|
- }
|
|
|
- );
|
|
|
+ return Object.assign(_converse.ChatBoxView.prototype.getToolbarOptions.apply(this, arguments), {
|
|
|
+ 'is_groupchat': true,
|
|
|
+ 'label_hide_occupants': __('Hide the list of participants'),
|
|
|
+ 'show_occupants_toggle': _converse.visible_toolbar_buttons.toggle_occupants
|
|
|
+ });
|
|
|
},
|
|
|
|
|
|
/**
|
|
@@ -734,7 +732,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
*/
|
|
|
async close () {
|
|
|
this.hide();
|
|
|
- if (_converse.router.history.getFragment() === "converse/room?jid="+this.model.get('jid')) {
|
|
|
+ if (_converse.router.history.getFragment() === 'converse/room?jid=' + this.model.get('jid')) {
|
|
|
_converse.router.navigate('');
|
|
|
}
|
|
|
await this.model.leave();
|
|
@@ -751,18 +749,18 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
ev.preventDefault();
|
|
|
ev.stopPropagation();
|
|
|
}
|
|
|
- this.model.save({'hidden_occupants': true});
|
|
|
+ this.model.save({ 'hidden_occupants': true });
|
|
|
this.scrollDown();
|
|
|
},
|
|
|
|
|
|
- verifyRoles (roles, occupant, show_error=true) {
|
|
|
+ verifyRoles (roles, occupant, show_error = true) {
|
|
|
if (!Array.isArray(roles)) {
|
|
|
throw new TypeError('roles must be an Array');
|
|
|
}
|
|
|
if (!roles.length) {
|
|
|
return true;
|
|
|
}
|
|
|
- occupant = occupant || this.model.occupants.findWhere({'jid': _converse.bare_jid});
|
|
|
+ occupant = occupant || this.model.occupants.findWhere({ 'jid': _converse.bare_jid });
|
|
|
if (occupant) {
|
|
|
const role = occupant.get('role');
|
|
|
if (roles.includes(role)) {
|
|
@@ -771,19 +769,19 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
}
|
|
|
if (show_error) {
|
|
|
const message = __('Forbidden: you do not have the necessary role in order to do that.');
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
}
|
|
|
return false;
|
|
|
},
|
|
|
|
|
|
- verifyAffiliations (affiliations, occupant, show_error=true) {
|
|
|
+ verifyAffiliations (affiliations, occupant, show_error = true) {
|
|
|
if (!Array.isArray(affiliations)) {
|
|
|
throw new TypeError('affiliations must be an Array');
|
|
|
}
|
|
|
if (!affiliations.length) {
|
|
|
return true;
|
|
|
}
|
|
|
- occupant = occupant || this.model.occupants.findWhere({'jid': _converse.bare_jid});
|
|
|
+ occupant = occupant || this.model.occupants.findWhere({ 'jid': _converse.bare_jid });
|
|
|
if (occupant) {
|
|
|
const a = occupant.get('affiliation');
|
|
|
if (affiliations.includes(a)) {
|
|
@@ -792,7 +790,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
}
|
|
|
if (show_error) {
|
|
|
const message = __('Forbidden: you do not have the necessary affiliation in order to do that.');
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
}
|
|
|
return false;
|
|
|
},
|
|
@@ -803,7 +801,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
'Error: the "%1$s" command takes two arguments, the user\'s nickname and optionally a reason.',
|
|
|
command
|
|
|
);
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
@@ -814,24 +812,24 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
return args.trim();
|
|
|
}
|
|
|
if (!args.startsWith('@')) {
|
|
|
- args = '@'+ args;
|
|
|
+ args = '@' + args;
|
|
|
}
|
|
|
const [text, references] = this.model.parseTextForReferences(args); // eslint-disable-line no-unused-vars
|
|
|
if (!references.length) {
|
|
|
const message = __("Error: couldn't find a groupchat participant based on your arguments");
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
return;
|
|
|
}
|
|
|
if (references.length > 1) {
|
|
|
- const message = __("Error: found multiple groupchat participant based on your arguments");
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ const message = __('Error: found multiple groupchat participant based on your arguments');
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
return;
|
|
|
}
|
|
|
const nick_or_jid = references.pop().value;
|
|
|
const reason = args.split(nick_or_jid, 2)[1];
|
|
|
if (reason && !reason.startsWith(' ')) {
|
|
|
const message = __("Error: couldn't find a groupchat participant based on your arguments");
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
return;
|
|
|
}
|
|
|
return nick_or_jid;
|
|
@@ -863,10 +861,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
jid = nick_or_jid;
|
|
|
} else {
|
|
|
const message = __(
|
|
|
- "Couldn't find a participant with that nickname. "+
|
|
|
- "They might have left the groupchat."
|
|
|
+ "Couldn't find a participant with that nickname. " + 'They might have left the groupchat.'
|
|
|
);
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
@@ -874,16 +871,17 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
if (occupant && api.settings.get('auto_register_muc_nickname')) {
|
|
|
attrs['nick'] = occupant.get('nick');
|
|
|
}
|
|
|
- this.model.setAffiliation(affiliation, [attrs])
|
|
|
+ this.model
|
|
|
+ .setAffiliation(affiliation, [attrs])
|
|
|
.then(() => this.model.occupants.fetchMembers())
|
|
|
.catch(err => this.onCommandError(err));
|
|
|
},
|
|
|
|
|
|
getReason (args) {
|
|
|
- return args.includes(',') ? args.slice(args.indexOf(',')+1).trim() : null;
|
|
|
+ return args.includes(',') ? args.slice(args.indexOf(',') + 1).trim() : null;
|
|
|
},
|
|
|
|
|
|
- setRole (command, args, required_affiliations=[], required_roles=[]) {
|
|
|
+ setRole (command, args, required_affiliations = [], required_roles = []) {
|
|
|
/* Check that a command to change a groupchat user's role or
|
|
|
* affiliation has anough arguments.
|
|
|
*/
|
|
@@ -911,9 +909,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
onCommandError (err) {
|
|
|
log.fatal(err);
|
|
|
const message =
|
|
|
- __("Sorry, an error happened while running the command.") + " " +
|
|
|
+ __('Sorry, an error happened while running the command.') +
|
|
|
+ ' ' +
|
|
|
__("Check your browser's developer console for details.");
|
|
|
- this.model.createMessage({message, 'type': 'error'});
|
|
|
+ this.model.createMessage({ message, 'type': 'error' });
|
|
|
},
|
|
|
|
|
|
getAllowedCommands () {
|
|
@@ -921,7 +920,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
if (this.model.config.get('changesubject') || ['owner', 'admin'].includes(this.model.getOwnAffiliation())) {
|
|
|
allowed_commands = [...allowed_commands, ...['subject', 'topic']];
|
|
|
}
|
|
|
- const occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
|
|
|
+ const occupant = this.model.occupants.findWhere({ 'jid': _converse.bare_jid });
|
|
|
if (this.verifyAffiliations(['owner'], occupant, false)) {
|
|
|
allowed_commands = allowed_commands.concat(OWNER_COMMANDS).concat(ADMIN_COMMANDS);
|
|
|
} else if (this.verifyAffiliations(['admin'], occupant, false)) {
|
|
@@ -943,42 +942,48 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
|
|
|
async destroy () {
|
|
|
const messages = [__('Are you sure you want to destroy this groupchat?')];
|
|
|
- let fields = [{
|
|
|
- 'name': 'challenge',
|
|
|
- 'label': __('Please enter the XMPP address of this groupchat to confirm'),
|
|
|
- 'challenge': this.model.get('jid'),
|
|
|
- 'placeholder': __('name@example.org'),
|
|
|
- 'required': true
|
|
|
- }, {
|
|
|
- 'name': 'reason',
|
|
|
- 'label': __('Optional reason for destroying this groupchat'),
|
|
|
- 'placeholder': __('Reason')
|
|
|
- }, {
|
|
|
- 'name': 'newjid',
|
|
|
- 'label': __('Optional XMPP address for a new groupchat that replaces this one'),
|
|
|
- 'placeholder': __('replacement@example.org')
|
|
|
- }];
|
|
|
+ let fields = [
|
|
|
+ {
|
|
|
+ 'name': 'challenge',
|
|
|
+ 'label': __('Please enter the XMPP address of this groupchat to confirm'),
|
|
|
+ 'challenge': this.model.get('jid'),
|
|
|
+ 'placeholder': __('name@example.org'),
|
|
|
+ 'required': true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 'name': 'reason',
|
|
|
+ 'label': __('Optional reason for destroying this groupchat'),
|
|
|
+ 'placeholder': __('Reason')
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 'name': 'newjid',
|
|
|
+ 'label': __('Optional XMPP address for a new groupchat that replaces this one'),
|
|
|
+ 'placeholder': __('replacement@example.org')
|
|
|
+ }
|
|
|
+ ];
|
|
|
try {
|
|
|
fields = await api.confirm(__('Confirm'), messages, fields);
|
|
|
const reason = fields.filter(f => f.name === 'reason').pop()?.value;
|
|
|
const newjid = fields.filter(f => f.name === 'newjid').pop()?.value;
|
|
|
- return this.model.sendDestroyIQ(reason, newjid).then(() => this.close())
|
|
|
+ return this.model.sendDestroyIQ(reason, newjid).then(() => this.close());
|
|
|
} catch (e) {
|
|
|
log.error(e);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
parseMessageForCommands (text) {
|
|
|
- if (api.settings.get('muc_disable_slash_commands') &&
|
|
|
- !Array.isArray(api.settings.get('muc_disable_slash_commands'))) {
|
|
|
+ if (
|
|
|
+ api.settings.get('muc_disable_slash_commands') &&
|
|
|
+ !Array.isArray(api.settings.get('muc_disable_slash_commands'))
|
|
|
+ ) {
|
|
|
return _converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments);
|
|
|
}
|
|
|
- text = text.replace(/^\s*/, "");
|
|
|
+ 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();
|
|
|
+ const args = text.slice(('/' + command).length + 1).trim();
|
|
|
if (!this.getAllowedCommands().includes(command)) {
|
|
|
return false;
|
|
|
}
|
|
@@ -1014,9 +1019,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
break;
|
|
|
}
|
|
|
case 'help': {
|
|
|
- this.model.set({'show_help_messages': true});
|
|
|
+ this.model.set({ 'show_help_messages': true });
|
|
|
break;
|
|
|
- } case 'kick': {
|
|
|
+ }
|
|
|
+ case 'kick': {
|
|
|
this.setRole(command, args, [], ['moderator']);
|
|
|
break;
|
|
|
}
|
|
@@ -1034,15 +1040,16 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
} 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'});
|
|
|
-
|
|
|
+ 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());
|
|
|
+ api.send(
|
|
|
+ $pres({
|
|
|
+ from: _converse.connection.jid,
|
|
|
+ to: `${jid}/${args}`,
|
|
|
+ id: u.getUniqueId()
|
|
|
+ }).tree()
|
|
|
+ );
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
@@ -1061,7 +1068,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
});
|
|
|
} else {
|
|
|
this.model.registerNickname().then(err_msg => {
|
|
|
- err_msg && this.model.createMessage({'message': err_msg, 'type': 'error'});
|
|
|
+ err_msg && this.model.createMessage({ 'message': err_msg, 'type': 'error' });
|
|
|
});
|
|
|
}
|
|
|
break;
|
|
@@ -1130,7 +1137,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
container.insertAdjacentElement('beforeend', form_el);
|
|
|
}
|
|
|
}
|
|
|
- u.safeSave(this.model.session, {'connection_status': converse.ROOMSTATUS.NICKNAME_REQUIRED});
|
|
|
+ u.safeSave(this.model.session, { 'connection_status': converse.ROOMSTATUS.NICKNAME_REQUIRED });
|
|
|
},
|
|
|
|
|
|
/**
|
|
@@ -1160,7 +1167,8 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
getAndRenderConfigurationForm () {
|
|
|
if (!this.config_form || !u.isVisible(this.config_form.el)) {
|
|
|
this.showSpinner();
|
|
|
- this.model.fetchRoomConfiguration()
|
|
|
+ this.model
|
|
|
+ .fetchRoomConfiguration()
|
|
|
.then(iq => this.renderConfigurationForm(iq))
|
|
|
.catch(e => log.error(e));
|
|
|
} else {
|
|
@@ -1185,7 +1193,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
'model': new Model({
|
|
|
'validation_message': message
|
|
|
}),
|
|
|
- 'chatroomview': this,
|
|
|
+ 'chatroomview': this
|
|
|
});
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
|
|
container_el.insertAdjacentElement('beforeend', this.password_form.el);
|
|
@@ -1265,7 +1273,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
* @param {string} nick
|
|
|
*/
|
|
|
getPreviousJoinOrLeaveNotification (el, nick) {
|
|
|
- const today = (new Date()).toISOString().split('T')[0];
|
|
|
+ const today = new Date().toISOString().split('T')[0];
|
|
|
while (el !== null) {
|
|
|
if (!el.classList.contains('chat-info')) {
|
|
|
return;
|
|
@@ -1277,10 +1285,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
return;
|
|
|
}
|
|
|
const data = el?.dataset || {};
|
|
|
- if (data.join === nick ||
|
|
|
- data.leave === nick ||
|
|
|
- data.leavejoin === nick ||
|
|
|
- data.joinleave === nick) {
|
|
|
+ if (data.join === nick || data.leave === nick || data.leavejoin === nick || data.joinleave === nick) {
|
|
|
return el;
|
|
|
}
|
|
|
el = el.previousElementSibling;
|
|
@@ -1295,7 +1300,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
* @method _converse.ChatRoomView#renderAfterTransition
|
|
|
*/
|
|
|
renderAfterTransition () {
|
|
|
- const conn_status = this.model.session.get('connection_status')
|
|
|
+ const conn_status = this.model.session.get('connection_status');
|
|
|
if (conn_status == converse.ROOMSTATUS.NICKNAME_REQUIRED) {
|
|
|
this.renderNicknameForm();
|
|
|
} else if (conn_status == converse.ROOMSTATUS.PASSWORD_REQUIRED) {
|
|
@@ -1312,10 +1317,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
sizzle('.spinner', this.el).forEach(u.removeElement);
|
|
|
this.hideChatRoomContents();
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
|
|
- container_el.insertAdjacentElement(
|
|
|
- 'afterbegin',
|
|
|
- u.getElementFromTemplateResult(tpl_spinner())
|
|
|
- );
|
|
|
+ container_el.insertAdjacentElement('afterbegin', u.getElementFromTemplateResult(tpl_spinner()));
|
|
|
},
|
|
|
|
|
|
/**
|
|
@@ -1333,371 +1335,6 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
}
|
|
|
return this;
|
|
|
}
|
|
|
-});
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * View which renders MUC section of the control box.
|
|
|
- * @class
|
|
|
- * @namespace _converse.RoomsPanel
|
|
|
- * @memberOf _converse
|
|
|
- */
|
|
|
-export const RoomsPanel = View.extend({
|
|
|
- tagName: 'div',
|
|
|
- className: 'controlbox-section',
|
|
|
- id: 'chatrooms',
|
|
|
- events: {
|
|
|
- 'click a.controlbox-heading__btn.show-add-muc-modal': 'showAddRoomModal',
|
|
|
- 'click a.controlbox-heading__btn.show-list-muc-modal': 'showMUCListModal'
|
|
|
- },
|
|
|
-
|
|
|
- toHTML () {
|
|
|
- return tpl_room_panel({
|
|
|
- 'heading_chatrooms': __('Groupchats'),
|
|
|
- 'title_new_room': __('Add a new groupchat'),
|
|
|
- 'title_list_rooms': __('Query for groupchats')
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- showAddRoomModal (ev) {
|
|
|
- api.modal.show(AddMUCModal, {'model': this.model}, ev);
|
|
|
- },
|
|
|
-
|
|
|
- showMUCListModal(ev) {
|
|
|
- api.modal.show(MUCListModal, {'model': this.model}, ev);
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
-
|
|
|
-converse.plugins.add('converse-muc-views', {
|
|
|
- /* Dependencies are other plugins which might be
|
|
|
- * overridden or relied upon, and therefore need to be loaded before
|
|
|
- * this plugin. They are "optional" because they might not be
|
|
|
- * available, in which case any overrides applicable to them will be
|
|
|
- * ignored.
|
|
|
- *
|
|
|
- * NB: These plugins need to have already been loaded via require.js.
|
|
|
- *
|
|
|
- * It's possible to make these dependencies "non-optional".
|
|
|
- * If the setting "strict_plugin_dependencies" is set to true,
|
|
|
- * an error will be raised if the plugin is not found.
|
|
|
- */
|
|
|
- dependencies: ["converse-autocomplete", "converse-modal", "converse-controlbox", "converse-chatview"],
|
|
|
-
|
|
|
- overrides: {
|
|
|
- ControlBoxView: {
|
|
|
- renderControlBoxPane () {
|
|
|
- this.__super__.renderControlBoxPane.apply(this, arguments);
|
|
|
- if (api.settings.get('allow_muc')) {
|
|
|
- this.renderRoomsPanel();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- initialize () {
|
|
|
- const { _converse } = this;
|
|
|
-
|
|
|
- api.promises.add(['roomsPanelRendered']);
|
|
|
-
|
|
|
- // Configuration values for this plugin
|
|
|
- // ====================================
|
|
|
- // Refer to docs/source/configuration.rst for explanations of these
|
|
|
- // configuration settings.
|
|
|
- api.settings.extend({
|
|
|
- 'auto_list_rooms': false,
|
|
|
- 'cache_muc_messages': true,
|
|
|
- 'locked_muc_nickname': false,
|
|
|
- 'modtools_disable_query': [],
|
|
|
- 'modtools_disable_assign': false,
|
|
|
- 'muc_disable_slash_commands': false,
|
|
|
- 'muc_mention_autocomplete_filter': 'contains',
|
|
|
- 'muc_mention_autocomplete_min_chars': 0,
|
|
|
- 'muc_mention_autocomplete_show_avatar': true,
|
|
|
- 'muc_roomid_policy': null,
|
|
|
- 'muc_roomid_policy_hint': null,
|
|
|
- 'roomconfig_whitelist': [],
|
|
|
- 'show_retraction_warning': true,
|
|
|
- 'visible_toolbar_buttons': {
|
|
|
- 'toggle_occupants': true
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
-
|
|
|
- _converse.ChatRoomView = ChatRoomView;
|
|
|
- _converse.RoomsPanel = RoomsPanel;
|
|
|
-
|
|
|
-
|
|
|
- const viewWithRoomsPanel = {
|
|
|
- renderRoomsPanel () {
|
|
|
- if (this.roomspanel && u.isInDOM(this.roomspanel.el)) {
|
|
|
- return this.roomspanel;
|
|
|
- }
|
|
|
- const id = `converse.roomspanel${_converse.bare_jid}`;
|
|
|
-
|
|
|
- this.roomspanel = new _converse.RoomsPanel({
|
|
|
- 'model': new (_converse.RoomsPanelModel.extend({
|
|
|
- id,
|
|
|
- 'browserStorage': _converse.createStore(id)
|
|
|
- }))()
|
|
|
- });
|
|
|
- this.roomspanel.model.fetch();
|
|
|
- this.el.querySelector('.controlbox-pane').insertAdjacentElement(
|
|
|
- 'beforeEnd', this.roomspanel.render().el);
|
|
|
-
|
|
|
- /**
|
|
|
- * Triggered once the section of the { @link _converse.ControlBoxView }
|
|
|
- * which shows gropuchats has been rendered.
|
|
|
- * @event _converse#roomsPanelRendered
|
|
|
- * @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
|
|
|
- */
|
|
|
- api.trigger('roomsPanelRendered');
|
|
|
- return this.roomspanel;
|
|
|
- },
|
|
|
-
|
|
|
- getRoomsPanel () {
|
|
|
- if (this.roomspanel && u.isInDOM(this.roomspanel.el)) {
|
|
|
- return this.roomspanel;
|
|
|
- } else {
|
|
|
- return this.renderRoomsPanel();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- if (_converse.ControlBoxView) {
|
|
|
- Object.assign(_converse.ControlBoxView.prototype, viewWithRoomsPanel);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- _converse.MUCConfigForm = View.extend({
|
|
|
- className: 'chatroom-form-container muc-config-form',
|
|
|
-
|
|
|
- initialize (attrs) {
|
|
|
- this.chatroomview = attrs.chatroomview;
|
|
|
- this.listenTo(this.chatroomview.model.features, 'change:passwordprotected', this.render);
|
|
|
- this.listenTo(this.chatroomview.model.features, 'change:config_stanza', this.render);
|
|
|
- this.render();
|
|
|
- },
|
|
|
-
|
|
|
- toHTML () {
|
|
|
- const stanza = u.toStanza(this.model.get('config_stanza'));
|
|
|
- const whitelist = api.settings.get('roomconfig_whitelist');
|
|
|
- let fields = sizzle('field', stanza);
|
|
|
- if (whitelist.length) {
|
|
|
- fields = fields.filter(f => whitelist.includes(f.getAttribute('var')));
|
|
|
- }
|
|
|
- const password_protected = this.model.features.get('passwordprotected');
|
|
|
- const options = {
|
|
|
- 'new_password': !password_protected,
|
|
|
- 'fixed_username': this.model.get('jid')
|
|
|
- };
|
|
|
- return tpl_muc_config_form({
|
|
|
- 'closeConfigForm': ev => this.closeConfigForm(ev),
|
|
|
- 'fields': fields.map(f => u.xForm2webForm(f, stanza, options)),
|
|
|
- 'instructions': stanza.querySelector('instructions')?.textContent,
|
|
|
- 'submitConfigForm': ev => this.submitConfigForm(ev),
|
|
|
- 'title': stanza.querySelector('title')?.textContent
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- async submitConfigForm (ev) {
|
|
|
- ev.preventDefault();
|
|
|
- const inputs = sizzle(':input:not([type=button]):not([type=submit])', ev.target);
|
|
|
- const config_array = inputs.map(u.webForm2xForm).filter(f => f);
|
|
|
- try {
|
|
|
- await this.model.sendConfiguration(config_array);
|
|
|
- } catch (e) {
|
|
|
- log.error(e);
|
|
|
- const message =
|
|
|
- __("Sorry, an error occurred while trying to submit the config form.") + " " +
|
|
|
- __("Check your browser's developer console for details.");
|
|
|
- api.alert('error', __('Error'), message);
|
|
|
- }
|
|
|
- await this.model.refreshDiscoInfo();
|
|
|
- this.chatroomview.closeForm();
|
|
|
- },
|
|
|
-
|
|
|
- closeConfigForm (ev) {
|
|
|
- ev.preventDefault();
|
|
|
- this.chatroomview.closeForm();
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
-
|
|
|
- _converse.MUCPasswordForm = View.extend({
|
|
|
- className: 'chatroom-form-container muc-password-form',
|
|
|
-
|
|
|
- initialize (attrs) {
|
|
|
- this.chatroomview = attrs.chatroomview;
|
|
|
- this.listenTo(this.model, 'change:validation_message', this.render);
|
|
|
- this.render();
|
|
|
- },
|
|
|
-
|
|
|
- toHTML () {
|
|
|
- return tpl_muc_password_form({
|
|
|
- 'jid': this.model.get('jid'),
|
|
|
- 'submitPassword': ev => this.submitPassword(ev),
|
|
|
- 'validation_message': this.model.get('validation_message')
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- submitPassword (ev) {
|
|
|
- ev.preventDefault();
|
|
|
- const password = this.el.querySelector('input[type=password]').value;
|
|
|
- this.chatroomview.model.join(this.chatroomview.model.get('nick'), password);
|
|
|
- this.model.set('validation_message', null);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
-
|
|
|
- function setMUCDomain (domain, controlboxview) {
|
|
|
- controlboxview.getRoomsPanel().model.save('muc_domain', Strophe.getDomainFromJid(domain));
|
|
|
- }
|
|
|
-
|
|
|
- function setMUCDomainFromDisco (controlboxview) {
|
|
|
- /* Check whether service discovery for the user's domain
|
|
|
- * returned MUC information and use that to automatically
|
|
|
- * set the MUC domain in the "Add groupchat" modal.
|
|
|
- */
|
|
|
- function featureAdded (feature) {
|
|
|
- if (!feature) { return; }
|
|
|
- if (feature.get('var') === Strophe.NS.MUC) {
|
|
|
- feature.entity.getIdentity('conference', 'text').then(identity => {
|
|
|
- if (identity) {
|
|
|
- setMUCDomain(feature.get('from'), controlboxview);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- api.waitUntil('discoInitialized').then(() => {
|
|
|
- api.listen.on('serviceDiscovered', featureAdded);
|
|
|
- // Features could have been added before the controlbox was
|
|
|
- // initialized. We're only interested in MUC
|
|
|
- _converse.disco_entities.each(entity => featureAdded(entity.features.findWhere({'var': Strophe.NS.MUC })));
|
|
|
- }).catch(e => log.error(e));
|
|
|
- }
|
|
|
-
|
|
|
- function fetchAndSetMUCDomain (controlboxview) {
|
|
|
- if (controlboxview.model.get('connected')) {
|
|
|
- if (!controlboxview.getRoomsPanel().model.get('muc_domain')) {
|
|
|
- if (api.settings.get('muc_domain') === undefined) {
|
|
|
- setMUCDomainFromDisco(controlboxview);
|
|
|
- } else {
|
|
|
- setMUCDomain(api.settings.get('muc_domain'), controlboxview);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /************************ BEGIN Event Handlers ************************/
|
|
|
- api.listen.on('chatBoxViewsInitialized', () => {
|
|
|
-
|
|
|
- function openChatRoomFromURIClicked (ev) {
|
|
|
- ev.preventDefault();
|
|
|
- api.rooms.open(ev.target.href);
|
|
|
- }
|
|
|
- _converse.chatboxviews.delegate('click', 'a.open-chatroom', openChatRoomFromURIClicked);
|
|
|
-
|
|
|
- async function addView (model) {
|
|
|
- const views = _converse.chatboxviews;
|
|
|
- if (!views.get(model.get('id')) &&
|
|
|
- model.get('type') === _converse.CHATROOMS_TYPE &&
|
|
|
- model.isValid()
|
|
|
- ) {
|
|
|
- await model.initialized;
|
|
|
- return views.add(model.get('id'), new _converse.ChatRoomView({model}));
|
|
|
- }
|
|
|
- }
|
|
|
- _converse.chatboxes.on('add', addView);
|
|
|
- });
|
|
|
-
|
|
|
- api.listen.on('clearSession', () => {
|
|
|
- const view = _converse.chatboxviews.get('controlbox');
|
|
|
- if (view && view.roomspanel) {
|
|
|
- view.roomspanel.model.destroy();
|
|
|
- view.roomspanel.remove();
|
|
|
- delete view.roomspanel;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- api.listen.on('controlBoxInitialized', (view) => {
|
|
|
- if (!api.settings.get('allow_muc')) {
|
|
|
- return;
|
|
|
- }
|
|
|
- fetchAndSetMUCDomain(view);
|
|
|
- view.model.on('change:connected', () => fetchAndSetMUCDomain(view));
|
|
|
- });
|
|
|
- /************************ END Event Handlers ************************/
|
|
|
-
|
|
|
-
|
|
|
- /************************ BEGIN API ************************/
|
|
|
- Object.assign(_converse.api, {
|
|
|
- /**
|
|
|
- * The "roomviews" namespace groups methods relevant to chatroom
|
|
|
- * (aka groupchats) views.
|
|
|
- *
|
|
|
- * @namespace _converse.api.roomviews
|
|
|
- * @memberOf _converse.api
|
|
|
- */
|
|
|
- roomviews: {
|
|
|
- /**
|
|
|
- * Retrieves a groupchat (aka chatroom) view. The chat should already be open.
|
|
|
- *
|
|
|
- * @method _converse.api.roomviews.get
|
|
|
- * @param {String|string[]} name - e.g. 'coven@conference.shakespeare.lit' or
|
|
|
- * ['coven@conference.shakespeare.lit', 'cave@conference.shakespeare.lit']
|
|
|
- * @returns {View} View representing the groupchat
|
|
|
- *
|
|
|
- * @example
|
|
|
- * // To return a single view, provide the JID of the groupchat
|
|
|
- * const view = _converse.api.roomviews.get('coven@conference.shakespeare.lit');
|
|
|
- *
|
|
|
- * @example
|
|
|
- * // To return an array of views, provide an array of JIDs:
|
|
|
- * const views = _converse.api.roomviews.get(['coven@conference.shakespeare.lit', 'cave@conference.shakespeare.lit']);
|
|
|
- *
|
|
|
- * @example
|
|
|
- * // To return views of all open groupchats, call the method without any parameters::
|
|
|
- * const views = _converse.api.roomviews.get();
|
|
|
- *
|
|
|
- */
|
|
|
- get (jids) {
|
|
|
- if (Array.isArray(jids)) {
|
|
|
- const views = api.chatviews.get(jids);
|
|
|
- return views.filter(v => v.model.get('type') === _converse.CHATROOMS_TYPE)
|
|
|
- } else {
|
|
|
- const view = api.chatviews.get(jids);
|
|
|
- if (view.model.get('type') === _converse.CHATROOMS_TYPE) {
|
|
|
- return view;
|
|
|
- } else {
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- /**
|
|
|
- * Lets you close open chatrooms.
|
|
|
- *
|
|
|
- * You can call this method without any arguments to close
|
|
|
- * all open chatrooms, or you can specify a single JID or
|
|
|
- * an array of JIDs.
|
|
|
- *
|
|
|
- * @method _converse.api.roomviews.close
|
|
|
- * @param {(String[]|String)} jids The JID or array of JIDs of the chatroom(s)
|
|
|
- * @returns { Promise } - Promise which resolves once the views have been closed.
|
|
|
- */
|
|
|
- close (jids) {
|
|
|
- let views;
|
|
|
- if (jids === undefined) {
|
|
|
- views = _converse.chatboxviews;
|
|
|
- } else if (typeof jids === 'string') {
|
|
|
- views = [_converse.chatboxviews.get(jids)].filter(v => v);
|
|
|
- } else if (Array.isArray(jids)) {
|
|
|
- views = jids.map(jid => _converse.chatboxviews.get(jid));
|
|
|
- }
|
|
|
- return Promise.all(views.map(v => (v.is_chatroom && v.model && v.close())))
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-});
|
|
|
+export default ChatRoomViewMixin;
|