Update modtools component...

to accept jid and affiliation and to not use a Model to store state
This commit is contained in:
JC Brand 2021-07-27 16:38:51 +02:00
parent 7b4f8954b3
commit 106cde9262
6 changed files with 70 additions and 45 deletions

View File

@ -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);
},

View File

@ -12,7 +12,7 @@ export default (o) => {
${modal_header_close_button}
</div>
<div class="modal-body d-flex flex-column">
<converse-modtools .muc=${o.muc} .model=${o.model}></converse-modtools>
<converse-modtools jid=${o.jid} affiliation=${o.affiliation}></converse-modtools>
</div>
</div>
</div>`;

View File

@ -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) {

View File

@ -169,6 +169,8 @@ export default (o) => {
${ show_both_tabs ? tpl_navigation() : '' }
<div class="tab-content">
${ o.queryable_affiliations.length ? html`
<div class="tab-pane tab-pane--columns ${ o.queryable_affiliations.length ? 'active' : ''}" id="affiliations-tabpanel" role="tabpanel" aria-labelledby="affiliations-tab">
<form class="converse-form query-affiliation" @submit=${o.queryAffiliation}>
<p class="helptext pb-3">${i18n_helptext_affiliation}</p>
@ -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)) : '')) }
</ul>
</div>
</div>
</div>` : '' }
${ o.queryable_roles.length ? html`
<div class="tab-pane tab-pane--columns ${ !show_both_tabs && o.queryable_roles.length ? 'active' : ''}" id="roles-tabpanel" role="tabpanel" aria-labelledby="roles-tab">
<form class="converse-form query-role" @submit=${o.queryRole}>
<p class="helptext pb-3">${i18n_helptext_role}</p>
@ -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)) : '')) }
</ul>
</div>
</div>
</div>`: '' }
</div>`;
}

View File

@ -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']);

View File

@ -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();
}