diff --git a/src/plugins/chatview/view.js b/src/plugins/chatview/view.js
index 2480c3080..41175dfa9 100644
--- a/src/plugins/chatview/view.js
+++ b/src/plugins/chatview/view.js
@@ -133,26 +133,11 @@ export default class ChatView extends BaseChatView {
}
}
- async close (ev) {
- ev?.preventDefault?.();
+ close (ev) {
if (_converse.router.history.getFragment() === 'converse/chat?jid=' + this.model.get('jid')) {
_converse.router.navigate('');
}
- if (api.connection.connected()) {
- // Immediately sending the chat state, because the
- // model is going to be destroyed afterwards.
- this.model.setChatState(_converse.INACTIVE);
- this.model.sendChatState();
- }
- await this.model.close(ev);
- /**
- * Triggered once a chatbox has been closed.
- * @event _converse#chatBoxClosed
- * @type { _converse.ChatBoxView | _converse.ChatRoomView }
- * @example _converse.api.listen.on('chatBoxClosed', view => { ... });
- */
- api.trigger('chatBoxClosed', this);
- return this;
+ return super.close(ev);
}
afterShown () {
diff --git a/src/plugins/muc-views/heading.js b/src/plugins/muc-views/heading.js
index c092d09fe..1f91b7e6f 100644
--- a/src/plugins/muc-views/heading.js
+++ b/src/plugins/muc-views/heading.js
@@ -6,7 +6,11 @@ import tpl_muc_head from './templates/muc-head.js';
import { Model } from '@converse/skeletor/src/model.js';
import { __ } from 'i18n';
import { _converse, api, converse } from "@converse/headless/core";
-import { getHeadingDropdownItem, getHeadingStandaloneButton } from 'plugins/chatview/utils.js';
+import {
+ getHeadingDropdownItem,
+ getHeadingStandaloneButton,
+ showModeratorToolsModal
+} from 'plugins/chatview/utils.js';
import './styles/muc-head.scss';
@@ -61,10 +65,6 @@ export default class MUCHeading extends ChatHeading {
this.model.session.set('view', converse.MUC.VIEWS.CONFIG);
}
- showModeratorToolsModal () {
- _converse.chatboxviews.get(this.getAttribute('jid'))?.showModeratorToolsModal();
- }
-
destroy () {
_converse.chatboxviews.get(this.getAttribute('jid'))?.destroy();
}
@@ -127,7 +127,7 @@ export default class MUCHeading extends ChatHeading {
buttons.push({
'i18n_text': __('Moderate'),
'i18n_title': __('Moderate this groupchat'),
- 'handler': () => this.showModeratorToolsModal(),
+ 'handler': () => showModeratorToolsModal(this.model),
'a_class': 'moderate-chatroom-button',
'icon_class': 'fa-user-cog',
'name': 'moderate'
diff --git a/src/plugins/muc-views/modals/moderator-tools.js b/src/plugins/muc-views/modals/moderator-tools.js
index a653c767f..5bd29a756 100644
--- a/src/plugins/muc-views/modals/moderator-tools.js
+++ b/src/plugins/muc-views/modals/moderator-tools.js
@@ -3,41 +3,38 @@ import log from "@converse/headless/log";
import tpl_moderator_tools_modal from "../templates/moderator-tools.js";
import { AFFILIATIONS, ROLES } from "@converse/headless/plugins/muc/index.js";
import { __ } from 'i18n';
-import { api, converse } from "@converse/headless/core";
+import { _converse, api, converse } from "@converse/headless/core";
import { getAffiliationList, setAffiliation } from '@converse/headless/plugins/muc/affiliations/utils.js'
const { Strophe, sizzle } = converse.env;
const u = converse.env.utils;
-let _converse;
-export default BootstrapModal.extend({
+const ModeratorToolsModal = BootstrapModal.extend({
id: "converse-modtools-modal",
persistent: true,
initialize (attrs) {
- _converse = attrs._converse;
- this.chatroomview = attrs.chatroomview;
+ this.muc = attrs.muc;
BootstrapModal.prototype.initialize.apply(this, arguments);
this.affiliations_filter = '';
this.roles_filter = '';
this.listenTo(this.model, 'change:role', () => {
- this.users_with_role = this.chatroomview.model.getOccupantsWithRole(this.model.get('role'));
+ this.users_with_role = this.muc.getOccupantsWithRole(this.model.get('role'));
this.render();
});
this.listenTo(this.model, 'change:affiliation', async () => {
this.loading_users_with_affiliation = true;
this.users_with_affiliation = null;
this.render();
- const chatroom = this.chatroomview.model;
const affiliation = this.model.get('affiliation');
if (this.shouldFetchAffiliationsList()) {
- const muc_jid = chatroom.get('jid');
+ const muc_jid = this.muc.get('jid');
this.users_with_affiliation = await getAffiliationList(affiliation, muc_jid);
} else {
- this.users_with_affiliation = chatroom.getOccupantsWithAffiliation(affiliation);
+ this.users_with_affiliation = this.muc.getOccupantsWithAffiliation(affiliation);
}
this.loading_users_with_affiliation = false;
this.render();
@@ -45,7 +42,7 @@ export default BootstrapModal.extend({
},
toHTML () {
- const occupant = this.chatroomview.model.occupants.findWhere({'jid': _converse.bare_jid});
+ const occupant = this.muc.occupants.findWhere({'jid': _converse.bare_jid});
return tpl_moderator_tools_modal(Object.assign(this.model.toJSON(), {
'affiliations_filter': this.affiliations_filter,
'assignAffiliation': ev => this.assignAffiliation(ev),
@@ -100,7 +97,7 @@ export default BootstrapModal.extend({
if (affiliation === 'none') {
return false;
}
- const chatroom = this.chatroomview.model;
+ const chatroom = this.muc;
const auto_fetched_affs = chatroom.occupants.getAutoFetchedAffiliationLists();
if (auto_fetched_affs.includes(affiliation)) {
return false;
@@ -159,7 +156,7 @@ export default BootstrapModal.extend({
'reason': data.get('reason')
}
const current_affiliation = this.model.get('affiliation');
- const muc_jid = this.chatroomview.model.get('jid');
+ const muc_jid = this.muc.get('jid');
try {
await setAffiliation(affiliation, muc_jid, [attrs]);
} catch (e) {
@@ -174,7 +171,7 @@ export default BootstrapModal.extend({
return;
}
this.alert(__('Affiliation changed'), 'primary');
- await this.chatroomview.model.occupants.fetchMembers()
+ await this.muc.occupants.fetchMembers()
this.model.set({'affiliation': null}, {'silent': true});
this.model.set({'affiliation': current_affiliation});
},
@@ -183,11 +180,11 @@ export default BootstrapModal.extend({
ev.stopPropagation();
ev.preventDefault();
const data = new FormData(ev.target);
- const occupant = this.chatroomview.model.getOccupant(data.get('jid') || data.get('nick'));
+ const occupant = this.muc.getOccupant(data.get('jid') || data.get('nick'));
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.muc.setRole(occupant, role, reason,
() => {
this.alert(__('Role changed'), 'primary');
this.model.set({'role': null}, {'silent': true});
@@ -206,3 +203,5 @@ export default BootstrapModal.extend({
);
}
});
+
+export default ModeratorToolsModal;
diff --git a/src/plugins/muc-views/muc.js b/src/plugins/muc-views/muc.js
index 45cbc1d98..04e8ff338 100644
--- a/src/plugins/muc-views/muc.js
+++ b/src/plugins/muc-views/muc.js
@@ -1,18 +1,11 @@
import BaseChatView from 'shared/chat/baseview.js';
-import ModeratorToolsModal from './modals/moderator-tools.js';
import log from '@converse/headless/log';
import tpl_muc from './templates/muc.js';
-import { Model } from '@converse/skeletor/src/model.js';
import { __ } from 'i18n';
import { _converse, api, converse } from '@converse/headless/core';
-import { html, render } from "lit";
+import { render } from "lit";
+
-/**
- * Mixin which turns a ChatBoxView into a ChatRoomView
- * @mixin
- * @namespace _converse.ChatRoomView
- * @memberOf _converse
- */
export default class MUCView extends BaseChatView {
length = 300
is_chatroom = true
@@ -23,6 +16,8 @@ export default class MUCView extends BaseChatView {
_converse.chatboxviews.add(jid, this);
this.initDebounced();
+ this.setAttribute('id', this.model.get('box_id'));
+
this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged);
this.listenTo(this.model, 'change:composing_spoiler', this.renderMessageForm);
this.listenTo(this.model, 'change:hidden', () => this.afterShown());
@@ -50,31 +45,10 @@ export default class MUCView extends BaseChatView {
}
render () {
- this.setAttribute('id', this.model.get('box_id'));
- render(
- tpl_muc({
- 'getNicknameRequiredTemplate': () => this.getNicknameRequiredTemplate(),
- 'model': this.model,
- }),
- this
- );
+ render(tpl_muc({ 'model': this.model }), this);
!this.model.get('hidden') && this.show();
}
- showModeratorToolsModal (affiliation) {
- if (!this.model.verifyRoles(['moderator'])) {
- return;
- }
- let modal = api.modal.get(ModeratorToolsModal.id);
- if (modal) {
- modal.model.set('affiliation', affiliation);
- } else {
- const model = new Model({ 'affiliation': affiliation });
- modal = api.modal.create(ModeratorToolsModal, { model, _converse, 'chatroomview': this });
- }
- modal.show();
- }
-
/**
* Callback method that gets called after the chat has become visible.
* @private
@@ -88,15 +62,15 @@ export default class MUCView extends BaseChatView {
}
/**
- * Closes this chat box, which implies leaving the groupchat as well.
+ * Closes this chat, which implies leaving the MUC as well.
* @private
* @method _converse.ChatRoomView#close
*/
- close () {
+ close (ev) {
if (_converse.router.history.getFragment() === 'converse/room?jid=' + this.model.get('jid')) {
_converse.router.navigate('');
}
- return _converse.ChatBoxView.prototype.close.apply(this, arguments);
+ return super.close(ev);
}
async destroy () {
@@ -130,15 +104,6 @@ export default class MUCView extends BaseChatView {
}
}
- getNicknameRequiredTemplate () {
- const jid = this.model.get('jid');
- if (api.settings.get('muc_show_logs_before_join')) {
- return html``;
- } else {
- return html``;
- }
- }
-
updateAfterTransition () {
const conn_status = this.model.session.get('connection_status');
if (conn_status === converse.ROOMSTATUS.CONNECTING) {
diff --git a/src/plugins/muc-views/utils.js b/src/plugins/muc-views/utils.js
index 2486bea9e..54cfd7c78 100644
--- a/src/plugins/muc-views/utils.js
+++ b/src/plugins/muc-views/utils.js
@@ -1,5 +1,7 @@
+import ModeratorToolsModal from './modals/moderator-tools.js';
import log from "@converse/headless/log";
import tpl_spinner from 'templates/spinner.js';
+import { Model } from '@converse/skeletor/src/model.js';
import { __ } from 'i18n';
import { _converse, api, converse } from "@converse/headless/core";
import { html } from "lit";
@@ -68,6 +70,14 @@ export function fetchAndSetMUCDomain (controlboxview) {
}
}
+export function getNicknameRequiredTemplate (model) {
+ const jid = model.get('jid');
+ if (api.settings.get('muc_show_logs_before_join')) {
+ return html``;
+ } else {
+ return html``;
+ }
+}
export function getChatRoomBodyTemplate (o) {
const view = o.model.session.get('view');
@@ -84,7 +94,7 @@ export function getChatRoomBodyTemplate (o) {
${ conn_status == RS.PASSWORD_REQUIRED ? html`` : '' }
${ conn_status == RS.ENTERED ? html`` : '' }
${ conn_status == RS.CONNECTING ? tpl_spinner() : '' }
- ${ conn_status == RS.NICKNAME_REQUIRED ? o.getNicknameRequiredTemplate() : '' }
+ ${ conn_status == RS.NICKNAME_REQUIRED ? getNicknameRequiredTemplate(o.model) : '' }
${ conn_status == RS.DISCONNECTED ? html`` : '' }
${ conn_status == RS.BANNED ? html`` : '' }
${ conn_status == RS.DESTROYED ? html`` : '' }
@@ -92,7 +102,6 @@ export function getChatRoomBodyTemplate (o) {
}
}
-
export function getAutoCompleteListItem (text, input) {
input = input.trim();
const element = document.createElement('li');
@@ -232,6 +241,21 @@ function verifyAndSetAffiliation (muc, command, args, required_affiliations) {
}
+function showModeratorToolsModal (muc, affiliation) {
+ if (!muc.verifyRoles(['moderator'])) {
+ return;
+ }
+ let modal = api.modal.get(ModeratorToolsModal.id);
+ if (modal) {
+ modal.model.set({ affiliation });
+ } else {
+ const model = new Model({ affiliation });
+ modal = api.modal.create(ModeratorToolsModal, { model, muc });
+ }
+ modal.show();
+}
+
+
export function parseMessageForMUCCommands (muc, text) {
if (
api.settings.get('muc_disable_slash_commands') &&
@@ -259,8 +283,7 @@ export function parseMessageForMUCCommands (muc, text) {
break;
}
case 'modtools': {
- const chatview = _converse.chatboxviews.get(muc.get('jid'));
- chatview.showModeratorToolsModal(args);
+ showModeratorToolsModal(muc, args);
break;
}
case 'deop': {
diff --git a/src/shared/chat/baseview.js b/src/shared/chat/baseview.js
index 9508073f7..adb653de0 100644
--- a/src/shared/chat/baseview.js
+++ b/src/shared/chat/baseview.js
@@ -50,6 +50,24 @@ export default class BaseChatView extends ElementView {
this.afterShown();
}
+ async close (ev) {
+ ev?.preventDefault?.();
+ if (api.connection.connected()) {
+ // Immediately sending the chat state, because the
+ // model is going to be destroyed afterwards.
+ this.model.setChatState(_converse.INACTIVE);
+ this.model.sendChatState();
+ }
+ await this.model.close(ev);
+ /**
+ * Triggered once a chatbox has been closed.
+ * @event _converse#chatBoxClosed
+ * @type { _converse.ChatBoxView | _converse.ChatRoomView }
+ * @example _converse.api.listen.on('chatBoxClosed', view => { ... });
+ */
+ api.trigger('chatBoxClosed', this);
+ }
+
emitBlurred (ev) {
if (this.contains(document.activeElement) || this.contains(ev.relatedTarget)) {
// Something else in this chatbox is still focused