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