diff --git a/src/plugins/muc-views/modals/moderator-tools.js b/src/plugins/muc-views/modals/moderator-tools.js index f9d09e3e8..813885a3e 100644 --- a/src/plugins/muc-views/modals/moderator-tools.js +++ b/src/plugins/muc-views/modals/moderator-tools.js @@ -7,7 +7,8 @@ const ModeratorToolsModal = BootstrapModal.extend({ persistent: true, initialize (attrs) { - this.muc = attrs.muc; + this.jid = attrs.jid; + this.affiliation = attrs.affiliation; BootstrapModal.prototype.initialize.apply(this, arguments); }, diff --git a/src/plugins/muc-views/modals/templates/moderator-tools.js b/src/plugins/muc-views/modals/templates/moderator-tools.js index 3ed2c2c61..ab66ac606 100644 --- a/src/plugins/muc-views/modals/templates/moderator-tools.js +++ b/src/plugins/muc-views/modals/templates/moderator-tools.js @@ -12,7 +12,7 @@ export default (o) => { ${modal_header_close_button} `; diff --git a/src/plugins/muc-views/modtools.js b/src/plugins/muc-views/modtools.js index a14727ad0..24276cff3 100644 --- a/src/plugins/muc-views/modtools.js +++ b/src/plugins/muc-views/modtools.js @@ -5,6 +5,7 @@ import { CustomElement } from 'shared/components/element.js'; import { __ } from 'i18n'; import { _converse, api, converse } from '@converse/headless/core'; import { getAssignableRoles } from '@converse/headless/plugins/muc/utils.js'; +import { getOpenPromise } from '@converse/openpromise'; import { getAffiliationList, getAssignableAffiliations, @@ -16,11 +17,13 @@ const { Strophe, sizzle, u } = converse.env; export default class ModeratorTools extends CustomElement { static get properties () { return { + affiliation: { type: String }, affiliations_filter: { type: String, attribute: false }, alert_message: { type: String, attribute: false }, alert_type: { type: String, attribute: false }, - model: { type: Object }, - muc: { type: Object }, + jid: { type: String }, + muc: { type: Object, attribute: false }, + role: { type: String }, roles_filter: { type: String, attribute: false }, users_with_affiliation: { type: Array, attribute: false }, users_with_role: { type: Array, attribute: false }, @@ -29,7 +32,9 @@ export default class ModeratorTools extends CustomElement { constructor () { super(); + this.affiliation = ''; this.affiliations_filter = ''; + this.role = ''; this.roles_filter = ''; } @@ -38,15 +43,24 @@ export default class ModeratorTools extends CustomElement { this.initialize(); } - initialize () { - this.listenTo(this.model, 'change:role', this.onSearchRoleChange, this); - this.listenTo(this.model, 'change:affiliation', this.onSearchAffiliationChange, this); + updated (changed) { + changed.has('role') && this.onSearchRoleChange(); + changed.has('affiliation') && this.onSearchAffiliationChange(); + changed.has('jid') && changed.get('jid') && this.initialize(); + } + + async initialize () { + this.initialized = getOpenPromise(); + const muc = await api.rooms.get(this.jid); + await muc.initialized; + this.muc = muc; + this.initialized.resolve(); } render () { - const occupant = this.muc.occupants.findWhere({ 'jid': _converse.bare_jid }); - return tpl_moderator_tools( - Object.assign(this.model.toJSON(), { + if (this.muc?.occupants) { + const occupant = this.muc.occupants.findWhere({ 'jid': _converse.bare_jid }); + return tpl_moderator_tools({ 'affiliations_filter': this.affiliations_filter, 'alert_message': this.alert_message, 'alert_type': this.alert_type, @@ -67,18 +81,22 @@ export default class ModeratorTools extends CustomElement { 'users_with_affiliation': this.users_with_affiliation, 'users_with_role': this.users_with_role, }) - ); + } else { + return ''; + } } async onSearchAffiliationChange () { + if (!this.affiliation) { + return; + } + await this.initialized; this.clearAlert(); this.loading_users_with_affiliation = true; this.users_with_affiliation = null; - const affiliation = this.model.get('affiliation'); if (this.shouldFetchAffiliationsList()) { - const muc_jid = this.muc.get('jid'); - const result = await getAffiliationList(affiliation, muc_jid); + const result = await getAffiliationList(this.affiliation, this.jid); if (result instanceof Error) { this.alert(result.message, 'danger'); this.users_with_affiliation = []; @@ -86,18 +104,22 @@ export default class ModeratorTools extends CustomElement { this.users_with_affiliation = result; } } else { - this.users_with_affiliation = this.muc.getOccupantsWithAffiliation(affiliation); + this.users_with_affiliation = this.muc.getOccupantsWithAffiliation(this.affiliation); } this.loading_users_with_affiliation = false; } - onSearchRoleChange () { + async onSearchRoleChange () { + if (!this.role) { + return; + } + await this.initialized; this.clearAlert(); - this.users_with_role = this.muc.getOccupantsWithRole(this.model.get('role')); + this.users_with_role = this.muc.getOccupantsWithRole(this.role); } shouldFetchAffiliationsList () { - const affiliation = this.model.get('affiliation'); + const affiliation = this.affiliation; if (affiliation === 'none') { return false; } @@ -136,8 +158,8 @@ export default class ModeratorTools extends CustomElement { 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 }); + this.role = null; + this.role = role; } queryAffiliation (ev) { @@ -145,8 +167,8 @@ export default class ModeratorTools extends CustomElement { 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 }); + this.affiliation = null; + this.affiliation = affiliation; } alert (message, type) { @@ -169,7 +191,7 @@ export default class ModeratorTools extends CustomElement { 'jid': data.get('jid'), 'reason': data.get('reason'), }; - const current_affiliation = this.model.get('affiliation'); + const current_affiliation = this.affiliation; const muc_jid = this.muc.get('jid'); try { await setAffiliation(affiliation, muc_jid, [attrs]); @@ -185,8 +207,8 @@ export default class ModeratorTools extends CustomElement { return; } await this.muc.occupants.fetchMembers(); - this.model.set({ 'affiliation': null }, { 'silent': true }); - this.model.set({ 'affiliation': current_affiliation }); + this.affiliation = null; + this.affiliation = current_affiliation; this.alert(__('Affiliation changed'), 'primary'); } @@ -198,15 +220,15 @@ export default class ModeratorTools extends CustomElement { 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'); + const current_role = this.role; this.muc.setRole( occupant, role, reason, () => { this.alert(__('Role changed'), 'primary'); - this.model.set({ 'role': null }, { 'silent': true }); - this.model.set({ 'role': current_role }); + this.role = null; + this.role = current_role; }, e => { if (sizzle(`not-allowed[xmlns="${Strophe.NS.STANZAS}"]`, e).length) { diff --git a/src/plugins/muc-views/templates/moderator-tools.js b/src/plugins/muc-views/templates/moderator-tools.js index 0ae66bddf..86b86db2e 100644 --- a/src/plugins/muc-views/templates/moderator-tools.js +++ b/src/plugins/muc-views/templates/moderator-tools.js @@ -169,6 +169,8 @@ export default (o) => { ${ show_both_tabs ? tpl_navigation() : '' }
+ + ${ o.queryable_affiliations.length ? html`

${i18n_helptext_affiliation}

@@ -208,8 +210,9 @@ export default (o) => { (o.users_with_affiliation || []).map(item => ((item.nick || item.jid).match(new RegExp(o.affiliations_filter, 'i')) ? affiliation_list_item(Object.assign({item}, o)) : '')) }
-
+ ` : '' } + ${ o.queryable_roles.length ? html`

${i18n_helptext_role}

@@ -242,6 +245,6 @@ export default (o) => { ${ (o.users_with_role || []).map(item => (item.nick.match(o.roles_filter) ? role_list_item(Object.assign({item}, o)) : '')) }
- + `: '' } `; } diff --git a/src/plugins/muc-views/tests/modtools.js b/src/plugins/muc-views/tests/modtools.js index 14e538fca..d552a6343 100644 --- a/src/plugins/muc-views/tests/modtools.js +++ b/src/plugins/muc-views/tests/modtools.js @@ -47,6 +47,7 @@ describe("The groupchat moderator tool", function () { let button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); + await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length); let user_els = modal.el.querySelectorAll('.list-group--users > li'); expect(user_els.length).toBe(1); expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: wiccarocks@shakespeare.lit'); @@ -57,6 +58,7 @@ describe("The groupchat moderator tool", function () { select.value = 'owner'; button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); + await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 2); user_els = modal.el.querySelectorAll('.list-group--users > li'); expect(user_els.length).toBe(2); expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit'); @@ -113,6 +115,7 @@ describe("The groupchat moderator tool", function () { const alert = modal.el.querySelector('.alert-primary'); expect(alert.textContent.trim()).toBe('Affiliation changed'); + await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 1); user_els = modal.el.querySelectorAll('.list-group--users > li'); expect(user_els.length).toBe(1); expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit'); @@ -132,9 +135,8 @@ describe("The groupchat moderator tool", function () { select.value = 'participant'; button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); - user_els = roles_panel.querySelectorAll('.list-group--users > li') - expect(user_els.length).toBe(1); - expect(user_els[0].textContent.trim()).toBe('No users with that role found.'); + await u.waitUntil(() => roles_panel.querySelectorAll('.list-group--users > li')[0]?.textContent.trim() === 'No users with that role found.'); + })); it("allows you to filter affiliation search results", @@ -162,8 +164,7 @@ describe("The groupchat moderator tool", function () { const button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); - const user_els = modal.el.querySelectorAll('.list-group--users > li'); - expect(user_els.length).toBe(6); + await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 6); const nicks = Array.from(modal.el.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick')); expect(nicks.join(' ')).toBe('gower juliet romeo thirdwitch wiccan witch'); @@ -273,8 +274,7 @@ describe("The groupchat moderator tool", function () { const button = modal.el.querySelector('.btn-primary[name="users_with_role"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_role); - const user_els = modal.el.querySelectorAll('.list-group--users > li'); - expect(user_els.length).toBe(6); + await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 6); const nicks = Array.from(modal.el.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick')); expect(nicks.join(' ')).toBe('crone newb nomorenicks oldhag some1 tux'); @@ -368,9 +368,9 @@ describe("The groupchat moderator tool", function () { const button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); - const user_els = modal.el.querySelectorAll('.list-group--users > li'); - expect(user_els.length).toBe(1); + await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 1); + const user_els = modal.el.querySelectorAll('.list-group--users > li'); const toggle = user_els[0].querySelector('.list-group-item:nth-child(3n) .toggle-form'); const form = user_els[0].querySelector('.list-group-item:nth-child(3n) .affiliation-form'); expect(u.hasClass('hidden', form)).toBeTruthy(); @@ -432,9 +432,9 @@ describe("The groupchat moderator tool", function () { button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); - const user_els = modal.el.querySelectorAll('.list-group--users > li'); - expect(user_els.length).toBe(2); + await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 2); + const user_els = modal.el.querySelectorAll('.list-group--users > li'); let change_affiliation_dropdown = user_els[0].querySelector('.select-affiliation'); expect(Array.from(change_affiliation_dropdown.options).map(o => o.value)).toEqual(['member', 'outcast', 'none']); diff --git a/src/plugins/muc-views/utils.js b/src/plugins/muc-views/utils.js index bd444b876..eac77b728 100644 --- a/src/plugins/muc-views/utils.js +++ b/src/plugins/muc-views/utils.js @@ -1,7 +1,6 @@ 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"; @@ -284,10 +283,10 @@ export function showModeratorToolsModal (muc, affiliation) { } let modal = api.modal.get(ModeratorToolsModal.id); if (modal) { - modal.model.set({ affiliation }); + modal.affiliation = affiliation; + modal.render(); } else { - const model = new Model({ affiliation }); - modal = api.modal.create(ModeratorToolsModal, { model, muc }); + modal = api.modal.create(ModeratorToolsModal, { affiliation, 'jid': muc.get('jid') }); } modal.show(); }