2019-07-11 10:48:52 +02:00
|
|
|
/**
|
|
|
|
* @module converse-muc-views
|
2020-01-26 16:21:20 +01:00
|
|
|
* @copyright 2020, the Converse.js contributors
|
2019-09-11 15:23:58 +02:00
|
|
|
* @description XEP-0045 Multi-User Chat Views
|
|
|
|
* @license Mozilla Public License (MPLv2)
|
2019-07-11 10:48:52 +02:00
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
import "converse-modal";
|
2019-10-09 16:01:38 +02:00
|
|
|
import "@converse/headless/utils/muc";
|
2019-09-19 16:54:55 +02:00
|
|
|
import { Model } from 'skeletor.js/src/model.js';
|
2020-02-07 14:58:26 +01:00
|
|
|
import { View } from 'skeletor.js/src/view.js';
|
|
|
|
import { get, head, isString, isUndefined } from "lodash";
|
2020-02-10 11:23:55 +01:00
|
|
|
import { render } from "lit-html";
|
2020-01-26 17:55:24 +01:00
|
|
|
import { __ } from '@converse/headless/i18n';
|
2018-10-23 03:41:38 +02:00
|
|
|
import converse from "@converse/headless/converse-core";
|
2019-11-06 11:01:34 +01:00
|
|
|
import log from "@converse/headless/log";
|
2020-01-23 10:18:41 +01:00
|
|
|
import tpl_add_chatroom_modal from "templates/add_chatroom_modal.js";
|
2018-10-23 03:41:38 +02:00
|
|
|
import tpl_chatarea from "templates/chatarea.html";
|
|
|
|
import tpl_chatroom from "templates/chatroom.html";
|
2019-04-10 23:25:14 +02:00
|
|
|
import tpl_chatroom_bottom_panel from "templates/chatroom_bottom_panel.html";
|
2018-10-23 03:41:38 +02:00
|
|
|
import tpl_chatroom_destroyed from "templates/chatroom_destroyed.html";
|
2020-01-23 10:18:41 +01:00
|
|
|
import tpl_chatroom_details_modal from "templates/chatroom_details_modal.js";
|
2018-10-23 03:41:38 +02:00
|
|
|
import tpl_chatroom_disconnect from "templates/chatroom_disconnect.html";
|
2020-01-26 18:36:25 +01:00
|
|
|
import tpl_muc_config_form from "templates/muc_config_form.js";
|
2020-02-07 14:58:26 +01:00
|
|
|
import tpl_chatroom_head from "templates/chatroom_head.js";
|
2020-01-27 13:20:23 +01:00
|
|
|
import tpl_muc_invite_modal from "templates/muc_invite_modal.js";
|
2019-11-28 17:01:08 +01:00
|
|
|
import tpl_chatroom_nickname_form from "templates/chatroom_nickname_form.html";
|
2020-01-26 18:36:25 +01:00
|
|
|
import tpl_muc_password_form from "templates/muc_password_form.js";
|
|
|
|
import tpl_muc_sidebar from "templates/muc_sidebar.js";
|
2018-10-23 03:41:38 +02:00
|
|
|
import tpl_info from "templates/info.html";
|
2020-01-23 10:18:41 +01:00
|
|
|
import tpl_list_chatrooms_modal from "templates/list_chatrooms_modal.js";
|
|
|
|
import tpl_moderator_tools_modal from "templates/moderator_tools_modal.js";
|
2018-10-23 03:41:38 +02:00
|
|
|
import tpl_room_description from "templates/room_description.html";
|
|
|
|
import tpl_room_item from "templates/room_item.html";
|
|
|
|
import tpl_room_panel from "templates/room_panel.html";
|
|
|
|
import tpl_rooms_results from "templates/rooms_results.html";
|
|
|
|
import tpl_spinner from "templates/spinner.html";
|
2019-05-13 18:50:25 +02:00
|
|
|
import xss from "xss/dist/xss";
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2020-01-26 18:36:25 +01:00
|
|
|
const { Strophe, sizzle, $iq, $pres } = converse.env;
|
2018-10-23 03:41:38 +02:00
|
|
|
const u = converse.env.utils;
|
2019-07-04 14:12:12 +02:00
|
|
|
|
|
|
|
const ROLES = ['moderator', 'participant', 'visitor'];
|
|
|
|
const AFFILIATIONS = ['admin', 'member', 'outcast', 'owner'];
|
2019-04-25 10:34:17 +02:00
|
|
|
const OWNER_COMMANDS = ['owner'];
|
2019-09-07 23:41:48 +02:00
|
|
|
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
|
|
|
|
const MODERATOR_COMMANDS = ['kick', 'mute', 'voice', 'modtools'];
|
2019-04-25 10:34:17 +02:00
|
|
|
const VISITOR_COMMANDS = ['nick'];
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2019-05-26 17:53:32 +02:00
|
|
|
const COMMAND_TO_ROLE = {
|
|
|
|
'deop': 'participant',
|
|
|
|
'kick': 'none',
|
|
|
|
'mute': 'visitor',
|
|
|
|
'op': 'moderator',
|
|
|
|
'voice': 'participant'
|
|
|
|
}
|
|
|
|
const COMMAND_TO_AFFILIATION = {
|
|
|
|
'admin': 'admin',
|
|
|
|
'ban': 'outcast',
|
|
|
|
'member': 'member',
|
|
|
|
'owner': 'owner',
|
|
|
|
'revoke': 'none'
|
|
|
|
}
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
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 () {
|
|
|
|
const { _converse } = this.__super__;
|
|
|
|
this.__super__.renderControlBoxPane.apply(this, arguments);
|
|
|
|
if (_converse.allow_muc) {
|
|
|
|
this.renderRoomsPanel();
|
|
|
|
}
|
2019-05-24 13:52:15 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
initialize () {
|
2020-01-26 17:55:24 +01:00
|
|
|
const { _converse } = this;
|
2018-10-23 03:41:38 +02:00
|
|
|
|
|
|
|
_converse.api.promises.add(['roomsPanelRendered']);
|
|
|
|
|
|
|
|
// Configuration values for this plugin
|
|
|
|
// ====================================
|
|
|
|
// Refer to docs/source/configuration.rst for explanations of these
|
|
|
|
// configuration settings.
|
|
|
|
_converse.api.settings.update({
|
|
|
|
'auto_list_rooms': false,
|
2019-04-17 11:52:41 +02:00
|
|
|
'cache_muc_messages': true,
|
|
|
|
'locked_muc_nickname': false,
|
2019-09-24 15:33:41 +02:00
|
|
|
'show_retraction_warning': true,
|
2019-04-25 10:59:16 +02:00
|
|
|
'muc_disable_slash_commands': false,
|
2019-01-29 05:50:43 +01:00
|
|
|
'muc_show_join_leave': true,
|
2019-05-18 06:33:42 +02:00
|
|
|
'muc_show_join_leave_status': true,
|
2019-06-19 14:51:34 +02:00
|
|
|
'muc_mention_autocomplete_min_chars': 0,
|
2019-11-11 15:27:34 +01:00
|
|
|
'muc_mention_autocomplete_filter': 'contains',
|
|
|
|
'muc_mention_autocomplete_show_avatar': true,
|
2019-01-12 17:04:17 +01:00
|
|
|
'roomconfig_whitelist': [],
|
2019-12-30 13:23:52 +01:00
|
|
|
'muc_roomid_policy': null,
|
|
|
|
'muc_roomid_policy_hint': null,
|
2018-10-23 03:41:38 +02:00
|
|
|
'visible_toolbar_buttons': {
|
|
|
|
'toggle_occupants': true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-05-24 13:52:15 +02:00
|
|
|
|
2019-09-11 11:28:28 +02:00
|
|
|
const viewWithRoomsPanel = {
|
|
|
|
renderRoomsPanel () {
|
|
|
|
if (this.roomspanel && u.isInDOM(this.roomspanel.el)) {
|
|
|
|
return this.roomspanel;
|
|
|
|
}
|
2019-10-11 20:29:12 +02:00
|
|
|
const id = `converse.roomspanel${_converse.bare_jid}`;
|
|
|
|
|
2019-09-11 11:28:28 +02:00
|
|
|
this.roomspanel = new _converse.RoomsPanel({
|
|
|
|
'model': new (_converse.RoomsPanelModel.extend({
|
2019-10-11 20:29:12 +02:00
|
|
|
id,
|
2019-12-17 13:39:32 +01:00
|
|
|
'browserStorage': _converse.createStore(id)
|
2019-09-11 11:28:28 +02:00
|
|
|
}))()
|
|
|
|
});
|
|
|
|
this.roomspanel.model.fetch();
|
|
|
|
this.el.querySelector('.controlbox-pane').insertAdjacentElement(
|
|
|
|
'beforeEnd', this.roomspanel.render().el);
|
2019-05-24 20:50:30 +02:00
|
|
|
|
2019-09-11 11:28:28 +02:00
|
|
|
/**
|
2020-01-30 19:37:00 +01:00
|
|
|
* Triggered once the section of the { @link _converse.ControlBoxView }
|
2019-09-11 11:28:28 +02:00
|
|
|
* which shows gropuchats has been rendered.
|
|
|
|
* @event _converse#roomsPanelRendered
|
|
|
|
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
|
|
|
|
*/
|
|
|
|
_converse.api.trigger('roomsPanelRendered');
|
|
|
|
return this.roomspanel;
|
|
|
|
},
|
|
|
|
|
|
|
|
getRoomsPanel () {
|
|
|
|
if (this.roomspanel && u.isInDOM(this.roomspanel.el)) {
|
|
|
|
return this.roomspanel;
|
|
|
|
} else {
|
|
|
|
return this.renderRoomsPanel();
|
|
|
|
}
|
|
|
|
}
|
2019-05-24 20:50:30 +02:00
|
|
|
}
|
2019-09-11 11:28:28 +02:00
|
|
|
|
2019-05-24 20:50:30 +02:00
|
|
|
if (_converse.ControlBoxView) {
|
2019-09-11 11:28:28 +02:00
|
|
|
Object.assign(_converse.ControlBoxView.prototype, viewWithRoomsPanel);
|
2019-05-24 20:50:30 +02:00
|
|
|
}
|
2019-05-24 13:52:15 +02:00
|
|
|
|
2019-03-29 23:47:56 +01:00
|
|
|
/* Insert groupchat info (based on returned #disco IQ stanza)
|
|
|
|
* @function insertRoomInfo
|
|
|
|
* @param { HTMLElement } el - The HTML DOM element that contains the info.
|
|
|
|
* @param { XMLElement } stanza - The IQ stanza containing the groupchat info.
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
function insertRoomInfo (el, stanza) {
|
2019-03-04 17:49:44 +01:00
|
|
|
// All MUC features found here: https://xmpp.org/registrar/disco-features.html
|
2018-10-23 03:41:38 +02:00
|
|
|
el.querySelector('span.spinner').remove();
|
|
|
|
el.querySelector('a.room-info').classList.add('selected');
|
|
|
|
el.insertAdjacentHTML(
|
|
|
|
'beforeEnd',
|
|
|
|
tpl_room_description({
|
|
|
|
'jid': stanza.getAttribute('from'),
|
2020-01-21 11:08:59 +01:00
|
|
|
'desc': get(head(sizzle('field[var="muc#roominfo_description"] value', stanza)), 'textContent'),
|
|
|
|
'occ': get(head(sizzle('field[var="muc#roominfo_occupants"] value', stanza)), 'textContent'),
|
2018-10-23 03:41:38 +02:00
|
|
|
'hidden': sizzle('feature[var="muc_hidden"]', stanza).length,
|
|
|
|
'membersonly': sizzle('feature[var="muc_membersonly"]', stanza).length,
|
|
|
|
'moderated': sizzle('feature[var="muc_moderated"]', stanza).length,
|
|
|
|
'nonanonymous': sizzle('feature[var="muc_nonanonymous"]', stanza).length,
|
|
|
|
'open': sizzle('feature[var="muc_open"]', stanza).length,
|
|
|
|
'passwordprotected': sizzle('feature[var="muc_passwordprotected"]', stanza).length,
|
|
|
|
'persistent': sizzle('feature[var="muc_persistent"]', stanza).length,
|
|
|
|
'publicroom': sizzle('feature[var="muc_publicroom"]', stanza).length,
|
|
|
|
'semianonymous': sizzle('feature[var="muc_semianonymous"]', stanza).length,
|
|
|
|
'temporary': sizzle('feature[var="muc_temporary"]', stanza).length,
|
|
|
|
'unmoderated': sizzle('feature[var="muc_unmoderated"]', stanza).length,
|
|
|
|
'label_desc': __('Description:'),
|
|
|
|
'label_jid': __('Groupchat Address (JID):'),
|
|
|
|
'label_occ': __('Participants:'),
|
|
|
|
'label_features': __('Features:'),
|
|
|
|
'label_requires_auth': __('Requires authentication'),
|
|
|
|
'label_hidden': __('Hidden'),
|
|
|
|
'label_requires_invite': __('Requires an invitation'),
|
|
|
|
'label_moderated': __('Moderated'),
|
|
|
|
'label_non_anon': __('Non-anonymous'),
|
|
|
|
'label_open_room': __('Open'),
|
|
|
|
'label_permanent_room': __('Permanent'),
|
|
|
|
'label_public': __('Public'),
|
|
|
|
'label_semi_anon': __('Semi-anonymous'),
|
|
|
|
'label_temp_room': __('Temporary'),
|
|
|
|
'label_unmoderated': __('Unmoderated')
|
|
|
|
}));
|
|
|
|
}
|
2018-02-22 17:39:44 +01:00
|
|
|
|
2019-09-07 22:00:28 +02:00
|
|
|
/**
|
|
|
|
* Show/hide extra information about a groupchat in a listing.
|
|
|
|
* @function toggleRoomInfo
|
|
|
|
* @param { Event }
|
|
|
|
*/
|
2018-10-25 22:42:38 +02:00
|
|
|
function toggleRoomInfo (ev) {
|
2019-09-07 22:00:28 +02:00
|
|
|
const parent_el = u.ancestor(ev.target, '.room-item');
|
|
|
|
const div_el = parent_el.querySelector('div.room-info');
|
2018-10-23 03:41:38 +02:00
|
|
|
if (div_el) {
|
2018-10-25 22:42:38 +02:00
|
|
|
u.slideIn(div_el).then(u.removeElement)
|
2018-10-23 03:41:38 +02:00
|
|
|
parent_el.querySelector('a.room-info').classList.remove('selected');
|
|
|
|
} else {
|
|
|
|
parent_el.insertAdjacentHTML('beforeend', tpl_spinner());
|
2018-10-25 22:42:38 +02:00
|
|
|
_converse.api.disco.info(ev.target.getAttribute('data-room-jid'), null)
|
|
|
|
.then(stanza => insertRoomInfo(parent_el, stanza))
|
2019-11-06 11:01:34 +01:00
|
|
|
.catch(e => log.error(e));
|
2018-02-22 17:39:44 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2018-02-22 17:39:44 +01:00
|
|
|
|
2018-09-12 15:06:08 +02:00
|
|
|
|
2019-07-04 14:12:12 +02:00
|
|
|
_converse.ModeratorToolsModal = _converse.BootstrapModal.extend({
|
2020-01-23 10:18:41 +01:00
|
|
|
id: "converse-modtools-modal",
|
2019-07-04 14:12:12 +02:00
|
|
|
events: {
|
|
|
|
'submit .affiliation-form': 'assignAffiliation',
|
|
|
|
'submit .role-form': 'assignRole',
|
|
|
|
'submit .query-affiliation': 'queryAffiliation',
|
|
|
|
'submit .query-role': 'queryRole',
|
|
|
|
'click .nav-item .nav-link': 'switchTab',
|
|
|
|
'click .toggle-form': 'toggleForm',
|
|
|
|
},
|
|
|
|
|
|
|
|
initialize (attrs) {
|
|
|
|
this.chatroomview = attrs.chatroomview;
|
|
|
|
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
|
|
|
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change:role', () => {
|
2019-07-04 14:12:12 +02:00
|
|
|
this.users_with_role = this.getUsersWithRole();
|
|
|
|
this.render();
|
|
|
|
});
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change:affiliation', async () => {
|
2019-07-04 14:12:12 +02:00
|
|
|
this.loading_users_with_affiliation = true;
|
|
|
|
this.users_with_affiliation = null;
|
|
|
|
this.render();
|
|
|
|
const affiliation = this.model.get('affiliation');
|
|
|
|
if (!_converse.muc_fetch_members || affiliation === 'outcast') {
|
|
|
|
this.users_with_affiliation = await this.chatroomview.model.getAffiliationList(affiliation);
|
|
|
|
} else {
|
|
|
|
this.users_with_affiliation = this.getUsersWithAffiliation();
|
|
|
|
}
|
|
|
|
this.loading_users_with_affiliation = false;
|
|
|
|
this.render();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
toHTML () {
|
|
|
|
const allowed_commands = this.chatroomview.getAllowedCommands();
|
|
|
|
const allowed_affiliations = allowed_commands.map(c => COMMAND_TO_AFFILIATION[c]).filter(c => c);
|
2020-01-21 11:08:59 +01:00
|
|
|
const allowed_roles = allowed_commands
|
|
|
|
.filter((value, i, list) => list.indexOf(value) == i)
|
2019-07-04 14:12:12 +02:00
|
|
|
.map(c => COMMAND_TO_ROLE[c])
|
2020-01-21 11:08:59 +01:00
|
|
|
.filter(c => c);
|
2019-07-04 14:12:12 +02:00
|
|
|
|
|
|
|
allowed_affiliations.sort();
|
|
|
|
allowed_roles.sort();
|
|
|
|
|
|
|
|
return tpl_moderator_tools_modal(Object.assign(this.model.toJSON(), {
|
2020-01-21 11:08:59 +01:00
|
|
|
allowed_affiliations,
|
|
|
|
allowed_roles,
|
2019-09-09 11:25:46 +02:00
|
|
|
'affiliations': [...AFFILIATIONS, 'none'],
|
2019-07-04 14:12:12 +02:00
|
|
|
'loading_users_with_affiliation': this.loading_users_with_affiliation,
|
|
|
|
'roles': ROLES,
|
|
|
|
'users_with_affiliation': this.users_with_affiliation,
|
|
|
|
'users_with_role': this.users_with_role
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleForm (ev) {
|
|
|
|
ev.stopPropagation();
|
|
|
|
ev.preventDefault();
|
|
|
|
const form_class = ev.target.getAttribute('data-form');
|
|
|
|
const form = u.ancestor(ev.target, '.list-group-item').querySelector(`.${form_class}`);
|
|
|
|
if (u.hasClass('hidden', form)) {
|
|
|
|
u.removeClass('hidden', form);
|
|
|
|
} else {
|
|
|
|
u.addClass('hidden', form);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getUsersWithAffiliation () {
|
|
|
|
return this.chatroomview.model.occupants
|
|
|
|
.where({'affiliation': this.model.get('affiliation')})
|
|
|
|
.map(item => {
|
|
|
|
return {
|
|
|
|
'jid': item.get('jid'),
|
|
|
|
'nick': item.get('nick'),
|
|
|
|
'affiliation': item.get('affiliation')
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
getUsersWithRole () {
|
|
|
|
return this.chatroomview.model.occupants
|
|
|
|
.where({'role': this.model.get('role')})
|
|
|
|
.map(item => {
|
|
|
|
return {
|
|
|
|
'jid': item.get('jid'),
|
|
|
|
'nick': item.get('nick'),
|
|
|
|
'role': item.get('role')
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
queryRole (ev) {
|
|
|
|
ev.stopPropagation();
|
|
|
|
ev.preventDefault();
|
|
|
|
const data = new FormData(ev.target);
|
|
|
|
const role = data.get('role');
|
|
|
|
this.model.set({'role': null}, {'silent': true});
|
|
|
|
this.model.set({'role': role});
|
|
|
|
},
|
|
|
|
|
|
|
|
queryAffiliation (ev) {
|
|
|
|
ev.stopPropagation();
|
|
|
|
ev.preventDefault();
|
|
|
|
const data = new FormData(ev.target);
|
|
|
|
const affiliation = data.get('affiliation');
|
|
|
|
this.model.set({'affiliation': null}, {'silent': true});
|
|
|
|
this.model.set({'affiliation': affiliation});
|
|
|
|
},
|
|
|
|
|
|
|
|
assignAffiliation (ev) {
|
|
|
|
ev.stopPropagation();
|
|
|
|
ev.preventDefault();
|
|
|
|
const data = new FormData(ev.target);
|
|
|
|
const affiliation = data.get('affiliation');
|
|
|
|
const attrs = {
|
|
|
|
'jid': data.get('jid'),
|
|
|
|
'reason': data.get('reason')
|
|
|
|
}
|
|
|
|
const current_affiliation = this.model.get('affiliation');
|
|
|
|
this.chatroomview.model.setAffiliation(affiliation, [attrs])
|
|
|
|
.then(async () => {
|
|
|
|
this.alert(__('Affiliation changed'), 'primary');
|
|
|
|
await this.chatroomview.model.occupants.fetchMembers()
|
|
|
|
this.model.set({'affiliation': null}, {'silent': true});
|
|
|
|
this.model.set({'affiliation': current_affiliation});
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
this.alert(__('Sorry, something went wrong while trying to set the affiliation'), 'danger');
|
2019-11-06 11:01:34 +01:00
|
|
|
log.error(err);
|
2019-07-04 14:12:12 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
assignRole (ev) {
|
|
|
|
ev.stopPropagation();
|
|
|
|
ev.preventDefault();
|
|
|
|
const data = new FormData(ev.target);
|
2019-09-07 23:41:48 +02:00
|
|
|
const occupant = this.chatroomview.model.getOccupant(data.get('jid') || data.get('nick'));
|
2019-07-04 14:12:12 +02:00
|
|
|
const role = data.get('role');
|
|
|
|
const reason = data.get('reason');
|
|
|
|
const current_role = this.model.get('role');
|
|
|
|
this.chatroomview.model.setRole(occupant, role, reason,
|
|
|
|
() => {
|
|
|
|
this.alert(__('Role changed'), 'primary');
|
|
|
|
this.model.set({'role': null}, {'silent': true});
|
|
|
|
this.model.set({'role': current_role});
|
|
|
|
},
|
|
|
|
(e) => {
|
|
|
|
if (sizzle(`not-allowed[xmlns="${Strophe.NS.STANZAS}"]`, e).length) {
|
|
|
|
this.alert(__('You\'re not allowed to make that change'), 'danger');
|
|
|
|
} else {
|
|
|
|
this.alert(__('Sorry, something went wrong while trying to set the role'), 'danger');
|
2019-09-07 23:41:48 +02:00
|
|
|
if (u.isErrorObject(e)) {
|
2019-11-06 11:01:34 +01:00
|
|
|
log.error(e);
|
2019-09-07 23:41:48 +02:00
|
|
|
}
|
2019-07-04 14:12:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
_converse.ListChatRoomsModal = _converse.BootstrapModal.extend({
|
2020-01-23 10:18:41 +01:00
|
|
|
id: "list-chatrooms-modal",
|
2018-10-23 03:41:38 +02:00
|
|
|
|
|
|
|
events: {
|
|
|
|
'submit form': 'showRooms',
|
|
|
|
'click a.room-info': 'toggleRoomInfo',
|
|
|
|
'change input[name=nick]': 'setNick',
|
2019-04-05 12:49:24 +02:00
|
|
|
'change input[name=server]': 'setDomainFromEvent',
|
2018-10-23 03:41:38 +02:00
|
|
|
'click .open-room': 'openRoom'
|
|
|
|
},
|
|
|
|
|
|
|
|
initialize () {
|
|
|
|
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
2019-02-26 10:34:41 +01:00
|
|
|
if (_converse.muc_domain && !this.model.get('muc_domain')) {
|
|
|
|
this.model.save('muc_domain', _converse.muc_domain);
|
|
|
|
}
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change:muc_domain', this.onDomainChange);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
toHTML () {
|
2019-02-26 10:34:41 +01:00
|
|
|
const muc_domain = this.model.get('muc_domain') || _converse.muc_domain;
|
2019-04-29 09:07:15 +02:00
|
|
|
return tpl_list_chatrooms_modal(Object.assign(this.model.toJSON(), {
|
2019-02-26 10:34:41 +01:00
|
|
|
'show_form': !_converse.locked_muc_domain,
|
|
|
|
'server_placeholder': muc_domain ? muc_domain : __('conference.example.org')
|
2018-10-23 03:41:38 +02:00
|
|
|
}));
|
|
|
|
},
|
|
|
|
|
|
|
|
afterRender () {
|
2019-02-26 10:34:41 +01:00
|
|
|
if (_converse.locked_muc_domain) {
|
|
|
|
this.updateRoomsList();
|
|
|
|
} else {
|
|
|
|
this.el.addEventListener('shown.bs.modal',
|
|
|
|
() => this.el.querySelector('input[name="server"]').focus(),
|
|
|
|
false
|
|
|
|
);
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
openRoom (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
const jid = ev.target.getAttribute('data-room-jid');
|
|
|
|
const name = ev.target.getAttribute('data-room-name');
|
|
|
|
this.modal.hide();
|
|
|
|
_converse.api.rooms.open(jid, {'name': name});
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleRoomInfo (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
toggleRoomInfo(ev);
|
|
|
|
},
|
|
|
|
|
2019-10-09 16:01:38 +02:00
|
|
|
onDomainChange () {
|
2018-10-23 03:41:38 +02:00
|
|
|
if (_converse.auto_list_rooms) {
|
|
|
|
this.updateRoomsList();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
roomStanzaItemToHTMLElement (groupchat) {
|
|
|
|
const name = Strophe.unescapeNode(groupchat.getAttribute('name') || groupchat.getAttribute('jid'));
|
|
|
|
const div = document.createElement('div');
|
|
|
|
div.innerHTML = tpl_room_item({
|
|
|
|
'name': Strophe.xmlunescape(name),
|
|
|
|
'jid': groupchat.getAttribute('jid'),
|
|
|
|
'open_title': __('Click to open this groupchat'),
|
|
|
|
'info_title': __('Show more information on this groupchat')
|
|
|
|
});
|
|
|
|
return div.firstElementChild;
|
|
|
|
},
|
|
|
|
|
|
|
|
removeSpinner () {
|
2019-05-14 11:38:41 +02:00
|
|
|
sizzle('.spinner', this.el).forEach(u.removeElement);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
informNoRoomsFound () {
|
|
|
|
const chatrooms_el = this.el.querySelector('.available-chatrooms');
|
2019-02-26 11:05:15 +01:00
|
|
|
chatrooms_el.innerHTML = tpl_rooms_results({'feedback_text': __('No groupchats found')});
|
2018-10-23 03:41:38 +02:00
|
|
|
const input_el = this.el.querySelector('input[name="server"]');
|
|
|
|
input_el.classList.remove('hidden')
|
|
|
|
this.removeSpinner();
|
|
|
|
},
|
|
|
|
|
|
|
|
onRoomsFound (iq) {
|
|
|
|
/* Handle the IQ stanza returned from the server, containing
|
|
|
|
* all its public groupchats.
|
|
|
|
*/
|
|
|
|
const available_chatrooms = this.el.querySelector('.available-chatrooms');
|
2019-05-14 11:38:41 +02:00
|
|
|
const rooms = sizzle('query item', iq);
|
|
|
|
if (rooms.length) {
|
2019-02-26 11:05:15 +01:00
|
|
|
available_chatrooms.innerHTML = tpl_rooms_results({'feedback_text': __('Groupchats found:')});
|
2018-10-23 03:41:38 +02:00
|
|
|
const fragment = document.createDocumentFragment();
|
2019-05-14 11:38:41 +02:00
|
|
|
rooms.map(this.roomStanzaItemToHTMLElement)
|
|
|
|
.filter(r => r)
|
|
|
|
.forEach(child => fragment.appendChild(child));
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
available_chatrooms.appendChild(fragment);
|
|
|
|
this.removeSpinner();
|
|
|
|
} else {
|
|
|
|
this.informNoRoomsFound();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
2018-02-22 17:39:44 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
updateRoomsList () {
|
|
|
|
/* Send an IQ stanza to the server asking for all groupchats
|
|
|
|
*/
|
2018-10-25 07:32:44 +02:00
|
|
|
const iq = $iq({
|
|
|
|
'to': this.model.get('muc_domain'),
|
|
|
|
'from': _converse.connection.jid,
|
|
|
|
'type': "get"
|
|
|
|
}).c("query", {xmlns: Strophe.NS.DISCO_ITEMS});
|
|
|
|
_converse.api.sendIQ(iq)
|
|
|
|
.then(iq => this.onRoomsFound(iq))
|
2019-10-09 16:01:38 +02:00
|
|
|
.catch(() => this.informNoRoomsFound())
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
showRooms (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
const data = new FormData(ev.target);
|
2019-04-05 12:49:24 +02:00
|
|
|
this.model.setDomain(data.get('server'));
|
2018-10-23 03:41:38 +02:00
|
|
|
this.updateRoomsList();
|
|
|
|
},
|
|
|
|
|
2019-04-05 12:49:24 +02:00
|
|
|
setDomainFromEvent (ev) {
|
|
|
|
this.model.setDomain(ev.target.value);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
setNick (ev) {
|
|
|
|
this.model.save({nick: ev.target.value});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
_converse.AddChatRoomModal = _converse.BootstrapModal.extend({
|
2020-01-23 10:18:41 +01:00
|
|
|
id: 'add-chatroom-modal',
|
2018-10-23 03:41:38 +02:00
|
|
|
|
|
|
|
events: {
|
2019-12-30 13:23:52 +01:00
|
|
|
'submit form.add-chatroom': 'openChatRoom',
|
|
|
|
'keyup .roomjid-input': 'checkRoomidPolicy',
|
|
|
|
'change .roomjid-input': 'checkRoomidPolicy'
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-02-26 10:34:41 +01:00
|
|
|
initialize () {
|
|
|
|
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change:muc_domain', this.render);
|
2019-12-30 13:23:52 +01:00
|
|
|
this.muc_roomid_policy_error_msg = null;
|
2019-02-26 10:34:41 +01:00
|
|
|
},
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
toHTML () {
|
2019-02-26 10:34:41 +01:00
|
|
|
let placeholder = '';
|
|
|
|
if (!_converse.locked_muc_domain) {
|
|
|
|
const muc_domain = this.model.get('muc_domain') || _converse.muc_domain;
|
|
|
|
placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
|
|
|
|
}
|
2019-04-29 09:07:15 +02:00
|
|
|
return tpl_add_chatroom_modal(Object.assign(this.model.toJSON(), {
|
2019-03-26 12:16:18 +01:00
|
|
|
'_converse': _converse,
|
2019-02-26 10:34:41 +01:00
|
|
|
'label_room_address': _converse.muc_domain ? __('Groupchat name') : __('Groupchat address'),
|
2019-12-30 13:23:52 +01:00
|
|
|
'chatroom_placeholder': placeholder,
|
|
|
|
'muc_roomid_policy_error_msg': this.muc_roomid_policy_error_msg,
|
|
|
|
'muc_roomid_policy_hint': xss.filterXSS(_converse.muc_roomid_policy_hint, {'whiteList': {b: [], br: [], em: []}})
|
2018-10-23 03:41:38 +02:00
|
|
|
}));
|
|
|
|
},
|
|
|
|
|
|
|
|
afterRender () {
|
|
|
|
this.el.addEventListener('shown.bs.modal', () => {
|
|
|
|
this.el.querySelector('input[name="chatroom"]').focus();
|
|
|
|
}, false);
|
|
|
|
},
|
|
|
|
|
|
|
|
parseRoomDataFromEvent (form) {
|
|
|
|
const data = new FormData(form);
|
|
|
|
const jid = data.get('chatroom');
|
2019-03-26 13:29:33 +01:00
|
|
|
let nick;
|
|
|
|
if (_converse.locked_muc_nickname) {
|
|
|
|
nick = _converse.getDefaultMUCNickname();
|
|
|
|
if (!nick) {
|
|
|
|
throw new Error("Using locked_muc_nickname but no nickname found!");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nick = data.get('nickname').trim();
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return {
|
|
|
|
'jid': jid,
|
2019-03-26 13:29:33 +01:00
|
|
|
'nick': nick
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
openChatRoom (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
const data = this.parseRoomDataFromEvent(ev.target);
|
|
|
|
if (data.nick === "") {
|
|
|
|
// Make sure defaults apply if no nick is provided.
|
|
|
|
data.nick = undefined;
|
|
|
|
}
|
2019-02-26 10:34:41 +01:00
|
|
|
let jid;
|
|
|
|
if (_converse.locked_muc_domain || (_converse.muc_domain && !u.isValidJID(data.jid))) {
|
|
|
|
jid = `${Strophe.escapeNode(data.jid)}@${_converse.muc_domain}`;
|
|
|
|
} else {
|
|
|
|
jid = data.jid
|
2019-04-05 12:49:24 +02:00
|
|
|
this.model.setDomain(jid);
|
2019-02-26 10:34:41 +01:00
|
|
|
}
|
2019-04-29 09:07:15 +02:00
|
|
|
_converse.api.rooms.open(jid, Object.assign(data, {jid}));
|
2018-10-23 03:41:38 +02:00
|
|
|
this.modal.hide();
|
|
|
|
ev.target.reset();
|
2019-12-30 13:23:52 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
checkRoomidPolicy () {
|
|
|
|
if (_converse.muc_roomid_policy && _converse.muc_domain) {
|
|
|
|
let jid = this.el.querySelector('.roomjid-input').value;
|
|
|
|
if (converse.locked_muc_domain || !u.isValidJID(jid)) {
|
|
|
|
jid = `${Strophe.escapeNode(jid)}@${_converse.muc_domain}`;
|
|
|
|
}
|
|
|
|
const roomid = Strophe.getNodeFromJid(jid);
|
|
|
|
const roomdomain = Strophe.getDomainFromJid(jid);
|
|
|
|
if (_converse.muc_domain !== roomdomain ||
|
|
|
|
_converse.muc_roomid_policy.test(roomid)) {
|
|
|
|
this.muc_roomid_policy_error_msg = null;
|
|
|
|
} else {
|
|
|
|
this.muc_roomid_policy_error_msg = __('Groupchat id is invalid.');
|
|
|
|
}
|
|
|
|
this.render();
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
});
|
2018-02-22 17:39:44 +01:00
|
|
|
|
2018-03-26 21:11:32 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
_converse.RoomDetailsModal = _converse.BootstrapModal.extend({
|
2020-01-23 10:18:41 +01:00
|
|
|
id: "room-details-modal",
|
2018-02-22 17:39:44 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
initialize () {
|
|
|
|
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change', this.render);
|
2020-01-28 19:25:21 +01:00
|
|
|
this.listenTo(this.model.features, 'change', this.render);
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model.occupants, 'add', this.render);
|
|
|
|
this.listenTo(this.model.occupants, 'change', this.render);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-22 17:39:44 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
toHTML () {
|
2019-04-29 09:07:15 +02:00
|
|
|
return tpl_chatroom_details_modal(Object.assign(
|
2018-10-23 03:41:38 +02:00
|
|
|
this.model.toJSON(), {
|
2020-01-16 13:01:57 +01:00
|
|
|
'config': this.model.config.toJSON(),
|
2018-10-23 03:41:38 +02:00
|
|
|
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
|
2019-01-10 12:14:45 +01:00
|
|
|
'features': this.model.features.toJSON(),
|
|
|
|
'num_occupants': this.model.occupants.length,
|
2018-10-23 03:41:38 +02:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
2018-02-22 17:39:44 +01:00
|
|
|
|
|
|
|
|
2019-03-29 15:47:23 +01:00
|
|
|
/**
|
2019-09-19 16:54:55 +02:00
|
|
|
* NativeView which renders a groupchat, based upon
|
2019-11-08 09:49:58 +01:00
|
|
|
* { @link _converse.ChatBoxView } for normal one-on-one chat boxes.
|
2019-03-29 15:47:23 +01:00
|
|
|
* @class
|
|
|
|
* @namespace _converse.ChatRoomView
|
|
|
|
* @memberOf _converse
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
_converse.ChatRoomView = _converse.ChatBoxView.extend({
|
|
|
|
length: 300,
|
|
|
|
tagName: 'div',
|
|
|
|
className: 'chatbox chatroom hidden',
|
|
|
|
is_chatroom: true,
|
|
|
|
events: {
|
|
|
|
'change input.fileupload': 'onFileSelection',
|
|
|
|
'click .chat-msg__action-edit': 'onMessageEditButtonClicked',
|
2019-09-24 15:33:41 +02:00
|
|
|
'click .chat-msg__action-retract': 'onMessageRetractButtonClicked',
|
2018-10-23 03:41:38 +02:00
|
|
|
'click .chatbox-navback': 'showControlBox',
|
|
|
|
'click .hide-occupants': 'hideOccupants',
|
|
|
|
'click .new-msgs-indicator': 'viewUnreadMessages',
|
2019-11-28 17:01:08 +01:00
|
|
|
// 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) },
|
2018-10-23 03:41:38 +02:00
|
|
|
'click .send-button': 'onFormSubmitted',
|
|
|
|
'click .toggle-call': 'toggleCall',
|
|
|
|
'click .toggle-occupants': 'toggleOccupants',
|
|
|
|
'click .upload-file': 'toggleFileUpload',
|
2019-11-28 17:01:08 +01:00
|
|
|
'dragover .chat-textarea': 'onDragOver',
|
|
|
|
'drop .chat-textarea': 'onDrop',
|
|
|
|
'input .chat-textarea': 'inputChanged',
|
2019-05-26 10:58:52 +02:00
|
|
|
'keydown .chat-textarea': 'onKeyDown',
|
|
|
|
'keyup .chat-textarea': 'onKeyUp',
|
2019-12-04 16:01:34 +01:00
|
|
|
'mousedown .dragresize-occupants-left': 'onStartResizeOccupants',
|
2019-06-16 14:16:35 +02:00
|
|
|
'paste .chat-textarea': 'onPaste',
|
2019-11-28 17:01:08 +01:00
|
|
|
'submit .muc-nickname-form': 'submitNickname'
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-12-02 14:00:19 +01:00
|
|
|
async initialize () {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.initDebounced();
|
|
|
|
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model.messages, 'add', this.onMessageAdded);
|
|
|
|
this.listenTo(this.model.messages, 'rendered', this.scrollDown);
|
2019-05-21 09:56:27 +02:00
|
|
|
this.model.messages.on('reset', () => {
|
|
|
|
this.content.innerHTML = '';
|
|
|
|
this.removeAll();
|
|
|
|
});
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change', this.renderHeading);
|
2019-12-10 15:40:33 +01:00
|
|
|
this.listenTo(this.model.session, 'change:connection_status', this.onConnectionStatusChanged);
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change:hidden_occupants', this.updateOccupantsToggle);
|
|
|
|
this.listenTo(this.model, 'change:subject', this.setChatRoomSubject);
|
|
|
|
this.listenTo(this.model, 'configurationNeeded', this.getAndRenderConfigurationForm);
|
|
|
|
this.listenTo(this.model, 'destroy', this.hide);
|
|
|
|
this.listenTo(this.model, 'show', this.show);
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model.features, 'change:moderated', this.renderBottomPanel);
|
2020-02-10 11:23:55 +01:00
|
|
|
this.listenTo(this.model.features, 'change:open', this.renderHeading);
|
2019-04-11 23:10:57 +02:00
|
|
|
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model.occupants, 'add', this.onOccupantAdded);
|
|
|
|
this.listenTo(this.model.occupants, 'remove', this.onOccupantRemoved);
|
|
|
|
this.listenTo(this.model.occupants, 'change:show', this.showJoinOrLeaveNotification);
|
|
|
|
this.listenTo(this.model.occupants, 'change:role', this.onOccupantRoleChanged);
|
|
|
|
this.listenTo(this.model.occupants, 'change:affiliation', this.onOccupantAffiliationChanged);
|
2018-10-23 03:41:38 +02:00
|
|
|
|
2019-12-04 16:01:34 +01:00
|
|
|
// Bind so that we can pass it to addEventListener and removeEventListener
|
|
|
|
this.onMouseMove = this.onMouseMove.bind(this);
|
|
|
|
this.onMouseUp = this.onMouseUp.bind(this);
|
|
|
|
|
2019-05-15 14:47:04 +02:00
|
|
|
this.render();
|
2020-01-27 13:20:23 +01:00
|
|
|
this.createSidebarView();
|
2019-12-02 14:00:19 +01:00
|
|
|
await this.updateAfterMessagesFetched();
|
2019-06-01 16:16:30 +02:00
|
|
|
this.onConnectionStatusChanged();
|
2020-02-02 15:38:37 +01:00
|
|
|
this.model.maybeShow();
|
2019-03-29 15:47:23 +01:00
|
|
|
/**
|
2019-12-18 12:42:40 +01:00
|
|
|
* Triggered once a { @link _converse.ChatRoomView } has been opened
|
|
|
|
* @event _converse#chatRoomViewInitialized
|
2019-03-29 15:47:23 +01:00
|
|
|
* @type { _converse.ChatRoomView }
|
2019-12-18 12:42:40 +01:00
|
|
|
* @example _converse.api.listen.on('chatRoomViewInitialized', view => { ... });
|
2019-03-29 15:47:23 +01:00
|
|
|
*/
|
2019-12-18 12:42:40 +01:00
|
|
|
_converse.api.trigger('chatRoomViewInitialized', this);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
render () {
|
|
|
|
this.el.setAttribute('id', this.model.get('box_id'));
|
|
|
|
this.el.innerHTML = tpl_chatroom();
|
|
|
|
this.renderHeading();
|
|
|
|
this.renderChatArea();
|
2019-04-10 23:25:14 +02:00
|
|
|
this.renderBottomPanel();
|
2019-11-28 17:01:08 +01:00
|
|
|
if (!_converse.muc_show_logs_before_join) {
|
2019-12-10 15:40:33 +01:00
|
|
|
this.model.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED && this.showSpinner();
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2019-11-08 09:49:58 +01:00
|
|
|
if (!this.model.get('hidden')) {
|
|
|
|
this.show();
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
return this;
|
|
|
|
},
|
2018-02-22 18:41:01 +01:00
|
|
|
|
2020-01-21 11:08:59 +01:00
|
|
|
/**
|
|
|
|
* Renders the MUC heading if any relevant attributes have changed.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#renderHeading
|
|
|
|
* @param { _converse.ChatRoom } [item]
|
|
|
|
*/
|
2020-02-10 23:21:35 +01:00
|
|
|
renderHeading () {
|
|
|
|
render(this.generateHeadingTemplate(), this.el.querySelector('.chat-head-chatroom'));
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2020-02-10 11:23:55 +01:00
|
|
|
|
2019-04-10 23:25:14 +02:00
|
|
|
renderBottomPanel () {
|
|
|
|
const container = this.el.querySelector('.bottom-panel');
|
2019-12-10 15:40:33 +01:00
|
|
|
const entered = this.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED;
|
2019-11-28 17:01:08 +01:00
|
|
|
const can_edit = entered && !(this.model.features.get('moderated') && this.model.getOwnRole() === 'visitor');
|
2019-11-29 10:38:17 +01:00
|
|
|
container.innerHTML = tpl_chatroom_bottom_panel({__, can_edit, entered});
|
2019-11-28 17:01:08 +01:00
|
|
|
if (entered && can_edit) {
|
2019-10-29 11:30:31 +01:00
|
|
|
this.renderMessageForm();
|
|
|
|
this.initMentionAutoComplete();
|
2019-04-10 23:25:14 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
renderChatArea () {
|
2019-09-24 15:33:41 +02:00
|
|
|
// Render the UI container in which groupchat messages will appear.
|
2019-05-15 14:47:04 +02:00
|
|
|
if (this.el.querySelector('.chat-area') === null) {
|
2018-10-23 03:41:38 +02:00
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
2019-05-15 14:47:04 +02:00
|
|
|
container_el.insertAdjacentHTML(
|
|
|
|
'beforeend',
|
2019-11-28 17:01:08 +01:00
|
|
|
tpl_chatarea({
|
|
|
|
__,
|
|
|
|
'muc_show_logs_before_join': _converse.muc_show_logs_before_join,
|
|
|
|
'show_send_button': _converse.show_send_button
|
|
|
|
})
|
2019-05-15 14:47:04 +02:00
|
|
|
);
|
2018-10-23 03:41:38 +02:00
|
|
|
this.content = this.el.querySelector('.chat-content');
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2020-01-27 13:20:23 +01:00
|
|
|
createSidebarView () {
|
2019-05-15 14:47:04 +02:00
|
|
|
this.model.occupants.chatroomview = this;
|
2020-01-27 13:20:23 +01:00
|
|
|
this.sidebar_view = new _converse.MUCSidebar({'model': this.model.occupants});
|
2019-05-15 14:47:04 +02:00
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
2019-12-04 16:01:34 +01:00
|
|
|
const occupants_width = this.model.get('occupants_width');
|
2020-01-27 13:20:23 +01:00
|
|
|
if (this.sidebar_view && occupants_width !== undefined) {
|
|
|
|
this.sidebar_view.el.style.flex = "0 0 " + occupants_width + "px";
|
2019-12-04 16:01:34 +01:00
|
|
|
}
|
2020-01-27 13:20:23 +01:00
|
|
|
container_el.insertAdjacentElement('beforeend', this.sidebar_view.el);
|
2019-12-04 16:01:34 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
onStartResizeOccupants (ev) {
|
|
|
|
this.resizing = true;
|
|
|
|
this.el.addEventListener('mousemove', this.onMouseMove);
|
|
|
|
this.el.addEventListener('mouseup', this.onMouseUp);
|
|
|
|
|
2020-01-27 13:20:23 +01:00
|
|
|
const style = window.getComputedStyle(this.sidebar_view.el);
|
2019-12-04 16:01:34 +01:00
|
|
|
this.width = parseInt(style.width.replace(/px$/, ''), 10);
|
|
|
|
this.prev_pageX = ev.pageX;
|
|
|
|
},
|
|
|
|
|
|
|
|
onMouseMove (ev) {
|
|
|
|
if (this.resizing) {
|
|
|
|
ev.preventDefault();
|
|
|
|
const delta = this.prev_pageX - ev.pageX;
|
2020-01-27 13:20:23 +01:00
|
|
|
this.resizeSidebarView(delta, ev.pageX);
|
2019-12-04 16:01:34 +01:00
|
|
|
this.prev_pageX = ev.pageX;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onMouseUp (ev) {
|
|
|
|
if (this.resizing) {
|
|
|
|
ev.preventDefault();
|
|
|
|
this.resizing = false;
|
|
|
|
this.el.removeEventListener('mousemove', this.onMouseMove);
|
|
|
|
this.el.removeEventListener('mouseup', this.onMouseUp);
|
2020-01-27 13:20:23 +01:00
|
|
|
const element_position = this.sidebar_view.el.getBoundingClientRect();
|
|
|
|
const occupants_width = this.calculateSidebarWidth(element_position, 0);
|
2019-12-04 16:01:34 +01:00
|
|
|
const attrs = {occupants_width};
|
|
|
|
_converse.connection.connected ? this.model.save(attrs) : this.model.set(attrs);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-01-27 13:20:23 +01:00
|
|
|
resizeSidebarView (delta, current_mouse_position) {
|
|
|
|
const element_position = this.sidebar_view.el.getBoundingClientRect();
|
2019-12-04 16:01:34 +01:00
|
|
|
if (this.is_minimum) {
|
|
|
|
this.is_minimum = element_position.left < current_mouse_position;
|
|
|
|
} else if (this.is_maximum) {
|
|
|
|
this.is_maximum = element_position.left > current_mouse_position;
|
|
|
|
} else {
|
2020-01-27 13:20:23 +01:00
|
|
|
const occupants_width = this.calculateSidebarWidth(element_position, delta);
|
|
|
|
this.sidebar_view.el.style.flex = "0 0 " + occupants_width + "px";
|
2019-12-04 16:01:34 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-01-27 13:20:23 +01:00
|
|
|
calculateSidebarWidth(element_position, delta) {
|
2019-12-04 16:01:34 +01:00
|
|
|
let occupants_width = element_position.width + delta;
|
|
|
|
const room_width = this.el.clientWidth;
|
|
|
|
// keeping display in boundaries
|
|
|
|
if (occupants_width < (room_width * 0.20)) {
|
|
|
|
// set pixel to 20% width
|
|
|
|
occupants_width = (room_width * 0.20);
|
|
|
|
this.is_minimum = true;
|
|
|
|
} else if (occupants_width > (room_width * 0.75)) {
|
|
|
|
// set pixel to 75% width
|
|
|
|
occupants_width = (room_width * 0.75);
|
|
|
|
this.is_maximum = true;
|
|
|
|
} 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;
|
|
|
|
} else {
|
|
|
|
this.is_maximum = false;
|
|
|
|
this.is_minimum = false;
|
|
|
|
}
|
|
|
|
return occupants_width;
|
2019-05-15 14:47:04 +02:00
|
|
|
},
|
|
|
|
|
2019-05-27 22:45:51 +02:00
|
|
|
getAutoCompleteList () {
|
2020-02-21 12:30:37 +01:00
|
|
|
return this.model.getAllKnownNicknames().map(nick => ({'label': nick, 'value': `@${nick}`}));
|
2019-05-27 22:45:51 +02:00
|
|
|
},
|
|
|
|
|
2019-11-11 15:27:34 +01:00
|
|
|
getAutoCompleteListItem(text, input) {
|
|
|
|
input = input.trim();
|
|
|
|
const element = document.createElement("li");
|
|
|
|
element.setAttribute("aria-selected", "false");
|
|
|
|
|
|
|
|
if (_converse.muc_mention_autocomplete_show_avatar) {
|
|
|
|
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');
|
|
|
|
}
|
|
|
|
|
|
|
|
img.setAttribute("src", dataUri);
|
|
|
|
img.setAttribute("width", "22");
|
|
|
|
img.setAttribute("class", "avatar avatar-autocomplete");
|
|
|
|
element.appendChild(img);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
} else {
|
|
|
|
element.appendChild(document.createTextNode(txt));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return element;
|
|
|
|
},
|
|
|
|
|
2019-03-03 20:56:48 +01:00
|
|
|
initMentionAutoComplete () {
|
2019-03-28 12:35:40 +01:00
|
|
|
this.mention_auto_complete = new _converse.AutoComplete(this.el, {
|
2018-10-23 03:41:38 +02:00
|
|
|
'auto_first': true,
|
|
|
|
'auto_evaluate': false,
|
2019-06-19 14:51:34 +02:00
|
|
|
'min_chars': _converse.muc_mention_autocomplete_min_chars,
|
2018-10-23 03:41:38 +02:00
|
|
|
'match_current_word': true,
|
2019-05-27 22:45:51 +02:00
|
|
|
'list': () => this.getAutoCompleteList(),
|
2019-11-11 15:27:34 +01:00
|
|
|
'filter': _converse.muc_mention_autocomplete_filter == 'contains' ? _converse.FILTER_CONTAINS : _converse.FILTER_STARTSWITH,
|
2019-03-04 09:42:16 +01:00
|
|
|
'ac_triggers': ["Tab", "@"],
|
2019-11-11 15:27:34 +01:00
|
|
|
'include_triggers': [],
|
|
|
|
'item': this.getAutoCompleteListItem
|
2018-10-23 03:41:38 +02:00
|
|
|
});
|
2019-03-28 12:35:40 +01:00
|
|
|
this.mention_auto_complete.on('suggestion-box-selectcomplete', () => (this.auto_completing = false));
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-22 15:51:44 +01:00
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
|
|
|
* Get the nickname value from the form and then join the groupchat with it.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#submitNickname
|
|
|
|
* @param { Event }
|
|
|
|
*/
|
|
|
|
submitNickname (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
const nick = ev.target.nick.value.trim();
|
2019-12-09 16:54:50 +01:00
|
|
|
nick && this.model.join(nick);
|
2019-11-28 17:01:08 +01:00
|
|
|
},
|
|
|
|
|
2019-05-26 10:58:52 +02:00
|
|
|
onKeyDown (ev) {
|
|
|
|
if (this.mention_auto_complete.onKeyDown(ev)) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return;
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
2019-06-16 14:16:35 +02:00
|
|
|
return _converse.ChatBoxView.prototype.onKeyDown.call(this, ev);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2019-05-26 10:58:52 +02:00
|
|
|
onKeyUp (ev) {
|
2019-03-28 12:35:40 +01:00
|
|
|
this.mention_auto_complete.evaluate(ev);
|
2019-06-16 14:16:35 +02:00
|
|
|
return _converse.ChatBoxView.prototype.onKeyUp.call(this, ev);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2019-09-24 15:33:41 +02:00
|
|
|
async onMessageRetractButtonClicked (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
const msg_el = u.ancestor(ev.target, '.message');
|
|
|
|
const msgid = msg_el.getAttribute('data-msgid');
|
|
|
|
const time = msg_el.getAttribute('data-isodate');
|
|
|
|
const message = this.model.messages.findWhere({msgid, time});
|
|
|
|
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.get('sender') === 'me') {
|
|
|
|
const messages = [__('Are you sure you want to retract this message?')];
|
|
|
|
if (_converse.show_retraction_warning) {
|
|
|
|
messages[1] = retraction_warning;
|
|
|
|
}
|
|
|
|
const result = await _converse.api.confirm(__('Confirm'), messages);
|
|
|
|
if (result) {
|
|
|
|
this.retractOwnMessage(message);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let messages = [
|
|
|
|
__('You are about to retract this message.'),
|
|
|
|
__('You may optionally include a message, explaining the reason for the retraction.')
|
|
|
|
];
|
|
|
|
if (_converse.show_retraction_warning) {
|
|
|
|
messages = [messages[0], retraction_warning, messages[1]]
|
|
|
|
}
|
|
|
|
const reason = await _converse.api.prompt(
|
|
|
|
__('Message Retraction'),
|
|
|
|
messages,
|
|
|
|
__('Optional reason')
|
|
|
|
);
|
|
|
|
if (reason !== false) {
|
|
|
|
this.retractOtherMessage(message, reason);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retract one of your messages in this groupchat.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#retractOwnMessage
|
|
|
|
* @param { _converse.Message } message - The message which we're retracting.
|
|
|
|
*/
|
|
|
|
retractOwnMessage(message) {
|
2020-02-18 15:42:55 +01:00
|
|
|
this.model.retractOwnMessage(message)
|
2019-09-24 15:33:41 +02:00
|
|
|
.catch(e => {
|
|
|
|
const errmsg = __('Sorry, something went wrong while trying to retract your message.');
|
|
|
|
if (u.isErrorStanza(e)) {
|
|
|
|
this.showErrorMessage(errmsg);
|
|
|
|
} else {
|
|
|
|
this.showErrorMessage(errmsg);
|
|
|
|
this.showErrorMessage(e.message);
|
|
|
|
}
|
|
|
|
log.error(e);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retract someone else's message in this groupchat.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#retractOtherMessage
|
|
|
|
* @param { _converse.Message } message - The message which we're retracting.
|
|
|
|
* @param { string } [reason] - The reason for retracting the message.
|
|
|
|
*/
|
|
|
|
async retractOtherMessage (message, reason) {
|
2020-02-18 16:06:59 +01:00
|
|
|
const result = await this.model.retractOtherMessage(message, reason);
|
2019-09-24 15:33:41 +02:00
|
|
|
if (result === null) {
|
|
|
|
const err_msg = __(`A timeout occurred while trying to retract the message`);
|
|
|
|
_converse.api.alert('error', __('Error'), err_msg);
|
|
|
|
_converse.log(err_msg, Strophe.LogLevel.WARN);
|
|
|
|
} else if (u.isErrorStanza(result)) {
|
|
|
|
const err_msg = __(`Sorry, you're not allowed to retract this message.`);
|
|
|
|
_converse.api.alert('error', __('Error'), err_msg);
|
|
|
|
_converse.log(err_msg, Strophe.LogLevel.WARN);
|
|
|
|
_converse.log(result, Strophe.LogLevel.WARN);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-07-04 14:12:12 +02:00
|
|
|
showModeratorToolsModal (affiliation) {
|
2019-09-07 23:41:48 +02:00
|
|
|
if (!this.verifyRoles(['moderator'])) {
|
|
|
|
return;
|
|
|
|
}
|
2020-01-21 11:08:59 +01:00
|
|
|
if (isUndefined(this.model.modtools_modal)) {
|
2019-09-19 16:54:55 +02:00
|
|
|
const model = new Model({'affiliation': affiliation});
|
2019-07-04 14:12:12 +02:00
|
|
|
this.modtools_modal = new _converse.ModeratorToolsModal({'model': model, 'chatroomview': this});
|
|
|
|
} else {
|
|
|
|
this.modtools_modal.set('affiliation', affiliation);
|
|
|
|
}
|
|
|
|
this.modtools_modal.show();
|
|
|
|
},
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
showRoomDetailsModal (ev) {
|
|
|
|
ev.preventDefault();
|
2019-07-29 10:19:05 +02:00
|
|
|
if (this.model.room_details_modal === undefined) {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.model.room_details_modal = new _converse.RoomDetailsModal({'model': this.model});
|
|
|
|
}
|
|
|
|
this.model.room_details_modal.show(ev);
|
|
|
|
},
|
2018-06-04 19:53:33 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
showChatStateNotification (message) {
|
|
|
|
if (message.get('sender') === 'me') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return _converse.ChatBoxView.prototype.showChatStateNotification.apply(this, arguments);
|
|
|
|
},
|
2018-06-04 19:53:33 +02:00
|
|
|
|
2019-07-10 09:47:13 +02:00
|
|
|
onOccupantAffiliationChanged (occupant) {
|
|
|
|
if (occupant.get('jid') === _converse.bare_jid) {
|
|
|
|
this.renderHeading();
|
|
|
|
}
|
|
|
|
this.informOfOccupantsAffiliationChange(occupant);
|
|
|
|
},
|
|
|
|
|
|
|
|
informOfOccupantsAffiliationChange (occupant) {
|
2019-08-08 15:12:18 +02:00
|
|
|
const previous_affiliation = occupant._previousAttributes.affiliation;
|
|
|
|
const current_affiliation = occupant.get('affiliation');
|
2018-10-23 03:41:38 +02:00
|
|
|
|
|
|
|
if (previous_affiliation === 'admin') {
|
|
|
|
this.showChatEvent(__("%1$s is no longer an admin of this groupchat", occupant.get('nick')))
|
|
|
|
} else if (previous_affiliation === 'owner') {
|
|
|
|
this.showChatEvent(__("%1$s is no longer an owner of this groupchat", occupant.get('nick')))
|
|
|
|
} else if (previous_affiliation === 'outcast') {
|
|
|
|
this.showChatEvent(__("%1$s is no longer banned from this groupchat", occupant.get('nick')))
|
2018-06-04 19:53:33 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
if (current_affiliation === 'none' && previous_affiliation === 'member') {
|
2019-06-16 11:23:36 +02:00
|
|
|
this.showChatEvent(__("%1$s is no longer a member of this groupchat", occupant.get('nick')))
|
2018-10-23 03:41:38 +02:00
|
|
|
} if (current_affiliation === 'member') {
|
2019-06-16 11:23:36 +02:00
|
|
|
this.showChatEvent(__("%1$s is now a member of this groupchat", occupant.get('nick')))
|
2018-10-23 03:41:38 +02:00
|
|
|
} else if (current_affiliation === 'admin' || current_affiliation == 'owner') {
|
2018-10-28 23:21:27 +01:00
|
|
|
// For example: AppleJack is now an (admin|owner) of this groupchat
|
2018-10-27 23:12:02 +02:00
|
|
|
this.showChatEvent(__('%1$s is now an %2$s of this groupchat', occupant.get('nick'), current_affiliation))
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-07-10 09:47:13 +02:00
|
|
|
onOccupantRoleChanged (occupant, changed) {
|
|
|
|
if (occupant.get('jid') === _converse.bare_jid) {
|
|
|
|
this.renderBottomPanel();
|
|
|
|
}
|
|
|
|
this.informOfOccupantsRoleChange(occupant, changed);
|
|
|
|
},
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
informOfOccupantsRoleChange (occupant, changed) {
|
2019-07-22 17:52:06 +02:00
|
|
|
if (changed === "none" || occupant.changed.affiliation) {
|
|
|
|
// We don't inform of role changes if they accompany affiliation changes.
|
2018-10-27 22:56:05 +02:00
|
|
|
return;
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
const previous_role = occupant._previousAttributes.role;
|
|
|
|
if (previous_role === 'moderator') {
|
|
|
|
this.showChatEvent(__("%1$s is no longer a moderator", occupant.get('nick')))
|
|
|
|
}
|
|
|
|
if (previous_role === 'visitor') {
|
2019-07-22 16:51:44 +02:00
|
|
|
this.showChatEvent(__("%1$s has been given a voice", occupant.get('nick')))
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
if (occupant.get('role') === 'visitor') {
|
|
|
|
this.showChatEvent(__("%1$s has been muted", occupant.get('nick')))
|
|
|
|
}
|
|
|
|
if (occupant.get('role') === 'moderator') {
|
2019-05-14 13:55:19 +02:00
|
|
|
if (!['owner', 'admin'].includes(occupant.get('affiliation'))) {
|
|
|
|
// We only show this message if the user isn't already
|
|
|
|
// an admin or owner, otherwise this isn't new
|
|
|
|
// information.
|
|
|
|
this.showChatEvent(__("%1$s is now a moderator", occupant.get('nick')))
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
2018-06-04 19:53:33 +02:00
|
|
|
|
2020-02-07 14:58:26 +01:00
|
|
|
getHeadingButtons () {
|
2020-02-10 11:23:55 +01:00
|
|
|
const buttons = [{
|
|
|
|
'i18n_text': __('Details'),
|
|
|
|
'i18n_title': __('Show more information about this groupchat'),
|
|
|
|
'handler': ev => this.showRoomDetailsModal(ev),
|
|
|
|
'a_class': 'show-room-details-modal',
|
|
|
|
'icon_class': 'fa-info-circle',
|
|
|
|
'name': 'details'
|
|
|
|
}];
|
|
|
|
if (this.model.invitesAllowed()) {
|
|
|
|
buttons.push({
|
|
|
|
'i18n_text': __('Invite'),
|
|
|
|
'i18n_title': __('Invite someone to join this groupchat'),
|
|
|
|
'handler': ev => this.showInviteModal(ev),
|
|
|
|
'a_class': 'open-invite-modal',
|
|
|
|
'icon_class': 'fa-user-plus',
|
|
|
|
'name': 'invite'
|
|
|
|
});
|
2020-02-07 14:58:26 +01:00
|
|
|
}
|
|
|
|
if (this.model.getOwnAffiliation() === 'owner') {
|
2020-02-10 11:23:55 +01:00
|
|
|
buttons.push({
|
|
|
|
'i18n_text': __('Configure'),
|
|
|
|
'i18n_title': __('Configure this groupchat'),
|
|
|
|
'handler': ev => this.getAndRenderConfigurationForm(ev),
|
|
|
|
'a_class': 'configure-chatroom-button',
|
|
|
|
'icon_class': 'fa-wrench',
|
|
|
|
'name': 'configure'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.model.get('subject')) {
|
|
|
|
buttons.push({
|
2020-02-18 17:26:49 +01:00
|
|
|
'i18n_text': this.model.get('subject_hidden') ? __('Show topic') : __('Hide topic'),
|
|
|
|
'i18n_title': this.model.get('subject_hidden') ?
|
2020-02-10 11:23:55 +01:00
|
|
|
__('Show the topic message in the heading') :
|
|
|
|
__('Hide the topic in the heading'),
|
|
|
|
'handler': ev => this.toggleTopic(ev),
|
|
|
|
'a_class': '',
|
|
|
|
'icon_class': 'fa-minus-square',
|
|
|
|
'name': 'toggle-topic'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_converse.singleton) {
|
|
|
|
buttons.push({
|
|
|
|
'i18n_text': __('Leave'),
|
|
|
|
'i18n_title': __('Leave and close this groupchat'),
|
|
|
|
'handler': async ev => {
|
|
|
|
const messages = [__('Are you sure you want to leave this groupchat?')];
|
|
|
|
const result = await _converse.api.confirm(__('Confirm'), messages);
|
|
|
|
result && this.close(ev);
|
|
|
|
},
|
|
|
|
'a_class': 'close-chatbox-button',
|
|
|
|
'standalone': _converse.view_mode === 'overlayed',
|
|
|
|
'icon_class': 'fa-sign-out-alt',
|
|
|
|
'name': 'signout'
|
|
|
|
});
|
|
|
|
}
|
2020-02-07 14:58:26 +01:00
|
|
|
return buttons;
|
|
|
|
},
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
2020-02-10 11:23:55 +01:00
|
|
|
* Returns the groupchat heading TemplateResult to be rendered.
|
2019-11-28 17:01:08 +01:00
|
|
|
* @private
|
2020-02-07 14:58:26 +01:00
|
|
|
* @method _converse.ChatRoomView#generateHeadingTemplate
|
2019-11-28 17:01:08 +01:00
|
|
|
*/
|
2020-02-07 14:58:26 +01:00
|
|
|
generateHeadingTemplate () {
|
2020-02-10 11:23:55 +01:00
|
|
|
const heading_btns = this.getHeadingButtons();
|
|
|
|
const standalone_btns = heading_btns.filter(b => b.standalone);
|
|
|
|
const dropdown_btns = heading_btns.filter(b => !b.standalone);
|
2018-10-23 03:41:38 +02:00
|
|
|
return tpl_chatroom_head(
|
2019-04-29 09:07:15 +02:00
|
|
|
Object.assign(this.model.toJSON(), {
|
2020-02-07 14:58:26 +01:00
|
|
|
_converse,
|
2020-02-10 11:23:55 +01:00
|
|
|
'dropdown_btns': dropdown_btns.map(b => this.getHeadingDropdownItem(b)),
|
|
|
|
'standalone_btns': standalone_btns.map(b => this.getHeadingStandaloneButton(b)),
|
2019-05-29 12:57:54 +02:00
|
|
|
'title': this.model.getDisplayName(),
|
2018-10-23 03:41:38 +02:00
|
|
|
}));
|
|
|
|
},
|
|
|
|
|
2020-02-10 11:23:55 +01:00
|
|
|
toggleTopic () {
|
2020-02-18 17:26:49 +01:00
|
|
|
this.model.save('subject_hidden', !this.model.get('subject_hidden'));
|
2020-02-10 11:23:55 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
showInviteModal (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
if (this.muc_invite_modal === undefined) {
|
|
|
|
this.muc_invite_modal = new _converse.MUCInviteModal({'model': new Model()});
|
|
|
|
// TODO: remove once we have API for sending direct invite
|
|
|
|
this.muc_invite_modal.chatroomview = this;
|
|
|
|
}
|
|
|
|
this.muc_invite_modal.show(ev);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2019-12-03 10:55:46 +01:00
|
|
|
/**
|
|
|
|
* Callback method that gets called after the chat has become visible.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#afterShown
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
afterShown () {
|
2019-12-03 10:55:46 +01:00
|
|
|
// Override from converse-chatview, specifically to avoid
|
|
|
|
// the 'active' chat state from being sent out prematurely.
|
|
|
|
// This is instead done in `onConnectionStatusChanged` below.
|
2018-10-23 03:41:38 +02:00
|
|
|
if (u.isPersistableModel(this.model)) {
|
|
|
|
this.model.clearUnreadMsgCounter();
|
|
|
|
}
|
2019-06-19 11:04:09 +02:00
|
|
|
this.scrollDown();
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-05-20 10:06:37 +02:00
|
|
|
onConnectionStatusChanged () {
|
2019-12-10 15:40:33 +01:00
|
|
|
const conn_status = this.model.session.get('connection_status');
|
2019-05-20 10:06:37 +02:00
|
|
|
if (conn_status === converse.ROOMSTATUS.NICKNAME_REQUIRED) {
|
2019-11-28 17:01:08 +01:00
|
|
|
this.renderNicknameForm();
|
2019-06-11 12:16:27 +02:00
|
|
|
} else if (conn_status === converse.ROOMSTATUS.PASSWORD_REQUIRED) {
|
|
|
|
this.renderPasswordForm();
|
2019-05-20 10:06:37 +02:00
|
|
|
} else if (conn_status === converse.ROOMSTATUS.CONNECTING) {
|
2019-05-19 22:11:26 +02:00
|
|
|
this.showSpinner();
|
2019-05-20 10:06:37 +02:00
|
|
|
} else if (conn_status === converse.ROOMSTATUS.ENTERED) {
|
2019-11-29 10:38:17 +01:00
|
|
|
this.renderBottomPanel();
|
2018-10-23 03:41:38 +02:00
|
|
|
this.hideSpinner();
|
2020-01-09 14:28:43 +01:00
|
|
|
this.maybeFocus();
|
2019-06-11 12:16:27 +02:00
|
|
|
} else if (conn_status === converse.ROOMSTATUS.DISCONNECTED) {
|
|
|
|
this.showDisconnectMessage();
|
|
|
|
} else if (conn_status === converse.ROOMSTATUS.DESTROYED) {
|
|
|
|
this.showDestroyedMessage();
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
2018-08-10 14:09:21 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
getToolbarOptions () {
|
2019-04-29 09:07:15 +02:00
|
|
|
return Object.assign(
|
2018-10-23 03:41:38 +02:00
|
|
|
_converse.ChatBoxView.prototype.getToolbarOptions.apply(this, arguments),
|
|
|
|
{
|
|
|
|
'label_hide_occupants': __('Hide the list of participants'),
|
2019-07-10 09:47:13 +02:00
|
|
|
'show_occupants_toggle': _converse.visible_toolbar_buttons.toggle_occupants
|
2018-08-14 14:05:33 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
);
|
|
|
|
},
|
2018-08-10 14:09:21 +02:00
|
|
|
|
2019-05-15 14:47:04 +02:00
|
|
|
/**
|
|
|
|
* Closes this chat box, which implies leaving the groupchat as well.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#close
|
|
|
|
*/
|
2019-10-24 14:29:15 +02:00
|
|
|
async close () {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.hide();
|
2019-09-19 16:54:55 +02:00
|
|
|
if (_converse.router.history.getFragment() === "converse/room?jid="+this.model.get('jid')) {
|
2018-10-23 03:41:38 +02:00
|
|
|
_converse.router.navigate('');
|
|
|
|
}
|
2019-10-24 14:29:15 +02:00
|
|
|
await this.model.leave();
|
2019-10-11 20:29:12 +02:00
|
|
|
return _converse.ChatBoxView.prototype.close.apply(this, arguments);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-05-15 14:47:04 +02:00
|
|
|
updateOccupantsToggle () {
|
2018-10-23 03:41:38 +02:00
|
|
|
const icon_el = this.el.querySelector('.toggle-occupants');
|
2019-05-15 14:47:04 +02:00
|
|
|
const chat_area = this.el.querySelector('.chat-area');
|
2018-10-23 03:41:38 +02:00
|
|
|
if (this.model.get('hidden_occupants')) {
|
|
|
|
u.removeClass('fa-angle-double-right', icon_el);
|
|
|
|
u.addClass('fa-angle-double-left', icon_el);
|
2018-10-26 16:37:56 +02:00
|
|
|
u.addClass('full', chat_area);
|
2018-10-23 03:41:38 +02:00
|
|
|
} else {
|
|
|
|
u.addClass('fa-angle-double-right', icon_el);
|
|
|
|
u.removeClass('fa-angle-double-left', icon_el);
|
2018-10-26 16:37:56 +02:00
|
|
|
u.removeClass('full', chat_area);
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
2018-08-14 14:05:33 +02:00
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
2020-02-10 11:23:55 +01:00
|
|
|
* Hide the right sidebar containing the chat occupants.
|
2019-11-28 17:01:08 +01:00
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#hideOccupants
|
|
|
|
*/
|
2019-10-09 16:01:38 +02:00
|
|
|
hideOccupants (ev) {
|
2018-10-23 03:41:38 +02:00
|
|
|
if (ev) {
|
2018-06-04 19:53:33 +02:00
|
|
|
ev.preventDefault();
|
2018-10-23 03:41:38 +02:00
|
|
|
ev.stopPropagation();
|
|
|
|
}
|
|
|
|
this.model.save({'hidden_occupants': true});
|
2019-06-19 11:04:09 +02:00
|
|
|
this.scrollDown();
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
2020-02-10 11:23:55 +01:00
|
|
|
* Show or hide the right sidebar containing the chat occupants.
|
2019-11-28 17:01:08 +01:00
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#toggleOccupants
|
|
|
|
*/
|
2019-05-15 14:47:04 +02:00
|
|
|
toggleOccupants (ev) {
|
2018-10-23 03:41:38 +02:00
|
|
|
if (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
ev.stopPropagation();
|
|
|
|
}
|
2019-05-15 14:47:04 +02:00
|
|
|
this.model.save({'hidden_occupants': !this.model.get('hidden_occupants')});
|
2019-06-19 11:04:09 +02:00
|
|
|
this.scrollDown();
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-09-07 11:53:50 +02:00
|
|
|
|
2019-04-25 10:34:17 +02:00
|
|
|
verifyRoles (roles, occupant, show_error=true) {
|
2019-05-26 17:53:32 +02:00
|
|
|
if (!Array.isArray(roles)) {
|
|
|
|
throw new TypeError('roles must be an Array');
|
|
|
|
}
|
|
|
|
if (!roles.length) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-25 10:34:17 +02:00
|
|
|
if (!occupant) {
|
|
|
|
occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
|
|
|
|
}
|
2019-05-14 11:38:41 +02:00
|
|
|
const role = occupant.get('role');
|
2019-05-26 17:53:32 +02:00
|
|
|
if (roles.includes(role)) {
|
2019-05-14 11:38:41 +02:00
|
|
|
return true;
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2019-05-14 11:38:41 +02:00
|
|
|
if (show_error) {
|
|
|
|
this.showErrorMessage(__('Forbidden: you do not have the necessary role in order to do that.'))
|
|
|
|
}
|
|
|
|
return false;
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-04-25 10:34:17 +02:00
|
|
|
verifyAffiliations (affiliations, occupant, show_error=true) {
|
2019-05-26 17:53:32 +02:00
|
|
|
if (!Array.isArray(affiliations)) {
|
|
|
|
throw new TypeError('affiliations must be an Array');
|
|
|
|
}
|
|
|
|
if (!affiliations.length) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-25 10:34:17 +02:00
|
|
|
if (!occupant) {
|
|
|
|
occupant = this.model.occupants.findWhere({'jid': _converse.bare_jid});
|
|
|
|
}
|
2019-05-14 11:38:41 +02:00
|
|
|
const a = occupant.get('affiliation');
|
2019-05-26 17:53:32 +02:00
|
|
|
if (affiliations.includes(a)) {
|
2019-05-14 11:38:41 +02:00
|
|
|
return true;
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2019-05-14 11:38:41 +02:00
|
|
|
if (show_error) {
|
|
|
|
this.showErrorMessage(__('Forbidden: you do not have the necessary affiliation in order to do that.'))
|
|
|
|
}
|
|
|
|
return false;
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2019-05-26 17:53:32 +02:00
|
|
|
validateRoleOrAffiliationChangeArgs (command, args) {
|
|
|
|
if (!args) {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.showErrorMessage(
|
|
|
|
__('Error: the "%1$s" command takes two arguments, the user\'s nickname and optionally a reason.', command)
|
2018-02-21 22:29:21 +01:00
|
|
|
);
|
2018-10-23 03:41:38 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-05-26 17:53:32 +02:00
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
getNickOrJIDFromCommandArgs (args) {
|
2019-05-27 12:24:46 +02:00
|
|
|
if (!args.startsWith('@')) {
|
|
|
|
args = '@'+ args;
|
|
|
|
}
|
2019-10-09 16:01:38 +02:00
|
|
|
const [text, references] = this.model.parseTextForReferences(args); // eslint-disable-line no-unused-vars
|
2019-05-26 17:53:32 +02:00
|
|
|
if (!references.length) {
|
|
|
|
this.showErrorMessage(__("Error: couldn't find a groupchat participant based on your arguments"));
|
2019-06-18 12:36:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (references.length > 1) {
|
|
|
|
this.showErrorMessage(__("Error: found multiple groupchat participant based on your arguments"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const nick_or_jid = references.pop().value;
|
2019-07-04 08:13:47 +02:00
|
|
|
const reason = args.split(nick_or_jid, 2)[1];
|
|
|
|
if (reason && !reason.startsWith(' ')) {
|
2019-06-18 12:36:16 +02:00
|
|
|
this.showErrorMessage(__("Error: couldn't find a groupchat participant based on your arguments"));
|
|
|
|
return;
|
2019-05-26 17:53:32 +02:00
|
|
|
}
|
2019-06-18 12:36:16 +02:00
|
|
|
return nick_or_jid;
|
2019-05-26 17:53:32 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
setAffiliation (command, args, required_affiliations) {
|
|
|
|
const affiliation = COMMAND_TO_AFFILIATION[command];
|
|
|
|
if (!affiliation) {
|
|
|
|
throw Error(`ChatRoomView#setAffiliation called with invalid command: ${command}`);
|
|
|
|
}
|
|
|
|
if (!this.verifyAffiliations(required_affiliations)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!this.validateRoleOrAffiliationChangeArgs(command, args)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-06-18 12:36:16 +02:00
|
|
|
const nick_or_jid = this.getNickOrJIDFromCommandArgs(args);
|
2019-05-26 17:53:32 +02:00
|
|
|
if (!nick_or_jid) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-05-27 12:24:46 +02:00
|
|
|
const reason = args.split(nick_or_jid, 2)[1].trim();
|
2019-05-26 17:53:32 +02:00
|
|
|
// We're guaranteed to have an occupant due to getNickOrJIDFromCommandArgs
|
|
|
|
const occupant = this.model.getOccupant(nick_or_jid);
|
|
|
|
const attrs = {
|
|
|
|
'jid': occupant.get('jid'),
|
|
|
|
'reason': reason
|
|
|
|
}
|
|
|
|
if (_converse.auto_register_muc_nickname && occupant) {
|
|
|
|
attrs['nick'] = occupant.get('nick');
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
},
|
|
|
|
|
|
|
|
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.verifyAffiliations(required_affiliations) || !this.verifyRoles(required_roles)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!this.validateRoleOrAffiliationChangeArgs(command, args)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-06-18 12:36:16 +02:00
|
|
|
const nick_or_jid = this.getNickOrJIDFromCommandArgs(args);
|
2019-05-26 17:53:32 +02:00
|
|
|
if (!nick_or_jid) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-05-27 12:24:46 +02:00
|
|
|
const reason = args.split(nick_or_jid, 2)[1].trim();
|
2019-05-26 17:53:32 +02:00
|
|
|
// 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.onCommandError.bind(this));
|
2018-10-23 03:41:38 +02:00
|
|
|
return true;
|
|
|
|
},
|
2018-05-30 16:55:14 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
onCommandError (err) {
|
2019-11-06 11:01:34 +01:00
|
|
|
log.fatal(err);
|
2020-01-16 13:01:57 +01:00
|
|
|
this.showErrorMessage(
|
|
|
|
__("Sorry, an error happened while running the command.") + " " +
|
|
|
|
__("Check your browser's developer console for details.")
|
|
|
|
);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-08-09 13:07:32 +02:00
|
|
|
|
2019-07-04 14:12:12 +02:00
|
|
|
getAllowedCommands () {
|
2020-01-16 14:20:33 +01:00
|
|
|
let allowed_commands = ['clear', 'help', 'me', 'nick', 'register'];
|
2020-01-17 14:50:25 +01:00
|
|
|
if (this.model.config.get('changesubject') || ['owner', 'admin'].includes(this.model.getOwnAffiliation())) {
|
2020-01-16 14:20:33 +01:00
|
|
|
allowed_commands = [...allowed_commands, ...['subject', 'topic']];
|
|
|
|
}
|
2019-07-04 14:12:12 +02:00
|
|
|
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)) {
|
|
|
|
allowed_commands = allowed_commands.concat(ADMIN_COMMANDS);
|
|
|
|
}
|
|
|
|
if (this.verifyRoles(['moderator'], occupant, false)) {
|
|
|
|
allowed_commands = allowed_commands.concat(MODERATOR_COMMANDS).concat(VISITOR_COMMANDS);
|
|
|
|
} else if (!this.verifyRoles(['visitor', 'participant', 'moderator'], occupant, false)) {
|
|
|
|
allowed_commands = allowed_commands.concat(VISITOR_COMMANDS);
|
|
|
|
}
|
2020-01-16 14:20:33 +01:00
|
|
|
allowed_commands.sort();
|
2019-07-04 14:12:12 +02:00
|
|
|
return allowed_commands;
|
|
|
|
},
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
parseMessageForCommands (text) {
|
2019-05-14 11:38:41 +02:00
|
|
|
if (_converse.muc_disable_slash_commands && !Array.isArray(_converse.muc_disable_slash_commands)) {
|
2018-12-10 15:38:41 +01:00
|
|
|
return _converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments);
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2019-05-26 17:53:32 +02:00
|
|
|
text = text.replace(/^\s*/, "");
|
|
|
|
const command = (text.match(/^\/([a-zA-Z]*) ?/) || ['']).pop().toLowerCase();
|
|
|
|
if (!command) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-04 14:12:12 +02:00
|
|
|
const args = text.slice(('/'+command).length+1).trim();
|
2019-05-14 20:31:43 +02:00
|
|
|
|
|
|
|
let disabled_commands = [];
|
|
|
|
if (Array.isArray(_converse.muc_disable_slash_commands)) {
|
|
|
|
disabled_commands = _converse.muc_disable_slash_commands;
|
|
|
|
if (disabled_commands.includes(command)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-01-14 09:59:54 +01:00
|
|
|
}
|
2020-01-17 13:56:58 +01:00
|
|
|
const allowed_commands = this.getAllowedCommands();
|
|
|
|
if (!allowed_commands.includes(command)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-05-14 20:31:43 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
switch (command) {
|
2019-04-25 10:34:17 +02:00
|
|
|
case 'admin': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setAffiliation(command, args, ['owner']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
|
|
|
case 'ban': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setAffiliation(command, args, ['admin', 'owner']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
2019-07-04 14:12:12 +02:00
|
|
|
case 'modtools': {
|
|
|
|
this.showModeratorToolsModal(args);
|
|
|
|
break;
|
|
|
|
}
|
2019-04-25 10:34:17 +02:00
|
|
|
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.
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setRole(command, args, ['admin', 'owner']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
|
|
|
case 'destroy': {
|
2019-05-26 17:53:32 +02:00
|
|
|
if (!this.verifyAffiliations(['owner'])) {
|
2018-12-22 18:27:07 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-05-26 17:53:32 +02:00
|
|
|
this.model.sendDestroyIQ(args)
|
2018-12-22 18:27:07 +01:00
|
|
|
.then(() => this.close())
|
|
|
|
.catch(e => this.onCommandError(e));
|
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
|
|
|
case 'help': {
|
|
|
|
this.showHelpMessages([`<strong>${__("You can run the following commands")}</strong>`]);
|
|
|
|
this.showHelpMessages([
|
2018-10-23 03:41:38 +02:00
|
|
|
`<strong>/admin</strong>: ${__("Change user's affiliation to admin")}`,
|
2019-06-25 16:50:02 +02:00
|
|
|
`<strong>/ban</strong>: ${__('Ban user by changing their affiliation to outcast')}`,
|
2019-04-25 10:34:17 +02:00
|
|
|
`<strong>/clear</strong>: ${__('Clear the chat area')}`,
|
2019-11-15 09:41:01 +01:00
|
|
|
`<strong>/close</strong>: ${__('Close this groupchat')}`,
|
2018-10-23 03:41:38 +02:00
|
|
|
`<strong>/deop</strong>: ${__('Change user role to participant')}`,
|
2019-02-22 18:21:26 +01:00
|
|
|
`<strong>/destroy</strong>: ${__('Remove this groupchat')}`,
|
2018-10-23 03:41:38 +02:00
|
|
|
`<strong>/help</strong>: ${__('Show this menu')}`,
|
|
|
|
`<strong>/kick</strong>: ${__('Kick user from groupchat')}`,
|
|
|
|
`<strong>/me</strong>: ${__('Write in 3rd person')}`,
|
|
|
|
`<strong>/member</strong>: ${__('Grant membership to a user')}`,
|
2019-07-04 14:12:12 +02:00
|
|
|
`<strong>/modtools</strong>: ${__('Opens up the moderator tools GUI')}`,
|
2018-10-23 03:41:38 +02:00
|
|
|
`<strong>/mute</strong>: ${__("Remove user's ability to post messages")}`,
|
|
|
|
`<strong>/nick</strong>: ${__('Change your nickname')}`,
|
|
|
|
`<strong>/op</strong>: ${__('Grant moderator role to user')}`,
|
|
|
|
`<strong>/owner</strong>: ${__('Grant ownership of this groupchat')}`,
|
2019-04-25 10:34:17 +02:00
|
|
|
`<strong>/register</strong>: ${__("Register your nickname")}`,
|
2019-06-25 16:50:02 +02:00
|
|
|
`<strong>/revoke</strong>: ${__("Revoke the user's current affiliation")}`,
|
2018-10-23 03:41:38 +02:00
|
|
|
`<strong>/subject</strong>: ${__('Set groupchat subject')}`,
|
|
|
|
`<strong>/topic</strong>: ${__('Set groupchat subject (alias for /subject)')}`,
|
|
|
|
`<strong>/voice</strong>: ${__('Allow muted user to post messages')}`
|
2019-04-25 10:34:17 +02:00
|
|
|
].filter(line => disabled_commands.every(c => (!line.startsWith(c+'<', 9))))
|
|
|
|
.filter(line => allowed_commands.some(c => line.startsWith(c+'<', 9)))
|
|
|
|
);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
} case 'kick': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setRole(command, args, [], ['moderator']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
|
|
|
case 'mute': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setRole(command, args, [], ['moderator']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
case 'member': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setAffiliation(command, args, ['admin', 'owner']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
|
|
|
case 'nick': {
|
2018-10-23 03:41:38 +02:00
|
|
|
if (!this.verifyRoles(['visitor', 'participant', 'moderator'])) {
|
2018-02-21 22:29:21 +01:00
|
|
|
break;
|
2019-06-11 11:23:42 +02:00
|
|
|
} else if (args.length === 0) {
|
2019-06-11 12:16:27 +02:00
|
|
|
// e.g. Your nickname is "coolguy69"
|
|
|
|
this.showErrorMessage(__('Your nickname is "%1$s"', this.model.get('nick')))
|
2019-06-11 11:23:42 +02:00
|
|
|
} else {
|
|
|
|
const jid = Strophe.getBareJidFromJid(this.model.get('jid'));
|
|
|
|
_converse.api.send($pres({
|
|
|
|
from: _converse.connection.jid,
|
|
|
|
to: `${jid}/${args}`,
|
2019-10-11 15:32:38 +02:00
|
|
|
id: u.getUniqueId()
|
2019-06-11 11:23:42 +02:00
|
|
|
}).tree());
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
case 'owner':
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setAffiliation(command, args, ['owner']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
case 'op': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setRole(command, args, ['admin', 'owner']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
|
|
|
case 'register': {
|
2018-10-23 03:41:38 +02:00
|
|
|
if (args.length > 1) {
|
2018-10-27 23:12:02 +02:00
|
|
|
this.showErrorMessage(__('Error: invalid number of arguments'))
|
2018-10-23 03:41:38 +02:00
|
|
|
} else {
|
|
|
|
this.model.registerNickname().then(err_msg => {
|
|
|
|
if (err_msg) this.showErrorMessage(err_msg)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
|
|
|
case 'revoke': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setAffiliation(command, args, ['admin', 'owner']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
case 'topic':
|
|
|
|
case 'subject':
|
2019-05-26 17:53:32 +02:00
|
|
|
this.model.setSubject(args);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
case 'voice': {
|
2019-05-26 17:53:32 +02:00
|
|
|
this.setRole(command, args, [], ['moderator']);
|
2018-10-23 03:41:38 +02:00
|
|
|
break;
|
2019-04-25 10:34:17 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
default:
|
2018-12-10 15:38:41 +01:00
|
|
|
return _converse.ChatBoxView.prototype.parseMessageForCommands.apply(this, arguments);
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2019-03-29 23:47:56 +01:00
|
|
|
/**
|
|
|
|
* Renders a form given an IQ stanza containing the current
|
|
|
|
* groupchat configuration.
|
|
|
|
* Returns a promise which resolves once the user has
|
|
|
|
* either submitted the form, or canceled it.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#renderConfigurationForm
|
|
|
|
* @param { XMLElement } stanza: The IQ stanza containing the groupchat config.
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
renderConfigurationForm (stanza) {
|
2019-04-24 11:03:27 +02:00
|
|
|
this.hideChatRoomContents();
|
|
|
|
this.model.save('config_stanza', stanza.outerHTML);
|
|
|
|
if (!this.config_form) {
|
|
|
|
const { _converse } = this.__super__;
|
|
|
|
this.config_form = new _converse.MUCConfigForm({
|
|
|
|
'model': this.model,
|
|
|
|
'chatroomview': this
|
|
|
|
});
|
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
|
|
|
container_el.insertAdjacentElement('beforeend', this.config_form.el);
|
|
|
|
}
|
|
|
|
u.showElement(this.config_form.el);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
|
|
|
* Renders a form which allows the user to choose theirnickname.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#renderNicknameForm
|
|
|
|
*/
|
|
|
|
renderNicknameForm () {
|
|
|
|
const heading = _converse.muc_show_logs_before_join ?
|
|
|
|
__('Choose a nickname to enter') :
|
|
|
|
__('Please choose your nickname');
|
|
|
|
|
2019-12-03 15:02:34 +01:00
|
|
|
const html = tpl_chatroom_nickname_form(Object.assign({
|
2019-11-28 17:01:08 +01:00
|
|
|
heading,
|
|
|
|
'label_nickname': __('Nickname'),
|
|
|
|
'label_join': __('Enter groupchat'),
|
2019-12-03 15:02:34 +01:00
|
|
|
}, this.model.toJSON()));
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
if (_converse.muc_show_logs_before_join) {
|
|
|
|
const container = this.el.querySelector('.muc-bottom-panel');
|
|
|
|
container.innerHTML = html;
|
|
|
|
u.addClass('muc-bottom-panel--nickname', container);
|
|
|
|
} else {
|
|
|
|
this.hideChatRoomContents();
|
|
|
|
const container = this.el.querySelector('.chatroom-body');
|
|
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
|
|
}
|
2019-12-10 15:40:33 +01:00
|
|
|
u.safeSave(this.model.session, {'connection_status': converse.ROOMSTATUS.NICKNAME_REQUIRED});
|
2019-11-28 17:01:08 +01:00
|
|
|
},
|
|
|
|
|
2020-01-26 18:36:25 +01:00
|
|
|
/**
|
|
|
|
* Remove the configuration form without submitting and return to the chat view.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#closeForm
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
closeForm () {
|
2019-04-24 11:03:27 +02:00
|
|
|
sizzle('.chatroom-form-container', this.el).forEach(e => u.addClass('hidden', e));
|
2018-10-23 03:41:38 +02:00
|
|
|
this.renderAfterTransition();
|
|
|
|
},
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
|
|
|
* Start the process of configuring a groupchat, either by
|
|
|
|
* rendering a configuration form, or by auto-configuring
|
|
|
|
* based on the "roomconfig" data stored on the
|
|
|
|
* {@link _converse.ChatRoom}.
|
|
|
|
* Stores the new configuration on the {@link _converse.ChatRoom}
|
|
|
|
* once completed.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#getAndRenderConfigurationForm
|
|
|
|
* @param { Event } ev - DOM event that might be passed in if this
|
|
|
|
* method is called due to a user action. In this
|
|
|
|
* case, auto-configure won't happen, regardless of
|
|
|
|
* the settings.
|
|
|
|
*/
|
2019-10-09 16:01:38 +02:00
|
|
|
getAndRenderConfigurationForm () {
|
2019-04-24 13:12:00 +02:00
|
|
|
if (!this.config_form || !u.isVisible(this.config_form.el)) {
|
|
|
|
this.showSpinner();
|
|
|
|
this.model.fetchRoomConfiguration()
|
|
|
|
.then(iq => this.renderConfigurationForm(iq))
|
2019-11-06 11:01:34 +01:00
|
|
|
.catch(e => log.error(e));
|
2019-04-24 13:12:00 +02:00
|
|
|
} else {
|
|
|
|
this.closeForm();
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
hideChatRoomContents () {
|
2019-11-28 17:01:08 +01:00
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
|
|
|
if (container_el !== null) {
|
|
|
|
[].forEach.call(container_el.children, child => child.classList.add('hidden'));
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-06-11 12:16:27 +02:00
|
|
|
renderPasswordForm () {
|
2019-04-24 11:03:27 +02:00
|
|
|
this.hideChatRoomContents();
|
2019-06-11 12:16:27 +02:00
|
|
|
const message = this.model.get('password_validation_message');
|
|
|
|
this.model.save('password_validation_message', undefined);
|
|
|
|
|
2019-04-24 11:03:27 +02:00
|
|
|
if (!this.password_form) {
|
|
|
|
this.password_form = new _converse.MUCPasswordForm({
|
2019-09-19 16:54:55 +02:00
|
|
|
'model': new Model({
|
2019-06-16 12:14:29 +02:00
|
|
|
'validation_message': message
|
|
|
|
}),
|
2019-04-25 08:35:44 +02:00
|
|
|
'chatroomview': this,
|
2019-04-24 11:03:27 +02:00
|
|
|
});
|
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
|
|
|
container_el.insertAdjacentElement('beforeend', this.password_form.el);
|
|
|
|
} else {
|
2019-06-11 12:16:27 +02:00
|
|
|
this.password_form.model.set('validation_message', message);
|
2019-04-24 11:03:27 +02:00
|
|
|
}
|
|
|
|
u.showElement(this.password_form.el);
|
2019-12-10 15:40:33 +01:00
|
|
|
this.model.session.save('connection_status', converse.ROOMSTATUS.PASSWORD_REQUIRED);
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-06-11 12:16:27 +02:00
|
|
|
showDestroyedMessage () {
|
2018-10-23 03:41:38 +02:00
|
|
|
u.hideElement(this.el.querySelector('.chat-area'));
|
|
|
|
u.hideElement(this.el.querySelector('.occupants'));
|
2019-05-14 11:38:41 +02:00
|
|
|
sizzle('.spinner', this.el).forEach(u.removeElement);
|
|
|
|
|
2019-06-11 12:16:27 +02:00
|
|
|
const reason = this.model.get('destroyed_reason');
|
|
|
|
const moved_jid = this.model.get('moved_jid');
|
|
|
|
this.model.save({
|
|
|
|
'destroyed_reason': undefined,
|
|
|
|
'moved_jid': undefined
|
|
|
|
});
|
2018-10-23 03:41:38 +02:00
|
|
|
const container = this.el.querySelector('.disconnect-container');
|
|
|
|
container.innerHTML = tpl_chatroom_destroyed({
|
|
|
|
'__':__,
|
|
|
|
'jid': moved_jid,
|
|
|
|
'reason': reason ? `"${reason}"` : null
|
|
|
|
});
|
|
|
|
const switch_el = container.querySelector('a.switch-chat');
|
|
|
|
if (switch_el) {
|
2019-12-08 20:42:09 +01:00
|
|
|
switch_el.addEventListener('click', async ev => {
|
2018-10-23 03:41:38 +02:00
|
|
|
ev.preventDefault();
|
2019-12-08 20:42:09 +01:00
|
|
|
const room = await _converse.api.rooms.get(moved_jid, null, true);
|
|
|
|
room.maybeShow(true);
|
|
|
|
this.model.destroy();
|
2018-10-23 03:41:38 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
u.showElement(container);
|
|
|
|
},
|
2018-10-10 22:28:32 +02:00
|
|
|
|
2019-06-11 12:16:27 +02:00
|
|
|
showDisconnectMessage () {
|
|
|
|
const message = this.model.get('disconnection_message');
|
|
|
|
if (!message) {
|
|
|
|
return;
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
u.hideElement(this.el.querySelector('.chat-area'));
|
|
|
|
u.hideElement(this.el.querySelector('.occupants'));
|
2019-05-14 11:38:41 +02:00
|
|
|
sizzle('.spinner', this.el).forEach(u.removeElement);
|
2019-06-11 12:16:27 +02:00
|
|
|
|
|
|
|
const messages = [message];
|
|
|
|
const actor = this.model.get('disconnection_actor');
|
|
|
|
if (actor) {
|
|
|
|
messages.push(__('This action was done by %1$s.', actor));
|
|
|
|
}
|
|
|
|
const reason = this.model.get('disconnection_reason');
|
|
|
|
if (reason) {
|
|
|
|
messages.push(__('The reason given is: "%1$s".', reason));
|
|
|
|
}
|
|
|
|
this.model.save({
|
|
|
|
'disconnection_message': undefined,
|
|
|
|
'disconnection_reason': undefined,
|
|
|
|
'disconnection_actor': undefined
|
|
|
|
});
|
2018-10-23 03:41:38 +02:00
|
|
|
const container = this.el.querySelector('.disconnect-container');
|
2020-01-21 11:08:59 +01:00
|
|
|
container.innerHTML = tpl_chatroom_disconnect({messages})
|
2018-10-23 03:41:38 +02:00
|
|
|
u.showElement(container);
|
|
|
|
},
|
|
|
|
|
|
|
|
getNotificationWithMessage (message) {
|
|
|
|
let el = this.content.lastElementChild;
|
2019-08-05 01:39:57 +02:00
|
|
|
while (el) {
|
2020-01-21 11:08:59 +01:00
|
|
|
if (!u.hasClass(el, 'chat-info')) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return;
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
if (el.textContent === message) {
|
|
|
|
return el;
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
el = el.previousElementSibling;
|
|
|
|
}
|
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2019-12-18 11:57:31 +01:00
|
|
|
removeEmptyHistoryFeedback () {
|
2020-02-20 13:04:19 +01:00
|
|
|
const el = this.content.firstElementChild;
|
|
|
|
if (_converse.muc_show_logs_before_join && el && el.matches('.empty-history-feedback')) {
|
2019-11-28 17:01:08 +01:00
|
|
|
this.content.removeChild(this.content.firstElementChild);
|
|
|
|
}
|
2019-12-18 11:57:31 +01:00
|
|
|
},
|
|
|
|
|
2019-12-18 12:42:40 +01:00
|
|
|
insertDayIndicator () {
|
2019-12-18 11:57:31 +01:00
|
|
|
this.removeEmptyHistoryFeedback();
|
|
|
|
return _converse.ChatBoxView.prototype.insertDayIndicator.apply(this, arguments);
|
|
|
|
},
|
|
|
|
|
|
|
|
insertMessage (view) {
|
|
|
|
this.removeEmptyHistoryFeedback();
|
2019-11-28 17:01:08 +01:00
|
|
|
return _converse.ChatBoxView.prototype.insertMessage.call(this, view);
|
|
|
|
},
|
|
|
|
|
2019-05-14 13:54:19 +02:00
|
|
|
insertNotification (message) {
|
2019-12-18 11:57:31 +01:00
|
|
|
this.removeEmptyHistoryFeedback();
|
2019-05-14 13:54:19 +02:00
|
|
|
this.content.insertAdjacentHTML(
|
|
|
|
'beforeend',
|
|
|
|
tpl_info({
|
|
|
|
'isodate': (new Date()).toISOString(),
|
|
|
|
'extra_classes': 'chat-event',
|
|
|
|
'message': message
|
|
|
|
})
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
onOccupantAdded (occupant) {
|
2019-07-10 09:47:13 +02:00
|
|
|
if (occupant.get('jid') === _converse.bare_jid) {
|
|
|
|
this.renderHeading();
|
2019-07-12 13:50:39 +02:00
|
|
|
this.renderBottomPanel();
|
2019-07-10 09:47:13 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
if (occupant.get('show') === 'online') {
|
|
|
|
this.showJoinNotification(occupant);
|
|
|
|
}
|
|
|
|
},
|
2018-10-11 13:21:44 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
onOccupantRemoved (occupant) {
|
2019-12-10 15:40:33 +01:00
|
|
|
if (this.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED &&
|
2019-07-08 13:32:30 +02:00
|
|
|
occupant.get('show') === 'online') {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.showLeaveNotification(occupant);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
showJoinOrLeaveNotification (occupant) {
|
2020-01-21 11:08:59 +01:00
|
|
|
if (occupant.get('states').includes('303')) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (occupant.get('show') === 'offline') {
|
|
|
|
this.showLeaveNotification(occupant);
|
|
|
|
} else if (occupant.get('show') === 'online') {
|
|
|
|
this.showJoinNotification(occupant);
|
|
|
|
}
|
|
|
|
},
|
2018-10-11 14:04:47 +02:00
|
|
|
|
2019-06-17 21:42:55 +02:00
|
|
|
/**
|
2019-06-26 17:52:45 +02:00
|
|
|
* Working backwards, get today's most recent join/leave notification
|
|
|
|
* from the same user (if any exists) after the most recent chat message.
|
2019-06-17 21:42:55 +02:00
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#getPreviousJoinOrLeaveNotification
|
|
|
|
* @param {HTMLElement} el
|
|
|
|
* @param {string} nick
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
getPreviousJoinOrLeaveNotification (el, nick) {
|
2019-06-26 17:52:45 +02:00
|
|
|
const today = (new Date()).toISOString().split('T')[0];
|
|
|
|
while (el !== null) {
|
|
|
|
if (!el.classList.contains('chat-info')) {
|
2018-07-31 13:34:04 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-06-26 17:52:45 +02:00
|
|
|
// Check whether el is still from today.
|
|
|
|
// We don't use `Dayjs.same` here, since it's about 4 times slower.
|
|
|
|
const date = el.getAttribute('data-isodate');
|
|
|
|
if (date && date.split('T')[0] !== today) {
|
|
|
|
return;
|
2018-10-13 13:23:55 +02:00
|
|
|
}
|
2020-01-21 11:08:59 +01:00
|
|
|
const data = get(el, 'dataset', {});
|
2018-10-23 03:41:38 +02:00
|
|
|
if (data.join === nick ||
|
|
|
|
data.leave === nick ||
|
|
|
|
data.leavejoin === nick ||
|
|
|
|
data.joinleave === nick) {
|
|
|
|
return el;
|
2018-04-08 19:44:53 +02:00
|
|
|
}
|
2019-06-19 11:04:09 +02:00
|
|
|
el = el.previousElementSibling;
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
showJoinNotification (occupant) {
|
2019-01-22 01:08:12 +01:00
|
|
|
if (!_converse.muc_show_join_leave ||
|
2019-12-10 15:40:33 +01:00
|
|
|
this.model.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const nick = occupant.get('nick'),
|
2019-05-18 06:33:42 +02:00
|
|
|
stat = _converse.muc_show_join_leave_status ? occupant.get('status') : null,
|
2019-06-19 11:04:09 +02:00
|
|
|
prev_info_el = this.getPreviousJoinOrLeaveNotification(this.content.lastElementChild, nick),
|
2020-01-21 11:08:59 +01:00
|
|
|
data = get(prev_info_el, 'dataset', {});
|
2018-10-23 03:41:38 +02:00
|
|
|
|
|
|
|
if (data.leave === nick) {
|
|
|
|
let message;
|
2019-08-05 01:39:57 +02:00
|
|
|
if (stat) {
|
2018-10-23 03:41:38 +02:00
|
|
|
message = __('%1$s has left and re-entered the groupchat. "%2$s"', nick, stat);
|
2019-08-05 01:39:57 +02:00
|
|
|
} else {
|
|
|
|
message = __('%1$s has left and re-entered the groupchat', nick);
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
const data = {
|
|
|
|
'data_name': 'leavejoin',
|
|
|
|
'data_value': nick,
|
2019-05-05 19:05:45 +02:00
|
|
|
'isodate': (new Date()).toISOString(),
|
2018-10-23 03:41:38 +02:00
|
|
|
'extra_classes': 'chat-event',
|
|
|
|
'message': message
|
|
|
|
};
|
|
|
|
this.content.removeChild(prev_info_el);
|
2019-06-19 11:04:09 +02:00
|
|
|
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
|
|
|
|
const el = this.content.lastElementChild;
|
2018-10-23 03:41:38 +02:00
|
|
|
setTimeout(() => u.addClass('fade-out', el), 5000);
|
|
|
|
setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5500);
|
|
|
|
} else {
|
|
|
|
let message;
|
2019-08-05 01:39:57 +02:00
|
|
|
if (stat) {
|
2018-10-23 03:41:38 +02:00
|
|
|
message = __('%1$s has entered the groupchat. "%2$s"', nick, stat);
|
2019-08-05 01:39:57 +02:00
|
|
|
} else {
|
|
|
|
message = __('%1$s has entered the groupchat', nick);
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
const data = {
|
|
|
|
'data_name': 'join',
|
|
|
|
'data_value': nick,
|
2019-05-05 19:05:45 +02:00
|
|
|
'isodate': (new Date()).toISOString(),
|
2018-10-23 03:41:38 +02:00
|
|
|
'extra_classes': 'chat-event',
|
|
|
|
'message': message
|
|
|
|
};
|
|
|
|
if (prev_info_el) {
|
2018-10-13 13:23:55 +02:00
|
|
|
this.content.removeChild(prev_info_el);
|
2019-06-19 11:04:09 +02:00
|
|
|
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
|
2018-02-21 22:29:21 +01:00
|
|
|
} else {
|
2019-06-19 11:04:09 +02:00
|
|
|
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
|
|
|
|
this.insertDayIndicator(this.content.lastElementChild);
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2019-06-19 11:04:09 +02:00
|
|
|
this.scrollDown();
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
showLeaveNotification (occupant) {
|
2019-01-22 01:08:12 +01:00
|
|
|
if (!_converse.muc_show_join_leave ||
|
2020-01-21 11:08:59 +01:00
|
|
|
occupant.get('states').includes('303') ||
|
|
|
|
occupant.get('states').includes('307')) {
|
2018-10-23 03:41:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const nick = occupant.get('nick'),
|
2019-05-18 06:33:42 +02:00
|
|
|
stat = _converse.muc_show_join_leave_status ? occupant.get('status') : null,
|
2019-06-19 11:04:09 +02:00
|
|
|
prev_info_el = this.getPreviousJoinOrLeaveNotification(this.content.lastElementChild, nick),
|
2020-01-21 11:08:59 +01:00
|
|
|
dataset = get(prev_info_el, 'dataset', {});
|
2018-10-23 03:41:38 +02:00
|
|
|
|
|
|
|
if (dataset.join === nick) {
|
|
|
|
let message;
|
2019-08-05 01:39:57 +02:00
|
|
|
if (stat) {
|
2018-10-23 03:41:38 +02:00
|
|
|
message = __('%1$s has entered and left the groupchat. "%2$s"', nick, stat);
|
2019-08-05 01:39:57 +02:00
|
|
|
} else {
|
|
|
|
message = __('%1$s has entered and left the groupchat', nick);
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
const data = {
|
|
|
|
'data_name': 'joinleave',
|
|
|
|
'data_value': nick,
|
2019-05-05 19:05:45 +02:00
|
|
|
'isodate': (new Date()).toISOString(),
|
2018-10-23 03:41:38 +02:00
|
|
|
'extra_classes': 'chat-event',
|
|
|
|
'message': message
|
|
|
|
};
|
|
|
|
this.content.removeChild(prev_info_el);
|
2019-06-19 11:04:09 +02:00
|
|
|
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
|
|
|
|
const el = this.content.lastElementChild;
|
2018-10-23 03:41:38 +02:00
|
|
|
setTimeout(() => u.addClass('fade-out', el), 5000);
|
|
|
|
setTimeout(() => el.parentElement && el.parentElement.removeChild(el), 5500);
|
|
|
|
} else {
|
|
|
|
let message;
|
2019-08-05 01:39:57 +02:00
|
|
|
if (stat) {
|
2018-10-23 03:41:38 +02:00
|
|
|
message = __('%1$s has left the groupchat. "%2$s"', nick, stat);
|
2019-08-05 01:39:57 +02:00
|
|
|
} else {
|
|
|
|
message = __('%1$s has left the groupchat', nick);
|
2018-09-07 11:53:50 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
const data = {
|
|
|
|
'message': message,
|
2019-05-05 19:05:45 +02:00
|
|
|
'isodate': (new Date()).toISOString(),
|
2018-10-23 03:41:38 +02:00
|
|
|
'extra_classes': 'chat-event',
|
|
|
|
'data_name': 'leave',
|
|
|
|
'data_value': nick
|
|
|
|
}
|
|
|
|
if (prev_info_el) {
|
2018-10-13 13:23:55 +02:00
|
|
|
this.content.removeChild(prev_info_el);
|
2019-06-19 11:04:09 +02:00
|
|
|
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
|
2018-02-21 22:29:21 +01:00
|
|
|
} else {
|
2019-06-19 11:04:09 +02:00
|
|
|
this.content.insertAdjacentHTML('beforeend', tpl_info(data));
|
|
|
|
this.insertDayIndicator(this.content.lastElementChild);
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2019-06-19 11:04:09 +02:00
|
|
|
this.scrollDown();
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
|
|
|
* Rerender the groupchat after some kind of transition. For
|
|
|
|
* example after the spinner has been removed or after a
|
|
|
|
* form has been submitted and removed.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#renderAfterTransition
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
renderAfterTransition () {
|
2019-12-10 15:40:33 +01:00
|
|
|
const conn_status = this.model.session.get('connection_status')
|
|
|
|
if (conn_status == converse.ROOMSTATUS.NICKNAME_REQUIRED) {
|
2019-11-28 17:01:08 +01:00
|
|
|
this.renderNicknameForm();
|
2019-12-10 15:40:33 +01:00
|
|
|
} else if (conn_status == converse.ROOMSTATUS.PASSWORD_REQUIRED) {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.renderPasswordForm();
|
2019-12-10 15:40:33 +01:00
|
|
|
} else if (conn_status == converse.ROOMSTATUS.ENTERED) {
|
2019-07-10 09:47:13 +02:00
|
|
|
this.hideChatRoomContents();
|
2019-05-15 14:47:04 +02:00
|
|
|
u.showElement(this.el.querySelector('.chat-area'));
|
|
|
|
u.showElement(this.el.querySelector('.occupants'));
|
2019-06-19 11:04:09 +02:00
|
|
|
this.scrollDown();
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
showSpinner () {
|
2019-05-14 11:38:41 +02:00
|
|
|
sizzle('.spinner', this.el).forEach(u.removeElement);
|
2019-04-24 11:03:27 +02:00
|
|
|
this.hideChatRoomContents();
|
2018-10-23 03:41:38 +02:00
|
|
|
const container_el = this.el.querySelector('.chatroom-body');
|
|
|
|
container_el.insertAdjacentHTML('afterbegin', tpl_spinner());
|
|
|
|
},
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
|
|
|
* Check if the spinner is being shown and if so, hide it.
|
|
|
|
* Also make sure then that the chat area and occupants
|
|
|
|
* list are both visible.
|
|
|
|
* @private
|
|
|
|
* @method _converse.ChatRoomView#hideSpinner
|
|
|
|
*/
|
2018-10-23 03:41:38 +02:00
|
|
|
hideSpinner () {
|
|
|
|
const spinner = this.el.querySelector('.spinner');
|
2019-08-05 01:39:57 +02:00
|
|
|
if (spinner !== null) {
|
2018-10-23 03:41:38 +02:00
|
|
|
u.removeElement(spinner);
|
|
|
|
this.renderAfterTransition();
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setChatRoomSubject () {
|
2020-01-17 12:16:56 +01:00
|
|
|
const subject = this.model.get('subject');
|
2020-01-17 05:36:07 +01:00
|
|
|
if (!subject.text && !subject.author) {
|
|
|
|
return; // Probably a new MUC
|
|
|
|
}
|
2020-01-17 12:16:56 +01:00
|
|
|
const author = subject.author;
|
|
|
|
// For translators: the %1$s part will get
|
|
|
|
// replaced by the user's name.
|
|
|
|
// Example: Topic set by JC Brand
|
|
|
|
const message = subject.text ? __('Topic set by %1$s', author) : __('Topic cleared by %1$s', author);
|
2018-10-23 03:41:38 +02:00
|
|
|
this.content.insertAdjacentHTML(
|
|
|
|
'beforeend',
|
|
|
|
tpl_info({
|
2020-02-10 11:23:55 +01:00
|
|
|
'isodate': (new Date()).toISOString(),
|
2018-10-23 03:41:38 +02:00
|
|
|
'extra_classes': 'chat-event',
|
|
|
|
'message': message
|
|
|
|
}));
|
2019-06-19 11:04:09 +02:00
|
|
|
this.scrollDown();
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
});
|
2018-02-21 22:29:21 +01:00
|
|
|
|
|
|
|
|
2019-11-28 17:01:08 +01:00
|
|
|
/**
|
2019-09-19 16:54:55 +02:00
|
|
|
* View which renders MUC section of the control box.
|
2019-11-28 17:01:08 +01:00
|
|
|
* @class
|
|
|
|
* @namespace _converse.RoomsPanel
|
|
|
|
* @memberOf _converse
|
|
|
|
*/
|
2019-09-19 16:54:55 +02:00
|
|
|
_converse.RoomsPanel = View.extend({
|
2018-10-23 03:41:38 +02:00
|
|
|
tagName: 'div',
|
|
|
|
className: 'controlbox-section',
|
|
|
|
id: 'chatrooms',
|
|
|
|
events: {
|
2018-11-07 11:09:41 +01:00
|
|
|
'click a.controlbox-heading__btn.show-add-muc-modal': 'showAddRoomModal',
|
|
|
|
'click a.controlbox-heading__btn.show-list-muc-modal': 'showListRoomsModal'
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
render () {
|
|
|
|
this.el.innerHTML = tpl_room_panel({
|
|
|
|
'heading_chatrooms': __('Groupchats'),
|
|
|
|
'title_new_room': __('Add a new groupchat'),
|
|
|
|
'title_list_rooms': __('Query for groupchats')
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
showAddRoomModal (ev) {
|
2019-07-29 10:19:05 +02:00
|
|
|
if (this.add_room_modal === undefined) {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.add_room_modal = new _converse.AddChatRoomModal({'model': this.model});
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
this.add_room_modal.show(ev);
|
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
showListRoomsModal(ev) {
|
2019-07-29 10:19:05 +02:00
|
|
|
if (this.list_rooms_modal === undefined) {
|
2018-10-23 03:41:38 +02:00
|
|
|
this.list_rooms_modal = new _converse.ListChatRoomsModal({'model': this.model});
|
|
|
|
}
|
|
|
|
this.list_rooms_modal.show(ev);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2020-01-30 12:57:11 +01:00
|
|
|
_converse.MUCConfigForm = View.extend({
|
2020-01-26 18:36:25 +01:00
|
|
|
className: 'chatroom-form-container muc-config-form',
|
2019-04-24 11:03:27 +02:00
|
|
|
|
|
|
|
initialize (attrs) {
|
|
|
|
this.chatroomview = attrs.chatroomview;
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.chatroomview.model.features, 'change:passwordprotected', this.render);
|
|
|
|
this.listenTo(this.chatroomview.model.features, 'change:config_stanza', this.render);
|
2019-04-24 11:03:27 +02:00
|
|
|
this.render();
|
|
|
|
},
|
|
|
|
|
|
|
|
toHTML () {
|
|
|
|
const stanza = u.toStanza(this.model.get('config_stanza'));
|
|
|
|
const whitelist = _converse.roomconfig_whitelist;
|
|
|
|
let fields = sizzle('field', stanza);
|
|
|
|
if (whitelist.length) {
|
2020-01-21 11:08:59 +01:00
|
|
|
fields = fields.filter(f => whitelist.includes(f.getAttribute('var')));
|
2019-04-24 11:03:27 +02:00
|
|
|
}
|
|
|
|
const password_protected = this.model.features.get('passwordprotected');
|
|
|
|
const options = {
|
|
|
|
'new_password': !password_protected,
|
|
|
|
'fixed_username': this.model.get('jid')
|
|
|
|
};
|
2020-01-26 18:36:25 +01:00
|
|
|
return tpl_muc_config_form({
|
|
|
|
'closeConfigForm': ev => this.closeConfigForm(ev),
|
|
|
|
'fields': fields.map(f => u.xForm2webForm(f, stanza, options)),
|
2020-01-21 11:08:59 +01:00
|
|
|
'instructions': get(stanza.querySelector('instructions'), 'textContent'),
|
2020-01-26 18:36:25 +01:00
|
|
|
'submitConfigForm': ev => this.submitConfigForm(ev),
|
|
|
|
'title': get(stanza.querySelector('title'), 'textContent')
|
2019-04-24 11:03:27 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2020-01-16 13:01:57 +01:00
|
|
|
async submitConfigForm (ev) {
|
2019-04-24 11:03:27 +02:00
|
|
|
ev.preventDefault();
|
2020-01-16 13:01:57 +01:00
|
|
|
const inputs = sizzle(':input:not([type=button]):not([type=submit])', ev.target);
|
|
|
|
const configArray = inputs.map(u.webForm2xForm);
|
|
|
|
try {
|
|
|
|
await this.model.sendConfiguration(configArray);
|
|
|
|
} catch (e) {
|
|
|
|
log.error(e);
|
|
|
|
this.showErrorMessage(
|
|
|
|
__("Sorry, an error occurred while trying to submit the config form.") + " " +
|
|
|
|
__("Check your browser's developer console for details.")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
await this.model.refreshDiscoInfo();
|
2019-04-24 11:03:27 +02:00
|
|
|
this.chatroomview.closeForm();
|
|
|
|
},
|
|
|
|
|
|
|
|
closeConfigForm (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
this.chatroomview.closeForm();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2020-01-30 12:57:11 +01:00
|
|
|
_converse.MUCPasswordForm = View.extend({
|
2020-01-26 18:36:25 +01:00
|
|
|
className: 'chatroom-form-container muc-password-form',
|
2019-04-24 11:03:27 +02:00
|
|
|
|
|
|
|
initialize (attrs) {
|
|
|
|
this.chatroomview = attrs.chatroomview;
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.model, 'change:validation_message', this.render);
|
2019-04-24 11:03:27 +02:00
|
|
|
this.render();
|
|
|
|
},
|
|
|
|
|
|
|
|
toHTML () {
|
2020-01-26 18:36:25 +01:00
|
|
|
return tpl_muc_password_form({
|
2019-04-24 11:03:27 +02:00
|
|
|
'jid': this.model.get('jid'),
|
2020-01-26 18:36:25 +01:00
|
|
|
'submitPassword': ev => this.submitPassword(ev),
|
|
|
|
'validation_message': this.model.get('validation_message')
|
2019-04-24 11:03:27 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
submitPassword (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
const password = this.el.querySelector('input[type=password]').value;
|
2019-05-20 10:06:37 +02:00
|
|
|
this.chatroomview.model.join(this.chatroomview.model.get('nick'), password);
|
2019-04-25 08:35:44 +02:00
|
|
|
this.model.set('validation_message', null);
|
2019-04-24 11:03:27 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2020-01-27 13:20:23 +01:00
|
|
|
_converse.MUCInviteModal = _converse.BootstrapModal.extend({
|
|
|
|
id: "muc-invite-modal",
|
|
|
|
|
|
|
|
initialize () {
|
|
|
|
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
|
|
|
this.listenTo(this.model, 'change', this.render);
|
|
|
|
this.initInviteWidget();
|
|
|
|
},
|
|
|
|
|
|
|
|
toHTML () {
|
|
|
|
return tpl_muc_invite_modal(Object.assign(
|
|
|
|
this.model.toJSON(), {
|
|
|
|
'submitInviteForm': ev => this.submitInviteForm(ev)
|
|
|
|
})
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
initInviteWidget () {
|
|
|
|
if (this.invite_auto_complete) {
|
|
|
|
this.invite_auto_complete.destroy();
|
|
|
|
}
|
|
|
|
const list = _converse.roster.map(i => ({'label': i.getDisplayName(), 'value': i.get('jid')}));
|
|
|
|
const el = this.el.querySelector('.suggestion-box').parentElement;
|
|
|
|
this.invite_auto_complete = new _converse.AutoComplete(el, {
|
|
|
|
'min_chars': 1,
|
|
|
|
'list': list
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
submitInviteForm (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
// TODO: Add support for sending an invite to multiple JIDs
|
|
|
|
const data = new FormData(ev.target);
|
|
|
|
const jid = data.get('invitee_jids');
|
|
|
|
const reason = data.get('reason');
|
|
|
|
if (u.isValidJID(jid)) {
|
|
|
|
// TODO: Create and use API here
|
|
|
|
this.chatroomview.model.directInvite(jid, reason);
|
|
|
|
this.modal.hide();
|
|
|
|
} else {
|
|
|
|
this.model.set({'invalid_invite_jid': true});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2020-01-30 12:57:11 +01:00
|
|
|
_converse.MUCSidebar = View.extend({
|
2018-10-23 03:41:38 +02:00
|
|
|
tagName: 'div',
|
|
|
|
className: 'occupants col-md-3 col-4',
|
|
|
|
|
2019-05-14 20:38:25 +02:00
|
|
|
async initialize () {
|
2020-01-26 18:36:25 +01:00
|
|
|
this.chatroomview = this.model.chatroomview;
|
|
|
|
this.listenTo(this.model, 'add', this.render);
|
|
|
|
this.listenTo(this.model, 'remove', this.render);
|
|
|
|
this.listenTo(this.model, 'change', this.render);
|
|
|
|
this.listenTo(this.chatroomview.model.features, 'change', this.render);
|
2019-09-06 14:34:59 +02:00
|
|
|
this.listenTo(this.chatroomview.model, 'change:hidden_occupants', this.setVisibility);
|
2018-10-23 03:41:38 +02:00
|
|
|
this.render();
|
2019-05-14 20:38:25 +02:00
|
|
|
await this.model.fetched;
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2020-01-26 18:36:25 +01:00
|
|
|
toHTML () {
|
|
|
|
return tpl_muc_sidebar(
|
2019-04-29 09:07:15 +02:00
|
|
|
Object.assign(this.chatroomview.model.toJSON(), {
|
2020-01-30 21:59:51 +01:00
|
|
|
_converse,
|
2020-01-26 18:36:25 +01:00
|
|
|
'features': this.chatroomview.model.features,
|
2020-02-10 11:23:55 +01:00
|
|
|
'occupants': this.model.models
|
2018-10-23 03:41:38 +02:00
|
|
|
})
|
|
|
|
);
|
2020-01-26 18:36:25 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
afterRender () {
|
2019-05-15 14:47:04 +02:00
|
|
|
this.setVisibility();
|
2018-10-23 03:41:38 +02:00
|
|
|
},
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2019-05-15 14:47:04 +02:00
|
|
|
setVisibility () {
|
|
|
|
if (this.chatroomview.model.get('hidden_occupants')) {
|
|
|
|
u.hideElement(this.el);
|
|
|
|
} else {
|
|
|
|
u.showElement(this.el);
|
|
|
|
this.setOccupantsHeight();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
setOccupantsHeight () {
|
2020-01-27 13:20:23 +01:00
|
|
|
// TODO: remove the features section in sidebar and then this as well
|
2018-10-23 03:41:38 +02:00
|
|
|
const el = this.el.querySelector('.chatroom-features');
|
2020-01-26 18:36:25 +01:00
|
|
|
if (el) {
|
|
|
|
this.el.querySelector('.occupant-list').style.cssText =
|
|
|
|
`height: calc(100% - ${el.offsetHeight}px - 5em);`;
|
|
|
|
}
|
2020-01-27 13:20:23 +01:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
});
|
2018-02-21 22:29:21 +01:00
|
|
|
|
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
function setMUCDomain (domain, controlboxview) {
|
2019-09-11 11:28:28 +02:00
|
|
|
controlboxview.getRoomsPanel().model.save('muc_domain', Strophe.getDomainFromJid(domain));
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
_converse.api.waitUntil('discoInitialized').then(() => {
|
|
|
|
_converse.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 })));
|
2019-11-06 11:01:34 +01:00
|
|
|
}).catch(e => log.error(e));
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
function fetchAndSetMUCDomain (controlboxview) {
|
|
|
|
if (controlboxview.model.get('connected')) {
|
2019-09-11 11:28:28 +02:00
|
|
|
if (!controlboxview.getRoomsPanel().model.get('muc_domain')) {
|
2019-07-29 10:19:05 +02:00
|
|
|
if (_converse.muc_domain === undefined) {
|
2018-10-23 03:41:38 +02:00
|
|
|
setMUCDomainFromDisco(controlboxview);
|
|
|
|
} else {
|
|
|
|
setMUCDomain(_converse.muc_domain, controlboxview);
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
2018-02-21 22:29:21 +01:00
|
|
|
|
2019-06-05 14:43:59 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
/************************ BEGIN Event Handlers ************************/
|
2019-03-29 14:15:03 +01:00
|
|
|
_converse.api.listen.on('chatBoxViewsInitialized', () => {
|
2018-10-13 20:55:05 +02:00
|
|
|
|
2018-10-23 03:41:38 +02:00
|
|
|
function openChatRoomFromURIClicked (ev) {
|
|
|
|
ev.preventDefault();
|
|
|
|
_converse.api.rooms.open(ev.target.href);
|
|
|
|
}
|
|
|
|
_converse.chatboxviews.delegate('click', 'a.open-chatroom', openChatRoomFromURIClicked);
|
2018-08-28 14:58:33 +02:00
|
|
|
|
2019-11-08 09:49:58 +01:00
|
|
|
async function addView (model) {
|
2019-06-05 14:43:59 +02:00
|
|
|
const views = _converse.chatboxviews;
|
|
|
|
if (!views.get(model.get('id')) &&
|
|
|
|
model.get('type') === _converse.CHATROOMS_TYPE &&
|
|
|
|
model.isValid()
|
|
|
|
) {
|
2019-11-08 09:49:58 +01:00
|
|
|
await model.initialized;
|
|
|
|
return views.add(model.get('id'), new _converse.ChatRoomView({model}));
|
2018-02-21 22:29:21 +01:00
|
|
|
}
|
2019-06-05 14:43:59 +02:00
|
|
|
}
|
|
|
|
_converse.chatboxes.on('add', addView);
|
2018-10-23 03:41:38 +02:00
|
|
|
});
|
2018-05-02 15:29:06 +02:00
|
|
|
|
2019-02-20 22:58:59 +01:00
|
|
|
_converse.api.listen.on('clearSession', () => {
|
|
|
|
const view = _converse.chatboxviews.get('controlbox');
|
|
|
|
if (view && view.roomspanel) {
|
2019-08-01 10:26:35 +02:00
|
|
|
view.roomspanel.model.destroy();
|
2019-02-20 22:58:59 +01:00
|
|
|
view.roomspanel.remove();
|
|
|
|
delete view.roomspanel;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-08-05 11:10:32 +02:00
|
|
|
_converse.api.listen.on('controlBoxInitialized', (view) => {
|
2018-10-23 03:41:38 +02:00
|
|
|
if (!_converse.allow_muc) {
|
|
|
|
return;
|
2018-05-02 15:29:06 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
fetchAndSetMUCDomain(view);
|
2019-09-07 22:00:28 +02:00
|
|
|
view.model.on('change:connected', () => fetchAndSetMUCDomain(view));
|
2018-10-23 03:41:38 +02:00
|
|
|
});
|
|
|
|
/************************ END Event Handlers ************************/
|
|
|
|
|
|
|
|
|
|
|
|
/************************ BEGIN API ************************/
|
2019-04-29 09:07:15 +02:00
|
|
|
Object.assign(_converse.api, {
|
2018-10-23 03:41:38 +02:00
|
|
|
/**
|
|
|
|
* The "roomviews" namespace groups methods relevant to chatroom
|
|
|
|
* (aka groupchats) views.
|
|
|
|
*
|
|
|
|
* @namespace _converse.api.roomviews
|
|
|
|
* @memberOf _converse.api
|
|
|
|
*/
|
2019-09-24 15:33:41 +02:00
|
|
|
roomviews: {
|
2018-11-21 14:57:49 +01:00
|
|
|
/**
|
|
|
|
* 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']
|
2020-01-26 18:36:25 +01:00
|
|
|
* @returns {View} View representing the groupchat
|
2018-11-21 14:57:49 +01:00
|
|
|
*
|
|
|
|
* @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) {
|
2019-05-14 11:38:41 +02:00
|
|
|
if (Array.isArray(jids)) {
|
2018-11-21 14:57:49 +01:00
|
|
|
const views = _converse.api.chatviews.get(jids);
|
|
|
|
return views.filter(v => v.model.get('type') === _converse.CHATROOMS_TYPE)
|
|
|
|
} else {
|
|
|
|
const view = _converse.api.chatviews.get(jids);
|
|
|
|
if (view.model.get('type') === _converse.CHATROOMS_TYPE) {
|
|
|
|
return view;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2018-09-02 15:07:14 +02:00
|
|
|
/**
|
2018-10-23 03:41:38 +02:00
|
|
|
* 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.
|
2018-09-02 15:07:14 +02:00
|
|
|
*
|
2018-10-23 03:41:38 +02:00
|
|
|
* @method _converse.api.roomviews.close
|
|
|
|
* @param {(String[]|String)} jids The JID or array of JIDs of the chatroom(s)
|
2019-10-24 14:29:15 +02:00
|
|
|
* @returns { Promise } - Promise which resolves once the views have been closed.
|
2018-09-02 15:07:14 +02:00
|
|
|
*/
|
2019-10-11 20:29:12 +02:00
|
|
|
close (jids) {
|
2019-05-14 11:38:41 +02:00
|
|
|
let views;
|
2019-07-29 10:19:05 +02:00
|
|
|
if (jids === undefined) {
|
2019-05-14 11:38:41 +02:00
|
|
|
views = _converse.chatboxviews;
|
2020-01-21 11:08:59 +01:00
|
|
|
} else if (isString(jids)) {
|
2019-05-14 11:38:41 +02:00
|
|
|
views = [_converse.chatboxviews.get(jids)].filter(v => v);
|
|
|
|
} else if (Array.isArray(jids)) {
|
|
|
|
views = jids.map(jid => _converse.chatboxviews.get(jid));
|
2018-09-02 15:07:14 +02:00
|
|
|
}
|
2019-10-24 14:29:15 +02:00
|
|
|
return Promise.all(views.map(v => (v.is_chatroom && v.model && v.close())))
|
2018-09-02 15:07:14 +02:00
|
|
|
}
|
2018-10-23 03:41:38 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|