Create an ElementView base modal and use it for all modals
Modals are now all web components and are opened by component name.
This commit is contained in:
parent
927add0707
commit
fbe86e5af8
@ -106,6 +106,7 @@ module.exports = function(config) {
|
|||||||
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' },
|
{ pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/rosterview/tests/add-contact-modal.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rosterview/tests/presence.js", type: 'module' },
|
{ pattern: "src/plugins/rosterview/tests/presence.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rosterview/tests/protocol.js", type: 'module' },
|
{ pattern: "src/plugins/rosterview/tests/protocol.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rosterview/tests/roster.js", type: 'module' },
|
{ pattern: "src/plugins/rosterview/tests/roster.js", type: 'module' },
|
||||||
|
2353
package-lock.json
generated
2353
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -87,7 +87,7 @@
|
|||||||
"karma-jasmine": "^5.0.0",
|
"karma-jasmine": "^5.0.0",
|
||||||
"karma-jasmine-html-reporter": "^1.7.0",
|
"karma-jasmine-html-reporter": "^1.7.0",
|
||||||
"karma-webpack": "^5.0.0",
|
"karma-webpack": "^5.0.0",
|
||||||
"lerna": "^5.1.8",
|
"lerna": "^5.5.1",
|
||||||
"mini-css-extract-plugin": "^2.6.0",
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"po-loader": "0.6.1",
|
"po-loader": "0.6.1",
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import RosterContact from './contact.js';
|
import RosterContact from './contact.js';
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import sum from 'lodash-es/sum';
|
|
||||||
import { Collection } from "@converse/skeletor/src/collection";
|
import { Collection } from "@converse/skeletor/src/collection";
|
||||||
import { Model } from "@converse/skeletor/src/model";
|
import { Model } from "@converse/skeletor/src/model";
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import BootstrapModal from "plugins/modal/base.js";
|
|
||||||
import tpl_image_modal from "./templates/image.js";
|
|
||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
|
||||||
id: 'image-modal',
|
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
return tpl_image_modal({
|
|
||||||
'src': this.src,
|
|
||||||
'onload': ev => (ev.target.parentElement.style.height = `${ev.target.height}px`)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,10 +0,0 @@
|
|||||||
import BootstrapModal from "plugins/modal/base.js";
|
|
||||||
import tpl_message_versions_modal from "./templates/message-versions.js";
|
|
||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
|
||||||
id: "message-versions-modal",
|
|
||||||
toHTML () {
|
|
||||||
return tpl_message_versions_modal(this.model);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,20 +0,0 @@
|
|||||||
import { html } from "lit";
|
|
||||||
import { __ } from 'i18n';
|
|
||||||
import { modal_close_button, modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
|
||||||
return html`
|
|
||||||
<div class="modal-dialog fit-content" role="document">
|
|
||||||
<div class="modal-content fit-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h4 class="modal-title" id="message-versions-modal-label">${__('Image: ')}<a target="_blank" rel="noopener" href="${o.src}">${o.src}</a></h4>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body modal-body--image fit-content">
|
|
||||||
<img class="chat-image" src="${o.src}" @load=${o.onload}>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">${modal_close_button}</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import 'shared/components/message-versions.js';
|
|
||||||
import { __ } from 'i18n';
|
|
||||||
import { html } from "lit";
|
|
||||||
import { modal_close_button, modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
|
|
||||||
export default (model) => html`
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h4 class="modal-title" id="message-versions-modal-label">${__('Message versions')}</h4>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<converse-message-versions .model=${model}></converse-message-versions>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">${modal_close_button}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
@ -1,71 +0,0 @@
|
|||||||
import avatar from 'shared/avatar/templates/avatar.js';
|
|
||||||
import { __ } from 'i18n';
|
|
||||||
import { html } from 'lit';
|
|
||||||
import { modal_close_button, modal_header_close_button } from 'plugins/modal/templates/buttons.js'
|
|
||||||
|
|
||||||
|
|
||||||
const remove_button = (o) => {
|
|
||||||
const i18n_remove_contact = __('Remove as contact');
|
|
||||||
return html`
|
|
||||||
<button type="button" @click="${o.removeContact}" class="btn btn-danger remove-contact">
|
|
||||||
<converse-icon
|
|
||||||
class="fas fa-trash-alt"
|
|
||||||
color="var(--text-color-lighten-15-percent)"
|
|
||||||
size="1em"
|
|
||||||
></converse-icon>
|
|
||||||
${i18n_remove_contact}
|
|
||||||
</button>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
|
||||||
const i18n_address = __('XMPP Address');
|
|
||||||
const i18n_email = __('Email');
|
|
||||||
const i18n_full_name = __('Full Name');
|
|
||||||
const i18n_nickname = __('Nickname');
|
|
||||||
const i18n_profile = __('The User\'s Profile Image');
|
|
||||||
const i18n_refresh = __('Refresh');
|
|
||||||
const i18n_role = __('Role');
|
|
||||||
const i18n_url = __('URL');
|
|
||||||
const avatar_data = {
|
|
||||||
'alt_text': i18n_profile,
|
|
||||||
'extra_classes': 'mb-3',
|
|
||||||
'height': '120',
|
|
||||||
'width': '120'
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="user-details-modal-label">${o.display_name}</h5>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
${ o.image ? html`<div class="mb-4">${avatar(Object.assign(o, avatar_data))}</div>` : '' }
|
|
||||||
${ o.fullname ? html`<p><label>${i18n_full_name}:</label> ${o.fullname}</p>` : '' }
|
|
||||||
<p><label>${i18n_address}:</label> <a href="xmpp:${o.jid}">${o.jid}</a></p>
|
|
||||||
${ o.nickname ? html`<p><label>${i18n_nickname}:</label> ${o.nickname}</p>` : '' }
|
|
||||||
${ o.url ? html`<p><label>${i18n_url}:</label> <a target="_blank" rel="noopener" href="${o.url}">${o.url}</a></p>` : '' }
|
|
||||||
${ o.email ? html`<p><label>${i18n_email}:</label> <a href="mailto:${o.email}">${o.email}</a></p>` : '' }
|
|
||||||
${ o.role ? html`<p><label>${i18n_role}:</label> ${o.role}</p>` : '' }
|
|
||||||
|
|
||||||
<converse-omemo-fingerprints jid=${o.jid}></converse-omemo-fingerprints>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
${modal_close_button}
|
|
||||||
<button type="button" class="btn btn-info refresh-contact">
|
|
||||||
<converse-icon
|
|
||||||
class="fa fa-refresh"
|
|
||||||
color="var(--text-color-lighten-15-percent)"
|
|
||||||
size="1em"
|
|
||||||
></converse-icon>
|
|
||||||
${i18n_refresh}</button>
|
|
||||||
${ (o.allow_contact_removal && o.is_roster_contact) ? remove_button(o) : '' }
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import MUCBookmarkFormModal from './modal.js';
|
import './modal.js';
|
||||||
import { _converse, api, converse } from '@converse/headless/core';
|
import { _converse, api, converse } from '@converse/headless/core';
|
||||||
|
|
||||||
const { u } = converse.env;
|
const { u } = converse.env;
|
||||||
@ -34,6 +34,6 @@ export const bookmarkableChatRoomView = {
|
|||||||
showBookmarkModal(ev) {
|
showBookmarkModal(ev) {
|
||||||
ev?.preventDefault();
|
ev?.preventDefault();
|
||||||
const jid = this.model.get('jid');
|
const jid = this.model.get('jid');
|
||||||
api.modal.show(MUCBookmarkFormModal, { jid }, ev);
|
api.modal.show('converse-bookmark-form-modal', { jid }, ev);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import './form.js';
|
import './form.js';
|
||||||
import BaseModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_modal from './templates/modal.js';
|
import { html } from "lit";
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import { api } from "@converse/headless/core";
|
||||||
|
|
||||||
const MUCBookmarkFormModal = BaseModal.extend({
|
export default class BookmarkFormModal extends BaseModal {
|
||||||
id: "converse-bookmark-modal",
|
|
||||||
|
|
||||||
initialize (attrs) {
|
renderModal () {
|
||||||
this.jid = attrs.jid;
|
return html`
|
||||||
this.affiliation = attrs.affiliation;
|
<converse-muc-bookmark-form class="muc-form-container" jid="${this.jid}">
|
||||||
BaseModal.prototype.initialize.apply(this, arguments);
|
</converse-muc-bookmark-form>`;
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
return tpl_modal(this);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
export default MUCBookmarkFormModal;
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Bookmark');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-bookmark-form-modal', BookmarkFormModal);
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import { __ } from 'i18n';
|
|
||||||
import { html } from "lit";
|
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
export default (o) => {
|
|
||||||
const i18n_moderator_tools = __('Bookmark');
|
|
||||||
return html`
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="converse-modtools-modal-label">${i18n_moderator_tools}</h5>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body d-flex flex-column">
|
|
||||||
<converse-muc-bookmark-form class="muc-form-container" jid="${o.jid}"></converse-muc-bookmark-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
@ -31,8 +31,8 @@ describe("A chat room", function () {
|
|||||||
expect(toggle.title).toBe('Bookmark this groupchat');
|
expect(toggle.title).toBe('Bookmark this groupchat');
|
||||||
toggle.click();
|
toggle.click();
|
||||||
|
|
||||||
const modal = _converse.api.modal.get('converse-bookmark-modal');
|
const modal = _converse.api.modal.get('converse-bookmark-form-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
/* Client uploads data:
|
/* Client uploads data:
|
||||||
* --------------------
|
* --------------------
|
||||||
@ -66,13 +66,13 @@ describe("A chat room", function () {
|
|||||||
* </iq>
|
* </iq>
|
||||||
*/
|
*/
|
||||||
expect(view.model.get('bookmarked')).toBeFalsy();
|
expect(view.model.get('bookmarked')).toBeFalsy();
|
||||||
const form = await u.waitUntil(() => modal.el.querySelector('.chatroom-form'));
|
const form = await u.waitUntil(() => modal.querySelector('.chatroom-form'));
|
||||||
form.querySelector('input[name="name"]').value = 'Play's the Thing';
|
form.querySelector('input[name="name"]').value = 'Play's the Thing';
|
||||||
form.querySelector('input[name="autojoin"]').checked = 'checked';
|
form.querySelector('input[name="autojoin"]').checked = 'checked';
|
||||||
form.querySelector('input[name="nick"]').value = 'JC';
|
form.querySelector('input[name="nick"]').value = 'JC';
|
||||||
|
|
||||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||||
modal.el.querySelector('converse-muc-bookmark-form .btn-primary').click();
|
modal.querySelector('converse-muc-bookmark-form .btn-primary').click();
|
||||||
|
|
||||||
const sent_stanza = await u.waitUntil(
|
const sent_stanza = await u.waitUntil(
|
||||||
() => IQ_stanzas.filter(s => sizzle('iq publish[node="storage:bookmarks"]', s).length).pop());
|
() => IQ_stanzas.filter(s => sizzle('iq publish[node="storage:bookmarks"]', s).length).pop());
|
||||||
@ -250,16 +250,16 @@ describe("A chat room", function () {
|
|||||||
bookmark_icon.click();
|
bookmark_icon.click();
|
||||||
expect(view.showBookmarkModal).toHaveBeenCalled();
|
expect(view.showBookmarkModal).toHaveBeenCalled();
|
||||||
|
|
||||||
const modal = _converse.api.modal.get('converse-bookmark-modal');
|
const modal = _converse.api.modal.get('converse-bookmark-form-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
const form = await u.waitUntil(() => modal.el.querySelector('.chatroom-form'));
|
const form = await u.waitUntil(() => modal.querySelector('.chatroom-form'));
|
||||||
|
|
||||||
expect(form.querySelector('input[name="name"]').value).toBe('The Play');
|
expect(form.querySelector('input[name="name"]').value).toBe('The Play');
|
||||||
expect(form.querySelector('input[name="autojoin"]').checked).toBeFalsy();
|
expect(form.querySelector('input[name="autojoin"]').checked).toBeFalsy();
|
||||||
expect(form.querySelector('input[name="nick"]').value).toBe('Othello');
|
expect(form.querySelector('input[name="nick"]').value).toBe('Othello');
|
||||||
|
|
||||||
// Remove the bookmark
|
// Remove the bookmark
|
||||||
modal.el.querySelector('.button-remove').click();
|
modal.querySelector('.button-remove').click();
|
||||||
|
|
||||||
await u.waitUntil(() => view.querySelector('.chatbox-title__text .fa-bookmark') === null);
|
await u.waitUntil(() => view.querySelector('.chatbox-title__text .fa-bookmark') === null);
|
||||||
expect(_converse.bookmarks.length).toBe(0);
|
expect(_converse.bookmarks.length).toBe(0);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import MUCBookmarkFormModal from './modal.js';
|
import './modal.js';
|
||||||
import invokeMap from 'lodash-es/invokeMap';
|
import invokeMap from 'lodash-es/invokeMap';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
@ -38,7 +38,7 @@ export async function removeBookmarkViaEvent (ev) {
|
|||||||
export function addBookmarkViaEvent (ev) {
|
export function addBookmarkViaEvent (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const jid = ev.currentTarget.getAttribute('data-room-jid');
|
const jid = ev.currentTarget.getAttribute('data-room-jid');
|
||||||
api.modal.show(MUCBookmarkFormModal, { jid }, ev);
|
api.modal.show('converse-bookmark-form-modal', { jid }, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import UserDetailsModal from 'modals/user-details.js';
|
import 'shared/modals/user-details.js';
|
||||||
import tpl_chatbox_head from './templates/chat-head.js';
|
import tpl_chatbox_head from './templates/chat-head.js';
|
||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
@ -39,7 +39,7 @@ export default class ChatHeading extends CustomElement {
|
|||||||
|
|
||||||
showUserDetailsModal (ev) {
|
showUserDetailsModal (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
api.modal.show(UserDetailsModal, { model: this.model }, ev);
|
api.modal.show('converse-user-details-modal', { model: this.model }, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
close (ev) {
|
close (ev) {
|
||||||
|
@ -343,9 +343,9 @@ describe("A Chat Message", function () {
|
|||||||
expect(view.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
|
expect(view.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
|
||||||
view.querySelector('.chat-msg__content .fa-edit').click();
|
view.querySelector('.chat-msg__content .fa-edit').click();
|
||||||
|
|
||||||
const modal = _converse.api.modal.get('message-versions-modal');
|
const modal = _converse.api.modal.get('converse-message-versions-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
const older_msgs = modal.el.querySelectorAll('.older-msg');
|
const older_msgs = modal.querySelectorAll('.older-msg');
|
||||||
expect(older_msgs.length).toBe(2);
|
expect(older_msgs.length).toBe(2);
|
||||||
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
||||||
expect(view.model.messages.models.length).toBe(1);
|
expect(view.model.messages.models.length).toBe(1);
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
const $msg = converse.env.$msg;
|
const $msg = converse.env.$msg;
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
const Strophe = converse.env.Strophe;
|
const Strophe = converse.env.Strophe;
|
||||||
const sizzle = converse.env.sizzle;
|
|
||||||
|
|
||||||
|
|
||||||
describe("The Controlbox", function () {
|
describe("The Controlbox", function () {
|
||||||
|
|
||||||
@ -124,10 +122,10 @@ describe("The Controlbox", function () {
|
|||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
var cbview = _converse.chatboxviews.get('controlbox');
|
var cbview = _converse.chatboxviews.get('controlbox');
|
||||||
cbview.querySelector('.change-status').click()
|
cbview.querySelector('.change-status').click()
|
||||||
const modal = _converse.api.modal.get('modal-status-change');
|
const modal = _converse.api.modal.get('converse-chat-status-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
|
modal.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
|
||||||
modal.el.querySelector('[type="submit"]').click();
|
modal.querySelector('[type="submit"]').click();
|
||||||
const sent_stanzas = _converse.connection.sent_stanzas;
|
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||||
const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
|
const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
|
||||||
expect(Strophe.serialize(sent_presence)).toBe(
|
expect(Strophe.serialize(sent_presence)).toBe(
|
||||||
@ -149,12 +147,12 @@ describe("The Controlbox", function () {
|
|||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
const cbview = _converse.chatboxviews.get('controlbox');
|
const cbview = _converse.chatboxviews.get('controlbox');
|
||||||
cbview.querySelector('.change-status').click()
|
cbview.querySelector('.change-status').click()
|
||||||
const modal = _converse.api.modal.get('modal-status-change');
|
const modal = _converse.api.modal.get('converse-chat-status-modal');
|
||||||
|
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
const msg = 'I am happy';
|
const msg = 'I am happy';
|
||||||
modal.el.querySelector('input[name="status_message"]').value = msg;
|
modal.querySelector('input[name="status_message"]').value = msg;
|
||||||
modal.el.querySelector('[type="submit"]').click();
|
modal.querySelector('[type="submit"]').click();
|
||||||
const sent_stanzas = _converse.connection.sent_stanzas;
|
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||||
const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
|
const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
|
||||||
expect(Strophe.serialize(sent_presence)).toBe(
|
expect(Strophe.serialize(sent_presence)).toBe(
|
||||||
@ -171,193 +169,3 @@ describe("The Controlbox", function () {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("The 'Add Contact' widget", function () {
|
|
||||||
|
|
||||||
it("opens up an add modal when you click on it",
|
|
||||||
mock.initConverse([], {}, async function (_converse) {
|
|
||||||
|
|
||||||
await mock.waitForRoster(_converse, 'all');
|
|
||||||
await mock.openControlBox(_converse);
|
|
||||||
|
|
||||||
const cbview = _converse.chatboxviews.get('controlbox');
|
|
||||||
cbview.querySelector('.add-contact').click()
|
|
||||||
const modal = _converse.api.modal.get('add-contact-modal');
|
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
|
||||||
expect(modal.el.querySelector('form.add-xmpp-contact')).not.toBe(null);
|
|
||||||
|
|
||||||
const input_jid = modal.el.querySelector('input[name="jid"]');
|
|
||||||
const input_name = modal.el.querySelector('input[name="name"]');
|
|
||||||
input_jid.value = 'someone@';
|
|
||||||
|
|
||||||
const evt = new Event('input');
|
|
||||||
input_jid.dispatchEvent(evt);
|
|
||||||
expect(modal.el.querySelector('.suggestion-box li').textContent).toBe('someone@montague.lit');
|
|
||||||
input_jid.value = 'someone@montague.lit';
|
|
||||||
input_name.value = 'Someone';
|
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
|
||||||
|
|
||||||
const sent_IQs = _converse.connection.IQ_stanzas;
|
|
||||||
const sent_stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`)).pop());
|
|
||||||
expect(Strophe.serialize(sent_stanza)).toEqual(
|
|
||||||
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
|
||||||
`<query xmlns="jabber:iq:roster"><item jid="someone@montague.lit" name="Someone"/></query>`+
|
|
||||||
`</iq>`);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("can be configured to not provide search suggestions",
|
|
||||||
mock.initConverse([], {'autocomplete_add_contact': false}, async function (_converse) {
|
|
||||||
|
|
||||||
await mock.waitForRoster(_converse, 'all', 0);
|
|
||||||
await mock.openControlBox(_converse);
|
|
||||||
const cbview = _converse.chatboxviews.get('controlbox');
|
|
||||||
cbview.querySelector('.add-contact').click()
|
|
||||||
const modal = _converse.api.modal.get('add-contact-modal');
|
|
||||||
expect(modal.jid_auto_complete).toBe(undefined);
|
|
||||||
expect(modal.name_auto_complete).toBe(undefined);
|
|
||||||
|
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
|
||||||
expect(modal.el.querySelector('form.add-xmpp-contact')).not.toBe(null);
|
|
||||||
const input_jid = modal.el.querySelector('input[name="jid"]');
|
|
||||||
input_jid.value = 'someone@montague.lit';
|
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
|
||||||
|
|
||||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
|
||||||
const sent_stanza = await u.waitUntil(
|
|
||||||
() => IQ_stanzas.filter(s => sizzle(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`, s).length).pop()
|
|
||||||
);
|
|
||||||
expect(Strophe.serialize(sent_stanza)).toEqual(
|
|
||||||
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
|
||||||
`<query xmlns="jabber:iq:roster"><item jid="someone@montague.lit"/></query>`+
|
|
||||||
`</iq>`
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("integrates with xhr_user_search_url to search for contacts",
|
|
||||||
mock.initConverse([], { 'xhr_user_search_url': 'http://example.org/?' },
|
|
||||||
async function (_converse) {
|
|
||||||
|
|
||||||
await mock.waitForRoster(_converse, 'all', 0);
|
|
||||||
|
|
||||||
class MockXHR extends XMLHttpRequest {
|
|
||||||
open () {} // eslint-disable-line
|
|
||||||
responseText = ''
|
|
||||||
send () {
|
|
||||||
this.responseText = JSON.stringify([
|
|
||||||
{"jid": "marty@mcfly.net", "fullname": "Marty McFly"},
|
|
||||||
{"jid": "doc@brown.com", "fullname": "Doc Brown"}
|
|
||||||
]);
|
|
||||||
this.onload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const XMLHttpRequestBackup = window.XMLHttpRequest;
|
|
||||||
window.XMLHttpRequest = MockXHR;
|
|
||||||
|
|
||||||
await mock.openControlBox(_converse);
|
|
||||||
const cbview = _converse.chatboxviews.get('controlbox');
|
|
||||||
cbview.querySelector('.add-contact').click()
|
|
||||||
const modal = _converse.api.modal.get('add-contact-modal');
|
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
|
||||||
|
|
||||||
// We only have autocomplete for the name input
|
|
||||||
expect(modal.jid_auto_complete).toBe(undefined);
|
|
||||||
expect(modal.name_auto_complete instanceof _converse.AutoComplete).toBe(true);
|
|
||||||
|
|
||||||
const input_el = modal.el.querySelector('input[name="name"]');
|
|
||||||
input_el.value = 'marty';
|
|
||||||
input_el.dispatchEvent(new Event('input'));
|
|
||||||
await u.waitUntil(() => modal.el.querySelector('.suggestion-box li'), 1000);
|
|
||||||
expect(modal.el.querySelectorAll('.suggestion-box li').length).toBe(1);
|
|
||||||
const suggestion = modal.el.querySelector('.suggestion-box li');
|
|
||||||
expect(suggestion.textContent).toBe('Marty McFly');
|
|
||||||
|
|
||||||
// Mock selection
|
|
||||||
modal.name_auto_complete.select(suggestion);
|
|
||||||
|
|
||||||
expect(input_el.value).toBe('Marty McFly');
|
|
||||||
expect(modal.el.querySelector('input[name="jid"]').value).toBe('marty@mcfly.net');
|
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
|
||||||
|
|
||||||
const sent_IQs = _converse.connection.IQ_stanzas;
|
|
||||||
const sent_stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`)).pop());
|
|
||||||
expect(Strophe.serialize(sent_stanza)).toEqual(
|
|
||||||
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
|
||||||
`<query xmlns="jabber:iq:roster"><item jid="marty@mcfly.net" name="Marty McFly"/></query>`+
|
|
||||||
`</iq>`);
|
|
||||||
window.XMLHttpRequest = XMLHttpRequestBackup;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("can be configured to not provide search suggestions for XHR search results",
|
|
||||||
mock.initConverse([],
|
|
||||||
{ 'autocomplete_add_contact': false,
|
|
||||||
'xhr_user_search_url': 'http://example.org/?' },
|
|
||||||
async function (_converse) {
|
|
||||||
|
|
||||||
await mock.waitForRoster(_converse, 'all');
|
|
||||||
await mock.openControlBox(_converse);
|
|
||||||
|
|
||||||
class MockXHR extends XMLHttpRequest {
|
|
||||||
open () {} // eslint-disable-line
|
|
||||||
responseText = ''
|
|
||||||
send () {
|
|
||||||
const value = modal.el.querySelector('input[name="name"]').value;
|
|
||||||
if (value === 'existing') {
|
|
||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
|
||||||
this.responseText = JSON.stringify([{"jid": contact_jid, "fullname": mock.cur_names[0]}]);
|
|
||||||
} else if (value === 'romeo') {
|
|
||||||
this.responseText = JSON.stringify([{"jid": "romeo@montague.lit", "fullname": "Romeo Montague"}]);
|
|
||||||
} else if (value === 'ambiguous') {
|
|
||||||
this.responseText = JSON.stringify([
|
|
||||||
{"jid": "marty@mcfly.net", "fullname": "Marty McFly"},
|
|
||||||
{"jid": "doc@brown.com", "fullname": "Doc Brown"}
|
|
||||||
]);
|
|
||||||
} else if (value === 'insufficient') {
|
|
||||||
this.responseText = JSON.stringify([]);
|
|
||||||
} else {
|
|
||||||
this.responseText = JSON.stringify([{"jid": "marty@mcfly.net", "fullname": "Marty McFly"}]);
|
|
||||||
}
|
|
||||||
this.onload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const XMLHttpRequestBackup = window.XMLHttpRequest;
|
|
||||||
window.XMLHttpRequest = MockXHR;
|
|
||||||
|
|
||||||
const cbview = _converse.chatboxviews.get('controlbox');
|
|
||||||
cbview.querySelector('.add-contact').click()
|
|
||||||
const modal = _converse.api.modal.get('add-contact-modal');
|
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
|
||||||
|
|
||||||
expect(modal.jid_auto_complete).toBe(undefined);
|
|
||||||
expect(modal.name_auto_complete).toBe(undefined);
|
|
||||||
|
|
||||||
const input_el = modal.el.querySelector('input[name="name"]');
|
|
||||||
input_el.value = 'ambiguous';
|
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
|
||||||
let feedback_el = modal.el.querySelector('.invalid-feedback');
|
|
||||||
expect(feedback_el.textContent).toBe('Sorry, could not find a contact with that name');
|
|
||||||
feedback_el.textContent = '';
|
|
||||||
|
|
||||||
input_el.value = 'insufficient';
|
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
|
||||||
feedback_el = modal.el.querySelector('.invalid-feedback');
|
|
||||||
expect(feedback_el.textContent).toBe('Sorry, could not find a contact with that name');
|
|
||||||
feedback_el.textContent = '';
|
|
||||||
|
|
||||||
input_el.value = 'existing';
|
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
|
||||||
feedback_el = modal.el.querySelector('.invalid-feedback');
|
|
||||||
expect(feedback_el.textContent).toBe('This contact has already been added');
|
|
||||||
|
|
||||||
input_el.value = 'Marty McFly';
|
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
|
||||||
|
|
||||||
const sent_IQs = _converse.connection.IQ_stanzas;
|
|
||||||
const sent_stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`)).pop());
|
|
||||||
expect(Strophe.serialize(sent_stanza)).toEqual(
|
|
||||||
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
|
||||||
`<query xmlns="jabber:iq:roster"><item jid="marty@mcfly.net" name="Marty McFly"/></query>`+
|
|
||||||
`</iq>`);
|
|
||||||
window.XMLHttpRequest = XMLHttpRequestBackup;
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
import BootstrapModal from "./base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_alert_modal from "./templates/alert.js";
|
import tpl_alert_modal from "./templates/alert.js";
|
||||||
import { __ } from 'i18n';
|
import { api } from "@converse/headless/core";
|
||||||
|
|
||||||
|
|
||||||
const Alert = BootstrapModal.extend({
|
export default class Alert extends BaseModal {
|
||||||
id: 'alert-modal',
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
super.initialize();
|
||||||
this.listenTo(this.model, 'change', this.render)
|
this.listenTo(this.model, 'change', () => this.render())
|
||||||
},
|
this.addEventListener('hide.bs.modal', () => this.remove(), false);
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
return tpl_alert_modal(Object.assign({__}, this.model.toJSON()));
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
export default Alert;
|
renderModal () {
|
||||||
|
return tpl_alert_modal(this.model.toJSON());
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () {
|
||||||
|
return this.model.get('title');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-alert-modal', Alert);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import Alert from './alert.js';
|
import './alert.js';
|
||||||
import Confirm from './confirm.js';
|
import Confirm from './confirm.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
|
|
||||||
let modals = [];
|
let modals = [];
|
||||||
|
let modals_map = {};
|
||||||
|
|
||||||
const modal_api = {
|
const modal_api = {
|
||||||
/**
|
/**
|
||||||
@ -17,13 +17,20 @@ const modal_api = {
|
|||||||
* Will create a new instance of that class if an existing one isn't
|
* Will create a new instance of that class if an existing one isn't
|
||||||
* found.
|
* found.
|
||||||
* @param { Class } ModalClass
|
* @param { Class } ModalClass
|
||||||
* @param { Object } [properties] - Optional properties that will be
|
* @param { Object } [properties] - Optional properties that will be set on a newly created modal instance.
|
||||||
* set on a newly created modal instance (if no pre-existing modal was
|
|
||||||
* found).
|
|
||||||
* @param { Event } [event] - The DOM event that causes the modal to be shown.
|
* @param { Event } [event] - The DOM event that causes the modal to be shown.
|
||||||
*/
|
*/
|
||||||
show (ModalClass, properties, ev) {
|
show (name, properties, ev) {
|
||||||
const modal = this.get(ModalClass.id) || this.create(ModalClass, properties);
|
let modal;
|
||||||
|
if (typeof name === 'string') {
|
||||||
|
modal = this.get(name) ?? this.create(name, properties);
|
||||||
|
Object.assign(modal, properties);
|
||||||
|
} else {
|
||||||
|
// Legacy...
|
||||||
|
const ModalClass = name;
|
||||||
|
const id = ModalClass.id ?? properties.id;
|
||||||
|
modal = this.get(id) ?? this.create(ModalClass, properties);
|
||||||
|
}
|
||||||
modal.show(ev);
|
modal.show(ev);
|
||||||
return modal;
|
return modal;
|
||||||
},
|
},
|
||||||
@ -33,28 +40,44 @@ const modal_api = {
|
|||||||
* @param { String } id
|
* @param { String } id
|
||||||
*/
|
*/
|
||||||
get (id) {
|
get (id) {
|
||||||
return modals.filter(m => m.id == id).pop();
|
return modals_map[id] ?? modals.filter(m => m.id == id).pop();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a modal of the passed-in type.
|
* Create a modal of the passed-in type.
|
||||||
* @param { Class } ModalClass
|
* @param { String } name
|
||||||
* @param { Object } [properties] - Optional properties that will be
|
* @param { Object } [properties] - Optional properties that will be
|
||||||
* set on the modal instance.
|
* set on the modal instance.
|
||||||
*/
|
*/
|
||||||
create (ModalClass, properties) {
|
create (name, properties) {
|
||||||
const modal = new ModalClass(properties);
|
let modal;
|
||||||
modals.push(modal);
|
if (typeof name === 'string') {
|
||||||
|
const ModalClass = customElements.get(name);
|
||||||
|
modal = modals_map[name] = new ModalClass(properties);
|
||||||
|
} else {
|
||||||
|
// Legacy...
|
||||||
|
const ModalClass = name;
|
||||||
|
modal = new ModalClass(properties);
|
||||||
|
modals.push(modal);
|
||||||
|
}
|
||||||
return modal;
|
return modal;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a particular modal
|
* Remove a particular modal
|
||||||
* @param { View } modal
|
* @param { String } name
|
||||||
*/
|
*/
|
||||||
remove (modal) {
|
remove (name) {
|
||||||
modals = modals.filter(m => m !== modal);
|
let modal;
|
||||||
modal.remove();
|
if (typeof name === 'string') {
|
||||||
|
modal = modals_map[name];
|
||||||
|
delete modals_map[name];
|
||||||
|
} else {
|
||||||
|
// Legacy...
|
||||||
|
modal = name;
|
||||||
|
modals = modals.filter(m => m !== modal);
|
||||||
|
}
|
||||||
|
modal?.remove();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,6 +86,7 @@ const modal_api = {
|
|||||||
removeAll () {
|
removeAll () {
|
||||||
modals.forEach(m => m.remove());
|
modals.forEach(m => m.remove());
|
||||||
modals = [];
|
modals = [];
|
||||||
|
modals_map = {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -157,7 +181,7 @@ const modal_api = {
|
|||||||
'level': level,
|
'level': level,
|
||||||
'type': 'alert'
|
'type': 'alert'
|
||||||
})
|
})
|
||||||
modal_api.modal.show(Alert, {model});
|
modal_api.modal.show('converse-alert-modal', { model });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import bootstrap from "bootstrap.native";
|
import bootstrap from "bootstrap.native";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import tpl_alert_component from "templates/alert.js";
|
import tpl_alert_component from "./templates/modal-alert.js";
|
||||||
import { View } from '@converse/skeletor/src/view.js';
|
import { View } from '@converse/skeletor/src/view.js';
|
||||||
import { api, converse } from "@converse/headless/core";
|
import { api, converse } from "@converse/headless/core";
|
||||||
import { render } from 'lit';
|
import { render } from 'lit';
|
||||||
|
@ -1,35 +1,32 @@
|
|||||||
import BootstrapModal from './base.js';
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_prompt from "./templates/prompt.js";
|
import tpl_prompt from "./templates/prompt.js";
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
|
import { api } from "@converse/headless/core";
|
||||||
|
|
||||||
|
export default class Confirm extends BaseModal {
|
||||||
|
|
||||||
const Confirm = BootstrapModal.extend({
|
constructor (options) {
|
||||||
id: 'confirm-modal',
|
super(options);
|
||||||
events: {
|
this.confirmation = getOpenPromise();
|
||||||
'submit .confirm': 'onConfimation'
|
}
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
this.confirmation = getOpenPromise();
|
super.initialize();
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
this.listenTo(this.model, 'change', () => this.render())
|
||||||
this.listenTo(this.model, 'change', this.render)
|
this.addEventListener('hide.bs.modal', () => {
|
||||||
this.el.addEventListener('closed.bs.modal', () => this.confirmation.reject(), false);
|
if (!this.confirmation.isResolved) {
|
||||||
},
|
this.confirmation.reject()
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
return tpl_prompt(this.model.toJSON());
|
return tpl_prompt(this);
|
||||||
},
|
}
|
||||||
|
|
||||||
afterRender () {
|
getModalTitle () {
|
||||||
if (!this.close_handler_registered) {
|
this.model.get('title');
|
||||||
this.el.addEventListener('closed.bs.modal', () => {
|
}
|
||||||
if (!this.confirmation.isResolved) {
|
|
||||||
this.confirmation.reject()
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
this.close_handler_registered = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onConfimation (ev) {
|
onConfimation (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@ -53,6 +50,6 @@ const Confirm = BootstrapModal.extend({
|
|||||||
this.confirmation.resolve(fields);
|
this.confirmation.resolve(fields);
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export default Confirm;
|
api.elements.define('converse-confirm-modal', Confirm);
|
||||||
|
70
src/plugins/modal/modal.js
Normal file
70
src/plugins/modal/modal.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import bootstrap from "bootstrap.native";
|
||||||
|
import tpl_modal from './templates/modal.js';
|
||||||
|
import { ElementView } from '@converse/skeletor/src/element.js';
|
||||||
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
|
|
||||||
|
|
||||||
|
import './styles/_modal.scss';
|
||||||
|
|
||||||
|
class BaseModal extends ElementView {
|
||||||
|
|
||||||
|
constructor (options) {
|
||||||
|
super();
|
||||||
|
this.className = 'modal';
|
||||||
|
this.initialized = getOpenPromise();
|
||||||
|
|
||||||
|
// Allow properties to be set via passed in options
|
||||||
|
Object.assign(this, options);
|
||||||
|
setTimeout(() => this.insertIntoDOM());
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
this.modal = new bootstrap.Modal(this, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true
|
||||||
|
});
|
||||||
|
this.addEventListener('hide.bs.modal', () => this.onHide(), false);
|
||||||
|
this.initialized.resolve();
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
toHTML () {
|
||||||
|
return tpl_modal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
// Intended to be overwritten
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
switchTab (ev) {
|
||||||
|
ev?.stopPropagation();
|
||||||
|
ev?.preventDefault();
|
||||||
|
this.tab = ev.target.getAttribute('data-name');
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
onHide () {
|
||||||
|
this.modal.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
insertIntoDOM () {
|
||||||
|
const container_el = document.querySelector("#converse-modals");
|
||||||
|
container_el.insertAdjacentElement('beforeEnd', this);
|
||||||
|
}
|
||||||
|
|
||||||
|
alert (message, type='primary') {
|
||||||
|
this.model.set('alert', { message, type });
|
||||||
|
setTimeout(() => {
|
||||||
|
this.model.set('alert', undefined);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async show () {
|
||||||
|
await this.initialized;
|
||||||
|
this.modal.show();
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BaseModal;
|
@ -55,7 +55,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.fit-content {
|
&.fit-content {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
|
|
||||||
@ -64,12 +63,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modal-body--image {
|
|
||||||
.chat-image {
|
|
||||||
max-height: 99%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
@ -103,43 +96,5 @@
|
|||||||
.btn {
|
.btn {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#user-profile-modal {
|
|
||||||
.profile-form {
|
|
||||||
label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fingerprint-removal {
|
|
||||||
label {
|
|
||||||
display: flex;
|
|
||||||
padding: 0.75rem 1.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: left;
|
|
||||||
font-size: 95%;
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fingerprints {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fingerprint-trust {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
font-size: 95%;
|
|
||||||
.fingerprint {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,8 @@
|
|||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { modal_header_close_button } from "./buttons.js"
|
|
||||||
|
|
||||||
|
|
||||||
export default (o) => html`
|
export default (o) => html`
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-body">
|
||||||
<div class="modal-content">
|
<span class="modal-alert"></span>
|
||||||
<div class="modal-header ${o.level}">
|
${ o.messages.map(message => html`<p>${message}</p>`) }
|
||||||
<h5 class="modal-title">${o.title}</h5>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<span class="modal-alert"></span>
|
|
||||||
${ o.messages.map(message => html`<p>${message}</p>`) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
|
29
src/plugins/modal/templates/modal.js
Normal file
29
src/plugins/modal/templates/modal.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import tpl_alert_component from "./modal-alert.js";
|
||||||
|
import { html } from "lit";
|
||||||
|
import { modal_close_button, modal_header_close_button } from "plugins/modal/templates/buttons.js";
|
||||||
|
|
||||||
|
|
||||||
|
export default (el) => {
|
||||||
|
const alert = el.model?.get('alert');
|
||||||
|
const level = el.model?.get('level') ?? '';
|
||||||
|
return html`
|
||||||
|
<div class="modal-dialog" role="document" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header ${level}">
|
||||||
|
<h5 class="modal-title">${el.getModalTitle()}</h5>
|
||||||
|
${modal_header_close_button}
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<span class="modal-alert">
|
||||||
|
${ alert ? tpl_alert_component({'type': `alert-${alert.type}`, 'message': alert.message}) : ''}
|
||||||
|
</span>
|
||||||
|
${ el.renderModal?.() ?? '' }
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
${ modal_close_button }
|
||||||
|
${ el.renderModalFooter?.() ?? '' }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
@ -15,29 +15,16 @@ const tpl_field = (f) => html`
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export default (el) => {
|
||||||
export default (o) => html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
<form class="converse-form converse-form--modal confirm" action="#" @submit=${ev => el.onConfimation(ev)}>
|
||||||
<div class="modal-content">
|
<div class="form-group">
|
||||||
<div class="modal-header ${o.level || ''}">
|
${ el.model.get('messages')?.map(message => html`<p>${message}</p>`) }
|
||||||
<h5 class="modal-title">${o.title}</h5>
|
</div>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
${ el.model.get('fields')?.map(f => tpl_field(f)) }
|
||||||
<span aria-hidden="true">×</span>
|
<div class="form-group">
|
||||||
</button>
|
<button type="submit" class="btn btn-primary">${__('OK')}</button>
|
||||||
</div>
|
<input type="button" class="btn btn-secondary" data-dismiss="modal" value="${__('Cancel')}"/>
|
||||||
<div class="modal-body">
|
</div>
|
||||||
<span class="modal-alert"></span>
|
</form>`;
|
||||||
<form class="converse-form converse-form--modal confirm" action="#">
|
}
|
||||||
<div class="form-group">
|
|
||||||
${ o.messages.map(message => html`<p>${message}</p>`) }
|
|
||||||
</div>
|
|
||||||
${ o.fields.map(f => tpl_field(f)) }
|
|
||||||
<div class="form-group">
|
|
||||||
<button type="submit" class="btn btn-primary">${__('OK')}</button>
|
|
||||||
<input type="button" class="btn btn-secondary" data-dismiss="modal" value="${__('Cancel')}"/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import MUCInviteModal from './modals/muc-invite.js';
|
import './modals/muc-details.js';
|
||||||
import NicknameModal from './modals/nickname.js';
|
import './modals/muc-invite.js';
|
||||||
import RoomDetailsModal from './modals/muc-details.js';
|
import './modals/nickname.js';
|
||||||
import tpl_muc_head from './templates/muc-head.js';
|
import tpl_muc_head from './templates/muc-head.js';
|
||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
@ -48,12 +48,12 @@ export default class MUCHeading extends CustomElement {
|
|||||||
|
|
||||||
showRoomDetailsModal (ev) {
|
showRoomDetailsModal (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
api.modal.show(RoomDetailsModal, { 'model': this.model }, ev);
|
api.modal.show('converse-muc-details-modal', { 'model': this.model }, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
showInviteModal (ev) {
|
showInviteModal (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
api.modal.show(MUCInviteModal, { 'model': new Model(), 'chatroomview': this }, ev);
|
api.modal.show('converse-muc-invite-modal', { 'model': new Model(), 'chatroomview': this }, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleTopic (ev) {
|
toggleTopic (ev) {
|
||||||
@ -104,7 +104,7 @@ export default class MUCHeading extends CustomElement {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
'i18n_text': __('Nickname'),
|
'i18n_text': __('Nickname'),
|
||||||
'i18n_title': __("Change the nickname you're using in this groupchat"),
|
'i18n_title': __("Change the nickname you're using in this groupchat"),
|
||||||
'handler': ev => api.modal.show(NicknameModal, { 'model': this.model }, ev),
|
'handler': ev => api.modal.show('converse-muc-nickname-modal', { 'model': this.model }, ev),
|
||||||
'a_class': 'open-nickname-modal',
|
'a_class': 'open-nickname-modal',
|
||||||
'icon_class': 'fa-smile',
|
'icon_class': 'fa-smile',
|
||||||
'name': 'nickname'
|
'name': 'nickname'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import tpl_add_muc from "../templates/add-muc.js";
|
import tpl_add_muc from "./templates/add-muc.js";
|
||||||
import BootstrapModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
|
|
||||||
@ -9,43 +9,27 @@ const u = converse.env.utils;
|
|||||||
const { Strophe } = converse.env;
|
const { Strophe } = converse.env;
|
||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default class AddMUCModal extends BaseModal {
|
||||||
persistent: true,
|
|
||||||
id: 'add-chatroom-modal',
|
|
||||||
|
|
||||||
events: {
|
|
||||||
'submit form.add-chatroom': 'openChatRoom',
|
|
||||||
'keyup .roomjid-input': 'checkRoomidPolicy',
|
|
||||||
'change .roomjid-input': 'checkRoomidPolicy'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
super.initialize();
|
||||||
this.listenTo(this.model, 'change:muc_domain', this.render);
|
this.listenTo(this.model, 'change:muc_domain', () => this.render());
|
||||||
this.muc_roomid_policy_error_msg = null;
|
this.muc_roomid_policy_error_msg = null;
|
||||||
},
|
this.render();
|
||||||
|
this.addEventListener('shown.bs.modal', () => {
|
||||||
toHTML () {
|
this.querySelector('input[name="chatroom"]').focus();
|
||||||
let placeholder = '';
|
|
||||||
if (!api.settings.get('locked_muc_domain')) {
|
|
||||||
const muc_domain = this.model.get('muc_domain') || api.settings.get('muc_domain');
|
|
||||||
placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
|
|
||||||
}
|
|
||||||
return tpl_add_muc(Object.assign(this.model.toJSON(), {
|
|
||||||
'label_room_address': api.settings.get('muc_domain') ? __('Groupchat name') : __('Groupchat address'),
|
|
||||||
'chatroom_placeholder': placeholder,
|
|
||||||
'muc_roomid_policy_error_msg': this.muc_roomid_policy_error_msg,
|
|
||||||
'muc_roomid_policy_hint': api.settings.get('muc_roomid_policy_hint')
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
afterRender () {
|
|
||||||
this.el.addEventListener('shown.bs.modal', () => {
|
|
||||||
this.el.querySelector('input[name="chatroom"]').focus();
|
|
||||||
}, false);
|
}, false);
|
||||||
},
|
}
|
||||||
|
|
||||||
parseRoomDataFromEvent (form) {
|
renderModal () {
|
||||||
|
return tpl_add_muc(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Enter a new Groupchat');
|
||||||
|
}
|
||||||
|
|
||||||
|
parseRoomDataFromEvent (form) { // eslint-disable-line class-methods-use-this
|
||||||
const data = new FormData(form);
|
const data = new FormData(form);
|
||||||
const jid = data.get('chatroom')?.trim();
|
const jid = data.get('chatroom')?.trim();
|
||||||
let nick;
|
let nick;
|
||||||
@ -61,10 +45,12 @@ export default BootstrapModal.extend({
|
|||||||
'jid': jid,
|
'jid': jid,
|
||||||
'nick': nick
|
'nick': nick
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
openChatRoom (ev) {
|
openChatRoom (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
if (this.checkRoomidPolicy()) return;
|
||||||
|
|
||||||
const data = this.parseRoomDataFromEvent(ev.target);
|
const data = this.parseRoomDataFromEvent(ev.target);
|
||||||
if (data.nick === "") {
|
if (data.nick === "") {
|
||||||
// Make sure defaults apply if no nick is provided.
|
// Make sure defaults apply if no nick is provided.
|
||||||
@ -77,14 +63,15 @@ export default BootstrapModal.extend({
|
|||||||
jid = data.jid
|
jid = data.jid
|
||||||
this.model.setDomain(jid);
|
this.model.setDomain(jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
api.rooms.open(jid, Object.assign(data, {jid}), true);
|
api.rooms.open(jid, Object.assign(data, {jid}), true);
|
||||||
this.modal.hide();
|
|
||||||
ev.target.reset();
|
ev.target.reset();
|
||||||
},
|
this.modal.hide();
|
||||||
|
}
|
||||||
|
|
||||||
checkRoomidPolicy () {
|
checkRoomidPolicy () {
|
||||||
if (api.settings.get('muc_roomid_policy') && api.settings.get('muc_domain')) {
|
if (api.settings.get('muc_roomid_policy') && api.settings.get('muc_domain')) {
|
||||||
let jid = this.el.querySelector('.roomjid-input').value;
|
let jid = this.querySelector('converse-autocomplete input').value;
|
||||||
if (api.settings.get('locked_muc_domain') || !u.isValidJID(jid)) {
|
if (api.settings.get('locked_muc_domain') || !u.isValidJID(jid)) {
|
||||||
jid = `${Strophe.escapeNode(jid)}@${api.settings.get('muc_domain')}`;
|
jid = `${Strophe.escapeNode(jid)}@${api.settings.get('muc_domain')}`;
|
||||||
}
|
}
|
||||||
@ -95,8 +82,11 @@ export default BootstrapModal.extend({
|
|||||||
this.muc_roomid_policy_error_msg = null;
|
this.muc_roomid_policy_error_msg = null;
|
||||||
} else {
|
} else {
|
||||||
this.muc_roomid_policy_error_msg = __('Groupchat id is invalid.');
|
this.muc_roomid_policy_error_msg = __('Groupchat id is invalid.');
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-add-muc-modal', AddMUCModal);
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
import '../modtools.js';
|
import '../modtools.js';
|
||||||
import BaseModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_moderator_tools from './templates/moderator-tools.js';
|
import { __ } from 'i18n';
|
||||||
|
import { api } from "@converse/headless/core";
|
||||||
|
import { html } from 'lit';
|
||||||
|
|
||||||
const ModeratorToolsModal = BaseModal.extend({
|
export default class ModeratorToolsModal extends BaseModal {
|
||||||
id: "converse-modtools-modal",
|
|
||||||
persistent: true,
|
|
||||||
|
|
||||||
initialize (attrs) {
|
constructor (options) {
|
||||||
this.jid = attrs.jid;
|
super(options);
|
||||||
this.affiliation = attrs.affiliation;
|
this.id = "converse-modtools-modal";
|
||||||
BaseModal.prototype.initialize.apply(this, arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
return tpl_moderator_tools(this);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
export default ModeratorToolsModal;
|
renderModal () {
|
||||||
|
return html`<converse-modtools jid=${this.jid} affiliation=${this.affiliation}></converse-modtools>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Moderator Tools');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-modtools-modal', ModeratorToolsModal);
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
import BaseModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_muc_details from "./templates/muc-details.js";
|
import tpl_muc_details from "./templates/muc-details.js";
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import { api } from "@converse/headless/core";
|
||||||
|
|
||||||
import '../styles/muc-details.scss';
|
import '../styles/muc-details-modal.scss';
|
||||||
|
|
||||||
|
|
||||||
export default BaseModal.extend({
|
export default class MUCDetailsModal extends BaseModal {
|
||||||
id: "muc-details-modal",
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BaseModal.prototype.initialize.apply(this, arguments);
|
super.initialize();
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(this.model, 'change', () => this.render());
|
||||||
this.listenTo(this.model.features, 'change', this.render);
|
this.listenTo(this.model.features, 'change', () => this.render());
|
||||||
this.listenTo(this.model.occupants, 'add', this.render);
|
this.listenTo(this.model.occupants, 'add', () => this.render());
|
||||||
this.listenTo(this.model.occupants, 'change', this.render);
|
this.listenTo(this.model.occupants, 'change', () => this.render());
|
||||||
},
|
}
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
return tpl_muc_details(this.model);
|
return tpl_muc_details(this.model);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Groupchat info for %1$s', this.model.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-muc-details-modal', MUCDetailsModal);
|
||||||
|
@ -1,45 +1,35 @@
|
|||||||
import 'shared/autocomplete/index.js';
|
import 'shared/autocomplete/index.js';
|
||||||
import BaseModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_muc_invite_modal from "./templates/muc-invite.js";
|
import tpl_muc_invite_modal from "./templates/muc-invite.js";
|
||||||
import { _converse, converse } from "@converse/headless/core";
|
import { __ } from 'i18n';
|
||||||
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
export default class MUCInviteModal extends BaseModal {
|
||||||
export default BaseModal.extend({
|
|
||||||
id: "muc-invite-modal",
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BaseModal.prototype.initialize.apply(this, arguments);
|
super.initialize();
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(this.model, 'change', () => this.render());
|
||||||
this.initInviteWidget();
|
}
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
return tpl_muc_invite_modal(Object.assign(
|
return tpl_muc_invite_modal(this);
|
||||||
this.model.toJSON(), {
|
}
|
||||||
'submitInviteForm': ev => this.submitInviteForm(ev)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
initInviteWidget () {
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
if (this.invite_auto_complete) {
|
return __('Invite someone to this groupchat');
|
||||||
this.invite_auto_complete.destroy();
|
}
|
||||||
}
|
|
||||||
const list = _converse.roster.map(i => ({'label': i.getDisplayName(), 'value': i.get('jid')}));
|
getAutoCompleteList () { // eslint-disable-line class-methods-use-this
|
||||||
const el = this.el.querySelector('.suggestion-box').parentElement;
|
return _converse.roster.map(i => ({'label': i.getDisplayName(), 'value': i.get('jid')}));
|
||||||
this.invite_auto_complete = new _converse.AutoComplete(el, {
|
}
|
||||||
'min_chars': 1,
|
|
||||||
'list': list
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
submitInviteForm (ev) {
|
submitInviteForm (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
// TODO: Add support for sending an invite to multiple JIDs
|
// TODO: Add support for sending an invite to multiple JIDs
|
||||||
const data = new FormData(ev.target);
|
const data = new FormData(ev.target);
|
||||||
const jid = data.get('invitee_jids');
|
const jid = data.get('invitee_jids')?.trim();
|
||||||
const reason = data.get('reason');
|
const reason = data.get('reason');
|
||||||
if (u.isValidJID(jid)) {
|
if (u.isValidJID(jid)) {
|
||||||
// TODO: Create and use API here
|
// TODO: Create and use API here
|
||||||
@ -49,4 +39,6 @@ export default BaseModal.extend({
|
|||||||
this.model.set({'invalid_invite_jid': true});
|
this.model.set({'invalid_invite_jid': true});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-muc-invite-modal', MUCInviteModal);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import BootstrapModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import head from "lodash-es/head";
|
import head from "lodash-es/head";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import tpl_muc_list from "../templates/muc-list.js";
|
|
||||||
import tpl_muc_description from "../templates/muc-description.js";
|
import tpl_muc_description from "../templates/muc-description.js";
|
||||||
|
import tpl_muc_list from "../templates/muc-list.js";
|
||||||
import tpl_spinner from "templates/spinner.js";
|
import tpl_spinner from "templates/spinner.js";
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
@ -65,28 +65,25 @@ function toggleRoomInfo (ev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default class MUCListModal extends BaseModal {
|
||||||
id: "muc-list-modal",
|
|
||||||
persistent: true,
|
|
||||||
|
|
||||||
initialize () {
|
constructor (options) {
|
||||||
|
super(options);
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.loading_items = false;
|
this.loading_items = false;
|
||||||
|
}
|
||||||
|
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
initialize () {
|
||||||
|
super.initialize();
|
||||||
this.listenTo(this.model, 'change:muc_domain', this.onDomainChange);
|
this.listenTo(this.model, 'change:muc_domain', this.onDomainChange);
|
||||||
this.listenTo(this.model, 'change:feedback_text', () => this.render());
|
this.listenTo(this.model, 'change:feedback_text', () => this.render());
|
||||||
|
|
||||||
|
this.addEventListener('shown.bs.modal', () => api.settings.get('locked_muc_domain') && this.updateRoomsList());
|
||||||
this.el.addEventListener('shown.bs.modal', () => api.settings.get('locked_muc_domain')
|
|
||||||
? this.updateRoomsList()
|
|
||||||
: this.el.querySelector('input[name="server"]').focus()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.model.save('feedback_text', '');
|
this.model.save('feedback_text', '');
|
||||||
},
|
}
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
return tpl_muc_list(
|
return tpl_muc_list(
|
||||||
Object.assign(this.model.toJSON(), {
|
Object.assign(this.model.toJSON(), {
|
||||||
'show_form': !api.settings.get('locked_muc_domain'),
|
'show_form': !api.settings.get('locked_muc_domain'),
|
||||||
@ -98,7 +95,11 @@ export default BootstrapModal.extend({
|
|||||||
'submitForm': ev => this.showRooms(ev),
|
'submitForm': ev => this.showRooms(ev),
|
||||||
'toggleRoomInfo': ev => this.toggleRoomInfo(ev)
|
'toggleRoomInfo': ev => this.toggleRoomInfo(ev)
|
||||||
}));
|
}));
|
||||||
},
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Query for Groupchats');
|
||||||
|
}
|
||||||
|
|
||||||
openRoom (ev) {
|
openRoom (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@ -106,16 +107,16 @@ export default BootstrapModal.extend({
|
|||||||
const name = ev.target.getAttribute('data-room-name');
|
const name = ev.target.getAttribute('data-room-name');
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
api.rooms.open(jid, {'name': name}, true);
|
api.rooms.open(jid, {'name': name}, true);
|
||||||
},
|
}
|
||||||
|
|
||||||
toggleRoomInfo (ev) {
|
toggleRoomInfo (ev) { // eslint-disable-line
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
toggleRoomInfo(ev);
|
toggleRoomInfo(ev);
|
||||||
},
|
}
|
||||||
|
|
||||||
onDomainChange () {
|
onDomainChange () {
|
||||||
api.settings.get('auto_list_rooms') && this.updateRoomsList();
|
api.settings.get('auto_list_rooms') && this.updateRoomsList();
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the IQ stanza returned from the server, containing
|
* Handle the IQ stanza returned from the server, containing
|
||||||
@ -136,7 +137,7 @@ export default BootstrapModal.extend({
|
|||||||
}
|
}
|
||||||
this.render();
|
this.render();
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an IQ stanza to the server asking for all groupchats
|
* Send an IQ stanza to the server asking for all groupchats
|
||||||
@ -152,7 +153,7 @@ export default BootstrapModal.extend({
|
|||||||
api.sendIQ(iq)
|
api.sendIQ(iq)
|
||||||
.then(iq => this.onRoomsFound(iq))
|
.then(iq => this.onRoomsFound(iq))
|
||||||
.catch(() => this.onRoomsFound())
|
.catch(() => this.onRoomsFound())
|
||||||
},
|
}
|
||||||
|
|
||||||
showRooms (ev) {
|
showRooms (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@ -162,13 +163,15 @@ export default BootstrapModal.extend({
|
|||||||
const data = new FormData(ev.target);
|
const data = new FormData(ev.target);
|
||||||
this.model.setDomain(data.get('server'));
|
this.model.setDomain(data.get('server'));
|
||||||
this.updateRoomsList();
|
this.updateRoomsList();
|
||||||
},
|
}
|
||||||
|
|
||||||
setDomainFromEvent (ev) {
|
setDomainFromEvent (ev) {
|
||||||
this.model.setDomain(ev.target.value);
|
this.model.setDomain(ev.target.value);
|
||||||
},
|
}
|
||||||
|
|
||||||
setNick (ev) {
|
setNick (ev) {
|
||||||
this.model.save({nick: ev.target.value});
|
this.model.save({nick: ev.target.value});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-muc-list-modal', MUCListModal);
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import tpl_nickname from "./templates/nickname.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import BaseModal from "plugins/modal/base.js";
|
import { __ } from 'i18n';
|
||||||
|
import { api } from "@converse/headless/core.js";
|
||||||
|
import { html } from 'lit';
|
||||||
|
|
||||||
export default BaseModal.extend({
|
export default class MUCNicknameModal extends BaseModal {
|
||||||
id: 'change-nickname-modal',
|
|
||||||
|
|
||||||
initialize (attrs) {
|
renderModal () {
|
||||||
this.model = attrs.model;
|
return html`<converse-muc-nickname-form jid="${this.model.get('jid')}"></converse-muc-nickname-form>`;
|
||||||
BaseModal.prototype.initialize.apply(this, arguments);
|
}
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
return tpl_nickname(this);
|
return __('Change your nickname');
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-muc-nickname-modal', MUCNicknameModal);
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import BaseModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_occupant_modal from "./templates/occupant.js";
|
import tpl_occupant_modal from "./templates/occupant.js";
|
||||||
import { _converse, api } from "@converse/headless/core";
|
import { _converse, api } from "@converse/headless/core";
|
||||||
|
|
||||||
|
|
||||||
const OccupantModal = BaseModal.extend({
|
export default class OccupantModal extends BaseModal {
|
||||||
id: "muc-occupant",
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BaseModal.prototype.initialize.apply(this, arguments);
|
super.initialize()
|
||||||
if (this.model) {
|
const model = this.model ?? this.message;
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(model, 'change', () => this.render());
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Triggered once the OccupantModal has been initialized
|
* Triggered once the OccupantModal has been initialized
|
||||||
* @event _converse#occupantModalInitialized
|
* @event _converse#occupantModalInitialized
|
||||||
@ -18,7 +16,7 @@ const OccupantModal = BaseModal.extend({
|
|||||||
* @example _converse.api.listen.on('occupantModalInitialized', data);
|
* @example _converse.api.listen.on('occupantModalInitialized', data);
|
||||||
*/
|
*/
|
||||||
api.trigger('occupantModalInitialized', { 'model': this.model, 'message': this.message });
|
api.trigger('occupantModalInitialized', { 'model': this.model, 'message': this.message });
|
||||||
},
|
}
|
||||||
|
|
||||||
getVcard () {
|
getVcard () {
|
||||||
const model = this.model ?? this.message;
|
const model = this.model ?? this.message;
|
||||||
@ -27,22 +25,24 @@ const OccupantModal = BaseModal.extend({
|
|||||||
}
|
}
|
||||||
const jid = model?.get('jid') || model?.get('from');
|
const jid = model?.get('jid') || model?.get('from');
|
||||||
return jid ? _converse.vcards.get(jid) : null;
|
return jid ? _converse.vcards.get(jid) : null;
|
||||||
},
|
}
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
const model = this.model ?? this.message;
|
const model = this.model ?? this.message;
|
||||||
const jid = model?.get('jid');
|
const jid = model?.get('jid');
|
||||||
const vcard = this.getVcard();
|
const vcard = this.getVcard();
|
||||||
const display_name = model?.getDisplayName();
|
|
||||||
const nick = model.get('nick');
|
const nick = model.get('nick');
|
||||||
const occupant_id = model.get('occupant_id');
|
const occupant_id = model.get('occupant_id');
|
||||||
const role = this.model?.get('role');
|
const role = this.model?.get('role');
|
||||||
const affiliation = this.model?.get('affiliation');
|
const affiliation = this.model?.get('affiliation');
|
||||||
const hats = this.model?.get('hats')?.length ? this.model.get('hats') : null;
|
const hats = this.model?.get('hats')?.length ? this.model.get('hats') : null;
|
||||||
return tpl_occupant_modal({ jid, vcard, display_name, nick, occupant_id, role, affiliation, hats });
|
return tpl_occupant_modal({ jid, vcard, nick, occupant_id, role, affiliation, hats });
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
_converse.OccupantModal = OccupantModal;
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
const model = this.model ?? this.message;
|
||||||
|
return model?.getDisplayName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default OccupantModal;
|
api.elements.define('converse-muc-occupant-modal', OccupantModal);
|
||||||
|
57
src/plugins/muc-views/modals/templates/add-muc.js
Normal file
57
src/plugins/muc-views/modals/templates/add-muc.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import { api } from '@converse/headless/core.js';
|
||||||
|
import { html } from "lit";
|
||||||
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||||
|
import { getAutoCompleteList } from "../../search.js";
|
||||||
|
|
||||||
|
|
||||||
|
const nickname_input = (el) => {
|
||||||
|
const i18n_nickname = __('Nickname');
|
||||||
|
const i18n_required_field = __('This field is required');
|
||||||
|
return html`
|
||||||
|
<div class="form-group" >
|
||||||
|
<label for="nickname">${i18n_nickname}:</label>
|
||||||
|
<input type="text"
|
||||||
|
title="${i18n_required_field}"
|
||||||
|
required="required"
|
||||||
|
name="nickname"
|
||||||
|
value="${el.model.get('nick') || ''}"
|
||||||
|
class="form-control"/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (el) => {
|
||||||
|
const i18n_join = __('Join');
|
||||||
|
const muc_domain = el.model.get('muc_domain') || api.settings.get('muc_domain');
|
||||||
|
|
||||||
|
let placeholder = '';
|
||||||
|
if (!api.settings.get('locked_muc_domain')) {
|
||||||
|
placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
|
||||||
|
}
|
||||||
|
|
||||||
|
const label_room_address = muc_domain ? __('Groupchat name') : __('Groupchat address');
|
||||||
|
const muc_roomid_policy_error_msg = el.muc_roomid_policy_error_msg;
|
||||||
|
const muc_roomid_policy_hint = api.settings.get('muc_roomid_policy_hint');
|
||||||
|
return html`
|
||||||
|
<form class="converse-form add-chatroom" @submit=${(ev) => el.openChatRoom(ev)}>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="chatroom">${label_room_address}:</label>
|
||||||
|
${ (muc_roomid_policy_error_msg) ? html`<label class="roomid-policy-error">${muc_roomid_policy_error_msg}</label>` : '' }
|
||||||
|
<converse-autocomplete
|
||||||
|
.getAutoCompleteList=${getAutoCompleteList}
|
||||||
|
?autofocus=${true}
|
||||||
|
min_chars="3"
|
||||||
|
position="below"
|
||||||
|
placeholder="${placeholder}"
|
||||||
|
class="add-muc-autocomplete"
|
||||||
|
name="chatroom">
|
||||||
|
</converse-autocomplete>
|
||||||
|
</div>
|
||||||
|
${ muc_roomid_policy_hint ? html`<div class="form-group">${unsafeHTML(DOMPurify.sanitize(muc_roomid_policy_hint, {'ALLOWED_TAGS': ['b', 'br', 'em']}))}</div>` : '' }
|
||||||
|
${ !api.settings.get('locked_muc_nickname') ? nickname_input(el) : '' }
|
||||||
|
<input type="submit" class="btn btn-primary" name="join" value="${i18n_join || ''}" ?disabled=${muc_roomid_policy_error_msg}/>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
import { __ } from 'i18n';
|
|
||||||
import { html } from "lit";
|
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
export default (o) => {
|
|
||||||
const i18n_moderator_tools = __('Moderator Tools');
|
|
||||||
return html`
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="converse-modtools-modal-label">${i18n_moderator_tools}</h5>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body d-flex flex-column">
|
|
||||||
<converse-modtools jid=${o.jid} affiliation=${o.affiliation}></converse-modtools>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { modal_close_button, modal_header_close_button } from "plugins/modal/templates/buttons.js";
|
|
||||||
|
|
||||||
|
|
||||||
const subject = (o) => {
|
const subject = (o) => {
|
||||||
@ -16,7 +15,6 @@ const subject = (o) => {
|
|||||||
export default (model) => {
|
export default (model) => {
|
||||||
const o = model.toJSON();
|
const o = model.toJSON();
|
||||||
const config = model.config.toJSON();
|
const config = model.config.toJSON();
|
||||||
const display_name = __('Groupchat info for %1$s', model.getDisplayName());
|
|
||||||
const features = model.features.toJSON();
|
const features = model.features.toJSON();
|
||||||
const num_occupants = model.occupants.filter(o => o.get('show') !== 'offline').length;
|
const num_occupants = model.occupants.filter(o => o.get('show') !== 'offline').length;
|
||||||
|
|
||||||
@ -51,43 +49,30 @@ export default (model) => {
|
|||||||
const i18n_temporary = __('Temporary');
|
const i18n_temporary = __('Temporary');
|
||||||
const i18n_temporary_help = __('This groupchat will disappear once the last person leaves');
|
const i18n_temporary_help = __('This groupchat will disappear once the last person leaves');
|
||||||
return html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
<div class="room-info">
|
||||||
<div class="modal-content">
|
<p class="room-info"><strong>${i18n_name}</strong>: ${o.name}</p>
|
||||||
<div class="modal-header">
|
<p class="room-info"><strong>${i18n_address}</strong>: <converse-rich-text text="xmpp:${o.jid}?join"></converse-rich-text></p>
|
||||||
<h5 class="modal-title" id="muc-details-modal-label">${display_name}</h5>
|
<p class="room-info"><strong>${i18n_desc}</strong>: <converse-rich-text text="${config.description}" render_styling></converse-rich-text></p>
|
||||||
${modal_header_close_button}
|
${ (o.subject) ? subject(o) : '' }
|
||||||
|
<p class="room-info"><strong>${i18n_online_users}</strong>: ${num_occupants}</p>
|
||||||
|
<p class="room-info"><strong>${i18n_features}</strong>:
|
||||||
|
<div class="chatroom-features">
|
||||||
|
<ul class="features-list">
|
||||||
|
${ features.passwordprotected ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-lock"></converse-icon>${i18n_password_protected} - <em>${i18n_password_help}</em></li>` : '' }
|
||||||
|
${ features.unsecured ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-unlock"></converse-icon>${i18n_no_password_required} - <em>${i18n_no_pass_help}</em></li>` : '' }
|
||||||
|
${ features.hidden ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-eye-slash"></converse-icon>${i18n_hidden} - <em>${i18n_hidden_help}</em></li>` : '' }
|
||||||
|
${ features.public_room ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-eye"></converse-icon>${i18n_public} - <em>${o.__('This groupchat is publicly searchable') }</em></li>` : '' }
|
||||||
|
${ features.membersonly ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-address-book"></converse-icon>${i18n_members_only} - <em>${i18n_members_help}</em></li>` : '' }
|
||||||
|
${ features.open ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-globe"></converse-icon>${i18n_open} - <em>${i18n_open_help}</em></li>` : '' }
|
||||||
|
${ features.persistent ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-save"></converse-icon>${i18n_persistent} - <em>${i18n_persistent_help}</em></li>` : '' }
|
||||||
|
${ features.temporary ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-snowflake"></converse-icon>${i18n_temporary} - <em>${i18n_temporary_help}</em></li>` : '' }
|
||||||
|
${ features.nonanonymous ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-id-card"></converse-icon>${i18n_not_anonymous} - <em>${i18n_not_anonymous_help}</em></li>` : '' }
|
||||||
|
${ features.semianonymous ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-user-secret"></converse-icon>${i18n_semi_anon} - <em>${i18n_semi_anon_help}</em></li>` : '' }
|
||||||
|
${ features.moderated ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-gavel"></converse-icon>${i18n_moderated} - <em>${i18n_moderated_help}</em></li>` : '' }
|
||||||
|
${ features.unmoderated ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-info-circle"></converse-icon>${i18n_not_moderated} - <em>${i18n_not_moderated_help}</em></li>` : '' }
|
||||||
|
${ features.mam_enabled ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-database"></converse-icon>${i18n_archiving} - <em>${i18n_archiving_help}</em></li>` : '' }
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
</p>
|
||||||
<span class="modal-alert"></span>
|
|
||||||
<div class="room-info">
|
|
||||||
<p class="room-info"><strong>${i18n_name}</strong>: ${o.name}</p>
|
|
||||||
<p class="room-info"><strong>${i18n_address}</strong>: <converse-rich-text text="xmpp:${o.jid}?join"></converse-rich-text></p>
|
|
||||||
<p class="room-info"><strong>${i18n_desc}</strong>: <converse-rich-text text="${config.description}" render_styling></converse-rich-text></p>
|
|
||||||
${ (o.subject) ? subject(o) : '' }
|
|
||||||
<p class="room-info"><strong>${i18n_online_users}</strong>: ${num_occupants}</p>
|
|
||||||
<p class="room-info"><strong>${i18n_features}</strong>:
|
|
||||||
<div class="chatroom-features">
|
|
||||||
<ul class="features-list">
|
|
||||||
${ features.passwordprotected ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-lock"></converse-icon>${i18n_password_protected} - <em>${i18n_password_help}</em></li>` : '' }
|
|
||||||
${ features.unsecured ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-unlock"></converse-icon>${i18n_no_password_required} - <em>${i18n_no_pass_help}</em></li>` : '' }
|
|
||||||
${ features.hidden ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-eye-slash"></converse-icon>${i18n_hidden} - <em>${i18n_hidden_help}</em></li>` : '' }
|
|
||||||
${ features.public_room ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-eye"></converse-icon>${i18n_public} - <em>${o.__('This groupchat is publicly searchable') }</em></li>` : '' }
|
|
||||||
${ features.membersonly ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-address-book"></converse-icon>${i18n_members_only} - <em>${i18n_members_help}</em></li>` : '' }
|
|
||||||
${ features.open ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-globe"></converse-icon>${i18n_open} - <em>${i18n_open_help}</em></li>` : '' }
|
|
||||||
${ features.persistent ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-save"></converse-icon>${i18n_persistent} - <em>${i18n_persistent_help}</em></li>` : '' }
|
|
||||||
${ features.temporary ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-snowflake-o"></converse-icon>${i18n_temporary} - <em>${i18n_temporary_help}</em></li>` : '' }
|
|
||||||
${ features.nonanonymous ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-id-card"></converse-icon>${i18n_not_anonymous} - <em>${i18n_not_anonymous_help}</em></li>` : '' }
|
|
||||||
${ features.semianonymous ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-user-secret"></converse-icon>${i18n_semi_anon} - <em>${i18n_semi_anon_help}</em></li>` : '' }
|
|
||||||
${ features.moderated ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-gavel"></converse-icon>${i18n_moderated} - <em>${i18n_moderated_help}</em></li>` : '' }
|
|
||||||
${ features.unmoderated ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-info-circle"></converse-icon>${i18n_not_moderated} - <em>${i18n_not_moderated_help}</em></li>` : '' }
|
|
||||||
${ features.mam_enabled ? html`<li class="feature" ><converse-icon size="1em" class="fa fa-database"></converse-icon>${i18n_archiving} - <em>${i18n_archiving_help}</em></li>` : '' }
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">${modal_close_button}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,35 @@
|
|||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
|
export default (el) => {
|
||||||
export default (o) => {
|
|
||||||
const i18n_invite = __('Invite');
|
const i18n_invite = __('Invite');
|
||||||
const i18n_invite_heading = __('Invite someone to this groupchat');
|
|
||||||
const i18n_jid_placeholder = __('user@example.org');
|
const i18n_jid_placeholder = __('user@example.org');
|
||||||
const i18n_error_message = __('Please enter a valid XMPP address');
|
const i18n_error_message = __('Please enter a valid XMPP address');
|
||||||
const i18n_invite_label = __('XMPP Address');
|
const i18n_invite_label = __('XMPP Address');
|
||||||
const i18n_reason = __('Optional reason for the invitation');
|
const i18n_reason = __('Optional reason for the invitation');
|
||||||
return html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
<form class="converse-form" @submit=${(ev) => el.submitInviteForm(ev)}>
|
||||||
<div class="modal-content">
|
<div class="form-group">
|
||||||
<div class="modal-header">
|
<label class="clearfix" for="invitee_jids">${i18n_invite_label}:</label>
|
||||||
<h5 class="modal-title" id="add-chatroom-modal-label">${i18n_invite_heading}</h5>
|
${ el.model.get('invalid_invite_jid') ? html`<div class="error error-feedback">${i18n_error_message}</div>` : '' }
|
||||||
${modal_header_close_button}
|
<converse-autocomplete
|
||||||
</div>
|
.getAutoCompleteList=${() => el.getAutoCompleteList()}
|
||||||
<div class="modal-body">
|
?autofocus=${true}
|
||||||
<span class="modal-alert"></span>
|
min_chars="1"
|
||||||
<div class="suggestion-box room-invite">
|
position="below"
|
||||||
<form @submit=${o.submitInviteForm}>
|
required="required"
|
||||||
<div class="form-group">
|
name="invitee_jids"
|
||||||
<label class="clearfix" for="invitee_jids">${i18n_invite_label}:</label>
|
id="invitee_jids"
|
||||||
${ o.invalid_invite_jid ? html`<div class="error error-feedback">${i18n_error_message}</div>` : '' }
|
placeholder="${i18n_jid_placeholder}">
|
||||||
<input class="form-control suggestion-box__input"
|
</converse-autocomplete>
|
||||||
required="required"
|
|
||||||
name="invitee_jids"
|
|
||||||
id="invitee_jids"
|
|
||||||
placeholder="${i18n_jid_placeholder}"
|
|
||||||
type="text"/>
|
|
||||||
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
|
||||||
<ul class="suggestion-box__results suggestion-box__results--below" hidden=""></ul>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>${i18n_reason}:</label>
|
|
||||||
<textarea class="form-control" name="reason"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<button type="submit" class="btn btn-primary">${i18n_invite}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
|
<label>${i18n_reason}:</label>
|
||||||
|
<textarea class="form-control" name="reason"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="submit" class="btn btn-primary" value="${i18n_invite}"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import { __ } from 'i18n';
|
|
||||||
import { html } from "lit";
|
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
export default (modal) => {
|
|
||||||
const jid = modal.model.get('jid');
|
|
||||||
const i18n_heading = __('Change your nickname');
|
|
||||||
return html`
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="converse-modtools-modal-label">
|
|
||||||
${i18n_heading}</h5>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body d-flex flex-column">
|
|
||||||
<converse-muc-nickname-form jid="${jid}"></converse-muc-nickname-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
@ -1,53 +1,39 @@
|
|||||||
import 'shared/avatar/avatar.js';
|
import 'shared/avatar/avatar.js';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { modal_close_button, modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
export default (o) => {
|
||||||
return html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
<div class="row">
|
||||||
<div class="modal-content">
|
<div class="col-auto">
|
||||||
<div class="modal-header">
|
<converse-avatar
|
||||||
<h5 class="modal-title" id="user-details-modal-label">${o.display_name}</h5>
|
class="avatar modal-avatar"
|
||||||
${modal_header_close_button}
|
.data=${o.vcard?.attributes}
|
||||||
</div>
|
nonce=${o.vcard?.get('vcard_updated')}
|
||||||
<div class="modal-body" class="d-flex">
|
height="120" width="120"></converse-avatar>
|
||||||
<div class="row">
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col">
|
||||||
<converse-avatar
|
<ul class="occupant-details">
|
||||||
class="avatar modal-avatar"
|
<li>
|
||||||
.data=${o.vcard?.attributes}
|
${ o.nick ? html`<div class="row"><strong>${__('Nickname')}:</strong></div><div class="row">${o.nick}</div>` : '' }
|
||||||
nonce=${o.vcard?.get('vcard_updated')}
|
</li>
|
||||||
height="120" width="120"></converse-avatar>
|
<li>
|
||||||
</div>
|
${ o.jid ? html`<div class="row"><strong>${__('XMPP Address')}:</strong></div><div class="row">${o.jid}</div>` : '' }
|
||||||
<div class="col">
|
</li>
|
||||||
<ul class="occupant-details">
|
<li>
|
||||||
<li>
|
${ o.affiliation ? html`<div class="row"><strong>${__('Affiliation')}:</strong></div><div class="row">${o.affiliation}</div>` : '' }
|
||||||
${ o.nick ? html`<div class="row"><strong>${__('Nickname')}:</strong></div><div class="row">${o.nick}</div>` : '' }
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
${ o.role ? html`<div class="row"><strong>${__('Roles')}:</strong></div><div class="row">${o.role}</div>` : '' }
|
||||||
${ o.jid ? html`<div class="row"><strong>${__('XMPP Address')}:</strong></div><div class="row">${o.jid}</div>` : '' }
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
${ o.hats ? html`<div class="row"><strong>${__('Hats')}:</strong></div><div class="row">${o.hats}</div>` : '' }
|
||||||
${ o.affiliation ? html`<div class="row"><strong>${__('Affiliation')}:</strong></div><div class="row">${o.affiliation}</div>` : '' }
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
${ o.occupant_id ? html`<div class="row"><strong>${__('Occupant Id')}:</strong></div><div class="row">${o.occupant_id}</div>` : '' }
|
||||||
${ o.role ? html`<div class="row"><strong>${__('Roles')}:</strong></div><div class="row">${o.role}</div>` : '' }
|
</li>
|
||||||
</li>
|
</ul>
|
||||||
<li>
|
|
||||||
${ o.hats ? html`<div class="row"><strong>${__('Hats')}:</strong></div><div class="row">${o.hats}</div>` : '' }
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
${ o.occupant_id ? html`<div class="row"><strong>${__('Occupant Id')}:</strong></div><div class="row">${o.occupant_id}</div>` : '' }
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
${modal_close_button}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -25,6 +25,7 @@ export default class ModeratorTools extends CustomElement {
|
|||||||
muc: { type: Object, attribute: false },
|
muc: { type: Object, attribute: false },
|
||||||
role: { type: String },
|
role: { type: String },
|
||||||
roles_filter: { type: String, attribute: false },
|
roles_filter: { type: String, attribute: false },
|
||||||
|
tab: { type: String },
|
||||||
users_with_affiliation: { type: Array, attribute: false },
|
users_with_affiliation: { type: Array, attribute: false },
|
||||||
users_with_role: { type: Array, attribute: false },
|
users_with_role: { type: Array, attribute: false },
|
||||||
};
|
};
|
||||||
@ -32,6 +33,7 @@ export default class ModeratorTools extends CustomElement {
|
|||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
super();
|
super();
|
||||||
|
this.tab = 'affiliations';
|
||||||
this.affiliation = '';
|
this.affiliation = '';
|
||||||
this.affiliations_filter = '';
|
this.affiliations_filter = '';
|
||||||
this.role = '';
|
this.role = '';
|
||||||
@ -72,6 +74,7 @@ export default class ModeratorTools extends CustomElement {
|
|||||||
'queryable_roles': ROLES.filter(a => !api.settings.get('modtools_disable_query').includes(a)),
|
'queryable_roles': ROLES.filter(a => !api.settings.get('modtools_disable_query').includes(a)),
|
||||||
'roles_filter': this.roles_filter,
|
'roles_filter': this.roles_filter,
|
||||||
'switchTab': ev => this.switchTab(ev),
|
'switchTab': ev => this.switchTab(ev),
|
||||||
|
'tab': this.tab,
|
||||||
'toggleForm': ev => this.toggleForm(ev),
|
'toggleForm': ev => this.toggleForm(ev),
|
||||||
'users_with_affiliation': this.users_with_affiliation,
|
'users_with_affiliation': this.users_with_affiliation,
|
||||||
'users_with_role': this.users_with_role,
|
'users_with_role': this.users_with_role,
|
||||||
@ -81,6 +84,13 @@ export default class ModeratorTools extends CustomElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchTab (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
this.tab = ev.target.getAttribute('data-name');
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
async onSearchAffiliationChange () {
|
async onSearchAffiliationChange () {
|
||||||
if (!this.affiliation) {
|
if (!this.affiliation) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
#add-chatroom-modal {
|
converse-add-muc-modal {
|
||||||
converse-autocomplete {
|
.add-chatroom {
|
||||||
.suggestion-box__results--below {
|
converse-autocomplete {
|
||||||
height: 10em;
|
.suggestion-box__results--below {
|
||||||
overflow: auto;
|
height: 10em;
|
||||||
}
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.suggestion-box ul li {
|
.suggestion-box ul li {
|
||||||
display: block;
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
@import "./controlbox.scss";
|
@import "./controlbox.scss";
|
||||||
@import "./muc.scss";
|
@import "./muc.scss";
|
||||||
@import "./muc-details-modal.scss";
|
|
||||||
|
|
||||||
converse-muc-disconnected,
|
converse-muc-disconnected,
|
||||||
converse-muc-destroyed {
|
converse-muc-destroyed {
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
#muc-details-modal {
|
converse-muc-details-modal {
|
||||||
.features-list {
|
.features-list {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.room-info {
|
||||||
|
strong {
|
||||||
|
color: var(--muc-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chatroom-features {
|
.chatroom-features {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.features-list {
|
.features-list {
|
||||||
@ -13,9 +19,8 @@
|
|||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
.fa {
|
converse-icon {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
color: var(--text-color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#muc-details-modal {
|
|
||||||
.room-info {
|
|
||||||
strong {
|
|
||||||
color: var(--muc-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
|||||||
import DOMPurify from 'dompurify';
|
|
||||||
import { __ } from 'i18n';
|
|
||||||
import { api } from '@converse/headless/core.js';
|
|
||||||
import { html } from "lit";
|
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
||||||
import { getAutoCompleteList } from "../search.js";
|
|
||||||
|
|
||||||
|
|
||||||
const nickname_input = (o) => {
|
|
||||||
const i18n_nickname = __('Nickname');
|
|
||||||
const i18n_required_field = __('This field is required');
|
|
||||||
return html`
|
|
||||||
<div class="form-group" >
|
|
||||||
<label for="nickname">${i18n_nickname}:</label>
|
|
||||||
<input type="text" title="${i18n_required_field}" required="required" name="nickname" value="${o.nick || ''}" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
|
||||||
const i18n_join = __('Join');
|
|
||||||
const i18n_enter = __('Enter a new Groupchat');
|
|
||||||
return html`
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="add-chatroom-modal-label">${i18n_enter}</h5>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<span class="modal-alert"></span>
|
|
||||||
<form class="converse-form add-chatroom">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="chatroom">${o.label_room_address}:</label>
|
|
||||||
${ (o.muc_roomid_policy_error_msg) ? html`<label class="roomid-policy-error">${o.muc_roomid_policy_error_msg}</label>` : '' }
|
|
||||||
<converse-autocomplete
|
|
||||||
.getAutoCompleteList=${getAutoCompleteList}
|
|
||||||
?autofocus=${true}
|
|
||||||
min_chars="3"
|
|
||||||
position="below"
|
|
||||||
placeholder="${o.chatroom_placeholder}"
|
|
||||||
class="add-muc-autocomplete"
|
|
||||||
name="chatroom">
|
|
||||||
</converse-autocomplete>
|
|
||||||
</div>
|
|
||||||
${ o.muc_roomid_policy_hint ? html`<div class="form-group">${unsafeHTML(DOMPurify.sanitize(o.muc_roomid_policy_hint, {'ALLOWED_TAGS': ['b', 'br', 'em']}))}</div>` : '' }
|
|
||||||
${ !api.settings.get('locked_muc_nickname') ? nickname_input(o) : '' }
|
|
||||||
<input type="submit" class="btn btn-primary" name="join" value="${i18n_join || ''}" ?disabled=${o.muc_roomid_policy_error_msg}>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
@ -146,13 +146,25 @@ const affiliation_list_item = (o) => html`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
const tpl_navigation = () => html`
|
const tpl_navigation = (o) => html`
|
||||||
<ul class="nav nav-pills justify-content-center">
|
<ul class="nav nav-pills justify-content-center">
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<a class="nav-link active" id="affiliations-tab" href="#affiliations-tabpanel" aria-controls="affiliations-tabpanel" role="tab" data-toggle="tab">Affiliations</a>
|
<a class="nav-link ${o.tab === "affiliations" ? "active" : ""}"
|
||||||
|
id="affiliations-tab"
|
||||||
|
href="#affiliations-tabpanel"
|
||||||
|
aria-controls="affiliations-tabpanel"
|
||||||
|
role="tab"
|
||||||
|
data-name="affiliations"
|
||||||
|
@click=${o.switchTab}>Affiliations</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<a class="nav-link" id="roles-tab" href="#roles-tabpanel" aria-controls="roles-tabpanel" role="tab" data-toggle="tab">Roles</a>
|
<a class="nav-link ${o.tab === "roles" ? "active" : ""}"
|
||||||
|
id="roles-tab"
|
||||||
|
href="#roles-tabpanel"
|
||||||
|
aria-controls="roles-tabpanel"
|
||||||
|
role="tab"
|
||||||
|
data-name="roles"
|
||||||
|
@click=${o.switchTab}>Roles</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
`;
|
`;
|
||||||
@ -178,12 +190,12 @@ export default (o) => {
|
|||||||
const show_both_tabs = o.queryable_roles.length && o.queryable_affiliations.length;
|
const show_both_tabs = o.queryable_roles.length && o.queryable_affiliations.length;
|
||||||
return html`
|
return html`
|
||||||
${o.alert_message ? html`<div class="alert alert-${o.alert_type}" role="alert">${o.alert_message}</div>` : '' }
|
${o.alert_message ? html`<div class="alert alert-${o.alert_type}" role="alert">${o.alert_message}</div>` : '' }
|
||||||
${ show_both_tabs ? tpl_navigation() : '' }
|
${ show_both_tabs ? tpl_navigation(o) : '' }
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
||||||
${ o.queryable_affiliations.length ? html`
|
${ 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">
|
<div class="tab-pane tab-pane--columns ${ o.tab === 'affiliations' ? 'active' : ''}" id="affiliations-tabpanel" role="tabpanel" aria-labelledby="affiliations-tab">
|
||||||
<form class="converse-form query-affiliation" @submit=${o.queryAffiliation}>
|
<form class="converse-form query-affiliation" @submit=${o.queryAffiliation}>
|
||||||
<p class="helptext pb-3">${i18n_helptext_affiliation}</p>
|
<p class="helptext pb-3">${i18n_helptext_affiliation}</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -225,7 +237,7 @@ export default (o) => {
|
|||||||
</div>` : '' }
|
</div>` : '' }
|
||||||
|
|
||||||
${ o.queryable_roles.length ? html`
|
${ 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">
|
<div class="tab-pane tab-pane--columns ${ o.tab === 'roles' ? 'active' : ''}" id="roles-tabpanel" role="tabpanel" aria-labelledby="roles-tab">
|
||||||
<form class="converse-form query-role" @submit=${o.queryRole}>
|
<form class="converse-form query-role" @submit=${o.queryRole}>
|
||||||
<p class="helptext pb-3">${i18n_helptext_role}</p>
|
<p class="helptext pb-3">${i18n_helptext_role}</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { repeat } from 'lit/directives/repeat.js';
|
import { repeat } from 'lit/directives/repeat.js';
|
||||||
import { modal_close_button, modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
import spinner from "templates/spinner.js";
|
import spinner from "templates/spinner.js";
|
||||||
|
|
||||||
|
|
||||||
@ -14,6 +13,7 @@ const form = (o) => {
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="chatroom">${i18n_server_address}:</label>
|
<label for="chatroom">${i18n_server_address}:</label>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
|
autofocus
|
||||||
@change=${o.setDomainFromEvent}
|
@change=${o.setDomainFromEvent}
|
||||||
value="${o.muc_domain || ''}"
|
value="${o.muc_domain || ''}"
|
||||||
required="required"
|
required="required"
|
||||||
@ -34,16 +34,16 @@ const tpl_item = (o, item) => {
|
|||||||
<li class="room-item list-group-item">
|
<li class="room-item list-group-item">
|
||||||
<div class="available-chatroom d-flex flex-row">
|
<div class="available-chatroom d-flex flex-row">
|
||||||
<a class="open-room available-room w-100"
|
<a class="open-room available-room w-100"
|
||||||
@click=${o.openRoom}
|
@click=${o.openRoom}
|
||||||
data-room-jid="${item.jid}"
|
data-room-jid="${item.jid}"
|
||||||
data-room-name="${item.name}"
|
data-room-name="${item.name}"
|
||||||
title="${i18n_open_title}"
|
title="${i18n_open_title}"
|
||||||
href="#">${item.name || item.jid}</a>
|
href="#">${item.name || item.jid}</a>
|
||||||
<a class="right room-info icon-room-info"
|
<a class="right room-info icon-room-info"
|
||||||
@click=${o.toggleRoomInfo}
|
@click=${o.toggleRoomInfo}
|
||||||
data-room-jid="${item.jid}"
|
data-room-jid="${item.jid}"
|
||||||
title="${i18n_info_title}"
|
title="${i18n_info_title}"
|
||||||
href="#"></a>
|
href="#"></a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
@ -51,25 +51,12 @@ const tpl_item = (o, item) => {
|
|||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
export default (o) => {
|
||||||
const i18n_list_chatrooms = __('Query for Groupchats');
|
|
||||||
return html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
${o.show_form ? form(o) : '' }
|
||||||
<div class="modal-content">
|
<ul class="available-chatrooms list-group">
|
||||||
<div class="modal-header">
|
${ o.loading_items ? html`<li class="list-group-item"> ${spinner()} </li>` : '' }
|
||||||
<h5 class="modal-title" id="muc-list-modal-label">${i18n_list_chatrooms}</h5>
|
${ o.feedback_text ? html`<li class="list-group-item active">${ o.feedback_text }</li>` : '' }
|
||||||
${modal_header_close_button}
|
${repeat(o.items, item => item.jid, item => tpl_item(o, item))}
|
||||||
</div>
|
</ul>
|
||||||
<div class="modal-body d-flex flex-column">
|
|
||||||
<span class="modal-alert"></span>
|
|
||||||
${o.show_form ? form(o) : '' }
|
|
||||||
<ul class="available-chatrooms list-group">
|
|
||||||
${ o.loading_items ? html`<li class="list-group-item"> ${spinner()} </li>` : '' }
|
|
||||||
${ o.feedback_text ? html`<li class="list-group-item active">${ o.feedback_text }</li>` : '' }
|
|
||||||
${repeat(o.items, item => item.jid, item => tpl_item(o, item))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">${modal_close_button}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ export default (el) => {
|
|||||||
const validation_message = el.model?.get('nickname_validation_message');
|
const validation_message = el.model?.get('nickname_validation_message');
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="chatroom-form-container muc-nickname-form"
|
<div class="chatroom-form-container muc-nickname-form">
|
||||||
@submit=${ev => el.submitNickname(ev)}>
|
<form class="converse-form chatroom-form converse-centered-form"
|
||||||
<form class="converse-form chatroom-form converse-centered-form">
|
@submit=${ev => el.submitNickname(ev)}>
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<label>${i18n_heading}</label>
|
<label>${i18n_heading}</label>
|
||||||
<p class="validation-message">${validation_message}</p>
|
<p class="validation-message">${validation_message}</p>
|
||||||
@ -26,7 +26,10 @@ export default (el) => {
|
|||||||
placeholder="${i18n_nickname}"/>
|
placeholder="${i18n_nickname}"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<input type="submit" class="btn btn-primary" name="join" value="${i18n_join}"/>
|
<input type="submit"
|
||||||
|
class="btn btn-primary"
|
||||||
|
name="join"
|
||||||
|
value="${i18n_join}"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -60,9 +60,9 @@ describe("A Groupchat Message", function () {
|
|||||||
expect(view.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
|
expect(view.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
|
||||||
const edit = await u.waitUntil(() => view.querySelector('.chat-msg__content .fa-edit'));
|
const edit = await u.waitUntil(() => view.querySelector('.chat-msg__content .fa-edit'));
|
||||||
edit.click();
|
edit.click();
|
||||||
const modal = _converse.api.modal.get('message-versions-modal');
|
const modal = _converse.api.modal.get('converse-message-versions-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
const older_msgs = modal.el.querySelectorAll('.older-msg');
|
const older_msgs = modal.querySelectorAll('.older-msg');
|
||||||
expect(older_msgs.length).toBe(2);
|
expect(older_msgs.length).toBe(2);
|
||||||
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
||||||
expect(older_msgs[1].textContent.includes('But soft, what light through yonder chimney breaks?')).toBe(true);
|
expect(older_msgs[1].textContent.includes('But soft, what light through yonder chimney breaks?')).toBe(true);
|
||||||
@ -151,9 +151,9 @@ describe("A Groupchat Message", function () {
|
|||||||
expect(view.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
|
expect(view.querySelectorAll('.chat-msg__content .fa-edit').length).toBe(1);
|
||||||
const edit = await u.waitUntil(() => view.querySelector('.chat-msg__content .fa-edit'));
|
const edit = await u.waitUntil(() => view.querySelector('.chat-msg__content .fa-edit'));
|
||||||
edit.click();
|
edit.click();
|
||||||
const modal = _converse.api.modal.get('message-versions-modal');
|
const modal = _converse.api.modal.get('converse-message-versions-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
const older_msgs = modal.el.querySelectorAll('.older-msg');
|
const older_msgs = modal.querySelectorAll('.older-msg');
|
||||||
expect(older_msgs.length).toBe(2);
|
expect(older_msgs.length).toBe(2);
|
||||||
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
||||||
expect(older_msgs[1].textContent.includes('But soft, what light through yonder chimney breaks?')).toBe(true);
|
expect(older_msgs[1].textContent.includes('But soft, what light through yonder chimney breaks?')).toBe(true);
|
||||||
|
@ -14,7 +14,7 @@ async function openModtools (_converse, view) {
|
|||||||
const message_form = view.querySelector('converse-muc-message-form');
|
const message_form = view.querySelector('converse-muc-message-form');
|
||||||
message_form.onKeyDown(enter);
|
message_form.onKeyDown(enter);
|
||||||
const modal = await u.waitUntil(() => _converse.api.modal.get('converse-modtools-modal'));
|
const modal = await u.waitUntil(() => _converse.api.modal.get('converse-modtools-modal'));
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
return modal;
|
return modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,18 +37,18 @@ describe("The groupchat moderator tool", function () {
|
|||||||
await u.waitUntil(() => (view.model.occupants.length === 5), 1000);
|
await u.waitUntil(() => (view.model.occupants.length === 5), 1000);
|
||||||
|
|
||||||
const modal = await openModtools(_converse, view);
|
const modal = await openModtools(_converse, view);
|
||||||
let tab = modal.el.querySelector('#affiliations-tab');
|
let tab = modal.querySelector('#affiliations-tab');
|
||||||
// Clear so that we don't match older stanzas
|
// Clear so that we don't match older stanzas
|
||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
tab.click();
|
tab.click();
|
||||||
let select = modal.el.querySelector('.select-affiliation');
|
let select = modal.querySelector('.select-affiliation');
|
||||||
expect(select.value).toBe('owner');
|
expect(select.value).toBe('owner');
|
||||||
select.value = 'admin';
|
select.value = 'admin';
|
||||||
let button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
|
let button = modal.querySelector('.btn-primary[name="users_with_affiliation"]');
|
||||||
button.click();
|
button.click();
|
||||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length);
|
await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length);
|
||||||
let user_els = modal.el.querySelectorAll('.list-group--users > li');
|
let user_els = modal.querySelectorAll('.list-group--users > li');
|
||||||
expect(user_els.length).toBe(1);
|
expect(user_els.length).toBe(1);
|
||||||
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: wiccarocks@shakespeare.lit');
|
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: wiccarocks@shakespeare.lit');
|
||||||
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: wiccan');
|
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: wiccan');
|
||||||
@ -58,8 +58,8 @@ describe("The groupchat moderator tool", function () {
|
|||||||
select.value = 'owner';
|
select.value = 'owner';
|
||||||
button.click();
|
button.click();
|
||||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 2);
|
await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 2);
|
||||||
user_els = modal.el.querySelectorAll('.list-group--users > li');
|
user_els = modal.querySelectorAll('.list-group--users > li');
|
||||||
expect(user_els.length).toBe(2);
|
expect(user_els.length).toBe(2);
|
||||||
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit');
|
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit');
|
||||||
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo');
|
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo');
|
||||||
@ -112,25 +112,25 @@ describe("The groupchat moderator tool", function () {
|
|||||||
];
|
];
|
||||||
await mock.returnMemberLists(_converse, muc_jid, members);
|
await mock.returnMemberLists(_converse, muc_jid, members);
|
||||||
await u.waitUntil(() => view.model.occupants.pluck('affiliation').filter(o => o === 'owner').length === 1);
|
await u.waitUntil(() => view.model.occupants.pluck('affiliation').filter(o => o === 'owner').length === 1);
|
||||||
const alert = modal.el.querySelector('.alert-primary');
|
const alert = modal.querySelector('.alert-primary');
|
||||||
expect(alert.textContent.trim()).toBe('Affiliation changed');
|
expect(alert.textContent.trim()).toBe('Affiliation changed');
|
||||||
|
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 1);
|
await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 1);
|
||||||
user_els = modal.el.querySelectorAll('.list-group--users > li');
|
user_els = modal.querySelectorAll('.list-group--users > li');
|
||||||
expect(user_els.length).toBe(1);
|
expect(user_els.length).toBe(1);
|
||||||
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit');
|
expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit');
|
||||||
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo');
|
expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo');
|
||||||
expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner');
|
expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner');
|
||||||
|
|
||||||
tab = modal.el.querySelector('#roles-tab');
|
modal.querySelector('#roles-tab').click();
|
||||||
tab.click();
|
select = modal.querySelector('.select-role');
|
||||||
select = modal.el.querySelector('.select-role');
|
await u.waitUntil(() => u.isVisible(select));
|
||||||
expect(u.isVisible(select)).toBe(true);
|
|
||||||
expect(select.value).toBe('moderator');
|
expect(select.value).toBe('moderator');
|
||||||
button = modal.el.querySelector('.btn-primary[name="users_with_role"]');
|
button = modal.querySelector('.btn-primary[name="users_with_role"]');
|
||||||
button.click();
|
button.click();
|
||||||
|
|
||||||
const roles_panel = modal.el.querySelector('#roles-tabpanel');
|
const roles_panel = modal.querySelector('#roles-tabpanel');
|
||||||
await u.waitUntil(() => roles_panel.querySelectorAll('.list-group--users > li').length === 1);
|
await u.waitUntil(() => roles_panel.querySelectorAll('.list-group--users > li').length === 1);
|
||||||
select.value = 'participant';
|
select.value = 'participant';
|
||||||
button.click();
|
button.click();
|
||||||
@ -158,35 +158,35 @@ describe("The groupchat moderator tool", function () {
|
|||||||
// Clear so that we don't match older stanzas
|
// Clear so that we don't match older stanzas
|
||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
const modal = await openModtools(_converse, view);
|
const modal = await openModtools(_converse, view);
|
||||||
const select = modal.el.querySelector('.select-affiliation');
|
const select = modal.querySelector('.select-affiliation');
|
||||||
expect(select.value).toBe('owner');
|
expect(select.value).toBe('owner');
|
||||||
select.value = 'member';
|
select.value = 'member';
|
||||||
const button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
|
const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]');
|
||||||
button.click();
|
button.click();
|
||||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 6);
|
await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 6);
|
||||||
|
|
||||||
const nicks = Array.from(modal.el.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick'));
|
const nicks = Array.from(modal.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick'));
|
||||||
expect(nicks.join(' ')).toBe('gower juliet romeo thirdwitch wiccan witch');
|
expect(nicks.join(' ')).toBe('gower juliet romeo thirdwitch wiccan witch');
|
||||||
|
|
||||||
const filter = modal.el.querySelector('[name="filter"]');
|
const filter = modal.querySelector('[name="filter"]');
|
||||||
expect(filter).not.toBe(null);
|
expect(filter).not.toBe(null);
|
||||||
|
|
||||||
filter.value = 'romeo';
|
filter.value = 'romeo';
|
||||||
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
||||||
await u.waitUntil(() => ( modal.el.querySelectorAll('.list-group--users > li').length === 1));
|
await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1));
|
||||||
|
|
||||||
filter.value = 'r';
|
filter.value = 'r';
|
||||||
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
||||||
await u.waitUntil(() => ( modal.el.querySelectorAll('.list-group--users > li').length === 3));
|
await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 3));
|
||||||
|
|
||||||
filter.value = 'gower';
|
filter.value = 'gower';
|
||||||
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
||||||
await u.waitUntil(() => ( modal.el.querySelectorAll('.list-group--users > li').length === 1));
|
await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1));
|
||||||
|
|
||||||
filter.value = 'RoMeO';
|
filter.value = 'RoMeO';
|
||||||
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
||||||
await u.waitUntil(() => ( modal.el.querySelectorAll('.list-group--users > li').length === 1));
|
await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1));
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -259,40 +259,40 @@ describe("The groupchat moderator tool", function () {
|
|||||||
message_form.onKeyDown(enter);
|
message_form.onKeyDown(enter);
|
||||||
|
|
||||||
const modal = await u.waitUntil(() => _converse.api.modal.get('converse-modtools-modal'));
|
const modal = await u.waitUntil(() => _converse.api.modal.get('converse-modtools-modal'));
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
const tab = modal.el.querySelector('#roles-tab');
|
const tab = modal.querySelector('#roles-tab');
|
||||||
tab.click();
|
tab.click();
|
||||||
|
|
||||||
// Clear so that we don't match older stanzas
|
// Clear so that we don't match older stanzas
|
||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
|
|
||||||
const select = modal.el.querySelector('.select-role');
|
const select = modal.querySelector('.select-role');
|
||||||
expect(select.value).toBe('moderator');
|
expect(select.value).toBe('moderator');
|
||||||
select.value = 'participant';
|
select.value = 'participant';
|
||||||
|
|
||||||
const button = modal.el.querySelector('.btn-primary[name="users_with_role"]');
|
const button = modal.querySelector('.btn-primary[name="users_with_role"]');
|
||||||
button.click();
|
button.click();
|
||||||
await u.waitUntil(() => !modal.loading_users_with_role);
|
await u.waitUntil(() => !modal.loading_users_with_role);
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 6);
|
await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 6);
|
||||||
|
|
||||||
const nicks = Array.from(modal.el.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick'));
|
const nicks = Array.from(modal.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick'));
|
||||||
expect(nicks.join(' ')).toBe('crone newb nomorenicks oldhag some1 tux');
|
expect(nicks.join(' ')).toBe('crone newb nomorenicks oldhag some1 tux');
|
||||||
|
|
||||||
const filter = modal.el.querySelector('[name="filter"]');
|
const filter = modal.querySelector('[name="filter"]');
|
||||||
expect(filter).not.toBe(null);
|
expect(filter).not.toBe(null);
|
||||||
|
|
||||||
filter.value = 'tux';
|
filter.value = 'tux';
|
||||||
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
||||||
await u.waitUntil(() => ( modal.el.querySelectorAll('.list-group--users > li').length === 1));
|
await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1));
|
||||||
|
|
||||||
filter.value = 'r';
|
filter.value = 'r';
|
||||||
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
||||||
await u.waitUntil(() => ( modal.el.querySelectorAll('.list-group--users > li').length === 2));
|
await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 2));
|
||||||
|
|
||||||
filter.value = 'crone';
|
filter.value = 'crone';
|
||||||
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
u.triggerEvent(filter, "keyup", "KeyboardEvent");
|
||||||
await u.waitUntil(() => ( modal.el.querySelectorAll('.list-group--users > li').length === 1));
|
await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("shows an error message if a particular affiliation list may not be retrieved",
|
it("shows an error message if a particular affiliation list may not be retrieved",
|
||||||
@ -310,14 +310,14 @@ describe("The groupchat moderator tool", function () {
|
|||||||
const view = _converse.chatboxviews.get(muc_jid);
|
const view = _converse.chatboxviews.get(muc_jid);
|
||||||
await u.waitUntil(() => (view.model.occupants.length === 5));
|
await u.waitUntil(() => (view.model.occupants.length === 5));
|
||||||
const modal = await openModtools(_converse, view);
|
const modal = await openModtools(_converse, view);
|
||||||
const tab = modal.el.querySelector('#affiliations-tab');
|
const tab = modal.querySelector('#affiliations-tab');
|
||||||
// Clear so that we don't match older stanzas
|
// Clear so that we don't match older stanzas
|
||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||||
tab.click();
|
tab.click();
|
||||||
const select = modal.el.querySelector('.select-affiliation');
|
const select = modal.querySelector('.select-affiliation');
|
||||||
select.value = 'outcast';
|
select.value = 'outcast';
|
||||||
const button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
|
const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]');
|
||||||
button.click();
|
button.click();
|
||||||
|
|
||||||
const iq_query = await u.waitUntil(() => _.filter(
|
const iq_query = await u.waitUntil(() => _.filter(
|
||||||
@ -338,10 +338,10 @@ describe("The groupchat moderator tool", function () {
|
|||||||
_converse.connection._dataRecv(mock.createRequest(error));
|
_converse.connection._dataRecv(mock.createRequest(error));
|
||||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||||
|
|
||||||
const alert = await u.waitUntil(() => modal.el.querySelector('.alert'));
|
const alert = await u.waitUntil(() => modal.querySelector('.alert'));
|
||||||
expect(alert.textContent.trim()).toBe('Error: not allowed to fetch outcast list for MUC lounge@montague.lit');
|
expect(alert.textContent.trim()).toBe('Error: not allowed to fetch outcast list for MUC lounge@montague.lit');
|
||||||
|
|
||||||
const user_els = modal.el.querySelectorAll('.list-group--users > li');
|
const user_els = modal.querySelectorAll('.list-group--users > li');
|
||||||
expect(user_els.length).toBe(1);
|
expect(user_els.length).toBe(1);
|
||||||
expect(user_els[0].textContent.trim()).toBe('No users with that affiliation found.');
|
expect(user_els[0].textContent.trim()).toBe('No users with that affiliation found.');
|
||||||
}));
|
}));
|
||||||
@ -361,16 +361,16 @@ describe("The groupchat moderator tool", function () {
|
|||||||
// Clear so that we don't match older stanzas
|
// Clear so that we don't match older stanzas
|
||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
|
|
||||||
const tab = modal.el.querySelector('#affiliations-tab');
|
const tab = modal.querySelector('#affiliations-tab');
|
||||||
tab.click();
|
tab.click();
|
||||||
const select = modal.el.querySelector('.select-affiliation');
|
const select = modal.querySelector('.select-affiliation');
|
||||||
select.value = 'member';
|
select.value = 'member';
|
||||||
const button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
|
const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]');
|
||||||
button.click();
|
button.click();
|
||||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 1);
|
await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 1);
|
||||||
|
|
||||||
const user_els = modal.el.querySelectorAll('.list-group--users > li');
|
const user_els = modal.querySelectorAll('.list-group--users > li');
|
||||||
const toggle = user_els[0].querySelector('.list-group-item:nth-child(3n) .toggle-form');
|
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');
|
const form = user_els[0].querySelector('.list-group-item:nth-child(3n) .affiliation-form');
|
||||||
expect(u.hasClass('hidden', form)).toBeTruthy();
|
expect(u.hasClass('hidden', form)).toBeTruthy();
|
||||||
@ -422,19 +422,19 @@ describe("The groupchat moderator tool", function () {
|
|||||||
const view = _converse.chatboxviews.get(muc_jid);
|
const view = _converse.chatboxviews.get(muc_jid);
|
||||||
await u.waitUntil(() => (view.model.occupants.length === 3));
|
await u.waitUntil(() => (view.model.occupants.length === 3));
|
||||||
const modal = await openModtools(_converse, view);
|
const modal = await openModtools(_converse, view);
|
||||||
const tab = modal.el.querySelector('#affiliations-tab');
|
const tab = modal.querySelector('#affiliations-tab');
|
||||||
// Clear so that we don't match older stanzas
|
// Clear so that we don't match older stanzas
|
||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
tab.click();
|
tab.click();
|
||||||
const show_affiliation_dropdown = modal.el.querySelector('.select-affiliation');
|
const show_affiliation_dropdown = modal.querySelector('.select-affiliation');
|
||||||
show_affiliation_dropdown.value = 'member';
|
show_affiliation_dropdown.value = 'member';
|
||||||
const button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
|
const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]');
|
||||||
button.click();
|
button.click();
|
||||||
|
|
||||||
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
await u.waitUntil(() => !modal.loading_users_with_affiliation);
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.list-group--users > li').length === 2);
|
await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 2);
|
||||||
|
|
||||||
const user_els = modal.el.querySelectorAll('.list-group--users > li');
|
const user_els = modal.querySelectorAll('.list-group--users > li');
|
||||||
let change_affiliation_dropdown = user_els[0].querySelector('.select-affiliation');
|
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']);
|
expect(Array.from(change_affiliation_dropdown.options).map(o => o.value)).toEqual(['member', 'outcast', 'none']);
|
||||||
|
|
||||||
|
@ -12,32 +12,32 @@ describe('The "Groupchats" Add modal', function () {
|
|||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
mock.closeControlBox(_converse);
|
mock.closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('add-chatroom-modal');
|
const modal = _converse.api.modal.get('converse-add-muc-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
let label_name = modal.el.querySelector('label[for="chatroom"]');
|
let label_name = modal.querySelector('label[for="chatroom"]');
|
||||||
expect(label_name.textContent.trim()).toBe('Groupchat address:');
|
expect(label_name.textContent.trim()).toBe('Groupchat address:');
|
||||||
let name_input = modal.el.querySelector('input[name="chatroom"]');
|
const name_input = modal.querySelector('input[name="chatroom"]');
|
||||||
expect(name_input.placeholder).toBe('name@conference.example.org');
|
expect(name_input.placeholder).toBe('name@conference.example.org');
|
||||||
|
|
||||||
const label_nick = modal.el.querySelector('label[for="nickname"]');
|
const label_nick = modal.querySelector('label[for="nickname"]');
|
||||||
expect(label_nick.textContent.trim()).toBe('Nickname:');
|
expect(label_nick.textContent.trim()).toBe('Nickname:');
|
||||||
const nick_input = modal.el.querySelector('input[name="nickname"]');
|
const nick_input = modal.querySelector('input[name="nickname"]');
|
||||||
expect(nick_input.value).toBe('');
|
expect(nick_input.value).toBe('');
|
||||||
nick_input.value = 'romeo';
|
nick_input.value = 'romeo';
|
||||||
|
|
||||||
expect(modal.el.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
|
expect(modal.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
|
||||||
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
||||||
modal.el.querySelector('input[name="chatroom"]').value = 'lounge@muc.montague.lit';
|
modal.querySelector('input[name="chatroom"]').value = 'lounge@muc.montague.lit';
|
||||||
modal.el.querySelector('form input[type="submit"]').click();
|
modal.querySelector('form input[type="submit"]').click();
|
||||||
await u.waitUntil(() => _converse.chatboxes.length);
|
await u.waitUntil(() => _converse.chatboxes.length);
|
||||||
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
|
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
|
||||||
|
|
||||||
roomspanel.model.set('muc_domain', 'muc.example.org');
|
roomspanel.model.set('muc_domain', 'muc.example.org');
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
label_name = modal.el.querySelector('label[for="chatroom"]');
|
label_name = modal.querySelector('label[for="chatroom"]');
|
||||||
expect(label_name.textContent.trim()).toBe('Groupchat address:');
|
expect(label_name.textContent.trim()).toBe('Groupchat name:');
|
||||||
await u.waitUntil(() => modal.el.querySelector('input[name="chatroom"]')?.placeholder === 'name@muc.example.org');
|
await u.waitUntil(() => modal.querySelector('input[name="chatroom"]')?.placeholder === 'name@muc.example.org');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -46,31 +46,31 @@ describe('The "Groupchats" Add modal', function () {
|
|||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
const modal = _converse.api.modal.get('add-chatroom-modal');
|
const modal = _converse.api.modal.get('converse-add-muc-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
expect(modal.el.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
|
expect(modal.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
|
||||||
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
||||||
const label_name = modal.el.querySelector('label[for="chatroom"]');
|
const label_name = modal.querySelector('label[for="chatroom"]');
|
||||||
expect(label_name.textContent.trim()).toBe('Groupchat name:');
|
expect(label_name.textContent.trim()).toBe('Groupchat name:');
|
||||||
let name_input = modal.el.querySelector('input[name="chatroom"]');
|
let name_input = modal.querySelector('input[name="chatroom"]');
|
||||||
expect(name_input.placeholder).toBe('name@muc.example.org');
|
expect(name_input.placeholder).toBe('name@muc.example.org');
|
||||||
name_input.value = 'lounge';
|
name_input.value = 'lounge';
|
||||||
let nick_input = modal.el.querySelector('input[name="nickname"]');
|
let nick_input = modal.querySelector('input[name="nickname"]');
|
||||||
nick_input.value = 'max';
|
nick_input.value = 'max';
|
||||||
|
|
||||||
modal.el.querySelector('form input[type="submit"]').click();
|
modal.querySelector('form input[type="submit"]').click();
|
||||||
await u.waitUntil(() => _converse.chatboxes.length);
|
await u.waitUntil(() => _converse.chatboxes.length);
|
||||||
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
|
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
|
||||||
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@muc.example.org')).toBe(true);
|
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@muc.example.org')).toBe(true);
|
||||||
|
|
||||||
// However, you can still open MUCs with different domains
|
// However, you can still open MUCs with different domains
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
name_input = modal.el.querySelector('input[name="chatroom"]');
|
name_input = modal.querySelector('input[name="chatroom"]');
|
||||||
name_input.value = 'lounge@conference.example.org';
|
name_input.value = 'lounge@conference.example.org';
|
||||||
nick_input = modal.el.querySelector('input[name="nickname"]');
|
nick_input = modal.querySelector('input[name="nickname"]');
|
||||||
nick_input.value = 'max';
|
nick_input.value = 'max';
|
||||||
modal.el.querySelector('form input[type="submit"]').click();
|
modal.querySelector('form input[type="submit"]').click();
|
||||||
await u.waitUntil(() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2);
|
await u.waitUntil(() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2);
|
||||||
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 2);
|
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 2);
|
||||||
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@conference.example.org')).toBe(
|
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@conference.example.org')).toBe(
|
||||||
@ -87,30 +87,30 @@ describe('The "Groupchats" Add modal', function () {
|
|||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
const modal = _converse.api.modal.get('add-chatroom-modal');
|
const modal = _converse.api.modal.get('converse-add-muc-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
expect(modal.el.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
|
expect(modal.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
|
||||||
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
||||||
const label_name = modal.el.querySelector('label[for="chatroom"]');
|
const label_name = modal.querySelector('label[for="chatroom"]');
|
||||||
expect(label_name.textContent.trim()).toBe('Groupchat name:');
|
expect(label_name.textContent.trim()).toBe('Groupchat name:');
|
||||||
let name_input = modal.el.querySelector('input[name="chatroom"]');
|
let name_input = modal.querySelector('input[name="chatroom"]');
|
||||||
expect(name_input.placeholder).toBe('');
|
expect(name_input.placeholder).toBe('');
|
||||||
name_input.value = 'lounge';
|
name_input.value = 'lounge';
|
||||||
let nick_input = modal.el.querySelector('input[name="nickname"]');
|
let nick_input = modal.querySelector('input[name="nickname"]');
|
||||||
nick_input.value = 'max';
|
nick_input.value = 'max';
|
||||||
modal.el.querySelector('form input[type="submit"]').click();
|
modal.querySelector('form input[type="submit"]').click();
|
||||||
await u.waitUntil(() => _converse.chatboxes.length);
|
await u.waitUntil(() => _converse.chatboxes.length);
|
||||||
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
|
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
|
||||||
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@muc.example.org')).toBe(true);
|
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@muc.example.org')).toBe(true);
|
||||||
|
|
||||||
// However, you can still open MUCs with different domains
|
// However, you can still open MUCs with different domains
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
name_input = modal.el.querySelector('input[name="chatroom"]');
|
name_input = modal.querySelector('input[name="chatroom"]');
|
||||||
name_input.value = 'lounge@conference';
|
name_input.value = 'lounge@conference';
|
||||||
nick_input = modal.el.querySelector('input[name="nickname"]');
|
nick_input = modal.querySelector('input[name="nickname"]');
|
||||||
nick_input.value = 'max';
|
nick_input.value = 'max';
|
||||||
modal.el.querySelector('form input[type="submit"]').click();
|
modal.querySelector('form input[type="submit"]').click();
|
||||||
await u.waitUntil(
|
await u.waitUntil(
|
||||||
() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2
|
() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2
|
||||||
);
|
);
|
||||||
|
@ -10,17 +10,17 @@ describe('The "Groupchats" List modal', function () {
|
|||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-list-muc-modal').click();
|
roomspanel.querySelector('.show-list-muc-modal').click();
|
||||||
mock.closeControlBox(_converse);
|
mock.closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('muc-list-modal');
|
const modal = _converse.api.modal.get('converse-muc-list-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
||||||
|
|
||||||
// See: https://xmpp.org/extensions/xep-0045.html#disco-rooms
|
// See: https://xmpp.org/extensions/xep-0045.html#disco-rooms
|
||||||
expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(0);
|
expect(modal.querySelectorAll('.available-chatrooms li').length).toBe(0);
|
||||||
|
|
||||||
const server_input = modal.el.querySelector('input[name="server"]');
|
const server_input = modal.querySelector('input[name="server"]');
|
||||||
expect(server_input.placeholder).toBe('conference.example.org');
|
expect(server_input.placeholder).toBe('conference.example.org');
|
||||||
server_input.value = 'chat.shakespeare.lit';
|
server_input.value = 'chat.shakespeare.lit';
|
||||||
modal.el.querySelector('input[type="submit"]').click();
|
modal.querySelector('input[type="submit"]').click();
|
||||||
await u.waitUntil(() => _converse.chatboxes.length);
|
await u.waitUntil(() => _converse.chatboxes.length);
|
||||||
|
|
||||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||||
@ -55,8 +55,8 @@ describe('The "Groupchats" List modal', function () {
|
|||||||
.c('item', { jid: 'street@chat.shakespeare.lit', name: 'A street' }).nodeTree;
|
.c('item', { jid: 'street@chat.shakespeare.lit', name: 'A street' }).nodeTree;
|
||||||
_converse.connection._dataRecv(mock.createRequest(iq));
|
_converse.connection._dataRecv(mock.createRequest(iq));
|
||||||
|
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.available-chatrooms li').length === 11);
|
await u.waitUntil(() => modal.querySelectorAll('.available-chatrooms li').length === 11);
|
||||||
const rooms = modal.el.querySelectorAll('.available-chatrooms li');
|
const rooms = modal.querySelectorAll('.available-chatrooms li');
|
||||||
expect(rooms[0].textContent.trim()).toBe('Groupchats found');
|
expect(rooms[0].textContent.trim()).toBe('Groupchats found');
|
||||||
expect(rooms[1].textContent.trim()).toBe('A Lonely Heath');
|
expect(rooms[1].textContent.trim()).toBe('A Lonely Heath');
|
||||||
expect(rooms[2].textContent.trim()).toBe('A Dark Cave');
|
expect(rooms[2].textContent.trim()).toBe('A Dark Cave');
|
||||||
@ -83,9 +83,9 @@ describe('The "Groupchats" List modal', function () {
|
|||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-list-muc-modal').click();
|
roomspanel.querySelector('.show-list-muc-modal').click();
|
||||||
mock.closeControlBox(_converse);
|
mock.closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('muc-list-modal');
|
const modal = _converse.api.modal.get('converse-muc-list-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
const server_input = modal.el.querySelector('input[name="server"]');
|
const server_input = modal.querySelector('input[name="server"]');
|
||||||
expect(server_input.value).toBe('muc.example.org');
|
expect(server_input.value).toBe('muc.example.org');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -99,12 +99,12 @@ describe('The "Groupchats" List modal', function () {
|
|||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-list-muc-modal').click();
|
roomspanel.querySelector('.show-list-muc-modal').click();
|
||||||
mock.closeControlBox(_converse);
|
mock.closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('muc-list-modal');
|
const modal = _converse.api.modal.get('converse-muc-list-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
|
||||||
|
|
||||||
expect(modal.el.querySelector('input[name="server"]')).toBe(null);
|
expect(modal.querySelector('input[name="server"]')).toBe(null);
|
||||||
expect(modal.el.querySelector('input[type="submit"]')).toBe(null);
|
expect(modal.querySelector('input[type="submit"]')).toBe(null);
|
||||||
await u.waitUntil(() => _converse.chatboxes.length);
|
await u.waitUntil(() => _converse.chatboxes.length);
|
||||||
const sent_stanza = await u.waitUntil(() =>
|
const sent_stanza = await u.waitUntil(() =>
|
||||||
_converse.connection.sent_stanzas
|
_converse.connection.sent_stanzas
|
||||||
@ -129,8 +129,8 @@ describe('The "Groupchats" List modal', function () {
|
|||||||
.c('item', { jid: 'forres@chat.shakespeare.lit', name: 'The Palace' }).up();
|
.c('item', { jid: 'forres@chat.shakespeare.lit', name: 'The Palace' }).up();
|
||||||
_converse.connection._dataRecv(mock.createRequest(iq));
|
_converse.connection._dataRecv(mock.createRequest(iq));
|
||||||
|
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.available-chatrooms li').length === 4);
|
await u.waitUntil(() => modal.querySelectorAll('.available-chatrooms li').length === 4);
|
||||||
const rooms = modal.el.querySelectorAll('.available-chatrooms li');
|
const rooms = modal.querySelectorAll('.available-chatrooms li');
|
||||||
expect(rooms[0].textContent.trim()).toBe('Groupchats found');
|
expect(rooms[0].textContent.trim()).toBe('Groupchats found');
|
||||||
expect(rooms[1].textContent.trim()).toBe('A Lonely Heath');
|
expect(rooms[1].textContent.trim()).toBe('A Lonely Heath');
|
||||||
expect(rooms[2].textContent.trim()).toBe('A Dark Cave');
|
expect(rooms[2].textContent.trim()).toBe('A Dark Cave');
|
||||||
|
@ -1332,21 +1332,21 @@ describe("Groupchats", function () {
|
|||||||
await u.waitUntil(() => view.querySelector('.open-invite-modal'));
|
await u.waitUntil(() => view.querySelector('.open-invite-modal'));
|
||||||
|
|
||||||
view.querySelector('.open-invite-modal').click();
|
view.querySelector('.open-invite-modal').click();
|
||||||
const modal = _converse.api.modal.get('muc-invite-modal');
|
const modal = _converse.api.modal.get('converse-muc-invite-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000)
|
await u.waitUntil(() => u.isVisible(modal), 1000)
|
||||||
|
|
||||||
expect(modal.el.querySelectorAll('#invitee_jids').length).toBe(1);
|
expect(modal.querySelectorAll('#invitee_jids').length).toBe(1);
|
||||||
expect(modal.el.querySelectorAll('textarea').length).toBe(1);
|
expect(modal.querySelectorAll('textarea').length).toBe(1);
|
||||||
|
|
||||||
spyOn(view.model, 'directInvite').and.callThrough();
|
spyOn(view.model, 'directInvite').and.callThrough();
|
||||||
|
|
||||||
const input = modal.el.querySelector('#invitee_jids');
|
const input = modal.querySelector('#invitee_jids input');
|
||||||
input.value = "Balt";
|
input.value = "Balt";
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
modal.querySelector('input[type="submit"]').click();
|
||||||
|
|
||||||
await u.waitUntil(() => modal.el.querySelector('.error'));
|
await u.waitUntil(() => modal.querySelector('.error'));
|
||||||
|
|
||||||
const error = modal.el.querySelector('.error');
|
const error = modal.querySelector('.error');
|
||||||
expect(error.textContent).toBe('Please enter a valid XMPP address');
|
expect(error.textContent).toBe('Please enter a valid XMPP address');
|
||||||
|
|
||||||
let evt = new Event('input');
|
let evt = new Event('input');
|
||||||
@ -1354,7 +1354,7 @@ describe("Groupchats", function () {
|
|||||||
|
|
||||||
let sent_stanza;
|
let sent_stanza;
|
||||||
spyOn(_converse.connection, 'send').and.callFake(stanza => (sent_stanza = stanza));
|
spyOn(_converse.connection, 'send').and.callFake(stanza => (sent_stanza = stanza));
|
||||||
const hint = await u.waitUntil(() => modal.el.querySelector('.suggestion-box__results li'));
|
const hint = await u.waitUntil(() => modal.querySelector('.suggestion-box__results li'));
|
||||||
expect(input.value).toBe('Balt');
|
expect(input.value).toBe('Balt');
|
||||||
expect(hint.textContent.trim()).toBe('Balthasar');
|
expect(hint.textContent.trim()).toBe('Balthasar');
|
||||||
|
|
||||||
@ -1362,9 +1362,9 @@ describe("Groupchats", function () {
|
|||||||
evt.button = 0;
|
evt.button = 0;
|
||||||
hint.dispatchEvent(evt);
|
hint.dispatchEvent(evt);
|
||||||
|
|
||||||
const textarea = modal.el.querySelector('textarea');
|
const textarea = modal.querySelector('textarea');
|
||||||
textarea.value = "Please join!";
|
textarea.value = "Please join!";
|
||||||
modal.el.querySelector('button[type="submit"]').click();
|
modal.querySelector('input[type="submit"]').click();
|
||||||
|
|
||||||
expect(view.model.directInvite).toHaveBeenCalled();
|
expect(view.model.directInvite).toHaveBeenCalled();
|
||||||
expect(Strophe.serialize(sent_stanza)).toBe(
|
expect(Strophe.serialize(sent_stanza)).toBe(
|
||||||
@ -1634,10 +1634,10 @@ describe("Groupchats", function () {
|
|||||||
|
|
||||||
const info_el = view.querySelector(".show-muc-details-modal");
|
const info_el = view.querySelector(".show-muc-details-modal");
|
||||||
info_el.click();
|
info_el.click();
|
||||||
let modal = _converse.api.modal.get('muc-details-modal');
|
let modal = _converse.api.modal.get('converse-muc-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
let features_list = modal.el.querySelector('.features-list');
|
let features_list = modal.querySelector('.features-list');
|
||||||
let features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
|
let features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
|
||||||
|
|
||||||
expect(features_shown.join(' ')).toBe(
|
expect(features_shown.join(' ')).toBe(
|
||||||
@ -1661,7 +1661,7 @@ describe("Groupchats", function () {
|
|||||||
expect(view.model.features.get('unsecured')).toBe(false);
|
expect(view.model.features.get('unsecured')).toBe(false);
|
||||||
await u.waitUntil(() => view.querySelector('.chatbox-title__text').textContent.trim() === 'Room');
|
await u.waitUntil(() => view.querySelector('.chatbox-title__text').textContent.trim() === 'Room');
|
||||||
|
|
||||||
modal.el.querySelector('.close').click();
|
modal.querySelector('.close').click();
|
||||||
view.querySelector('.configure-chatroom-button').click();
|
view.querySelector('.configure-chatroom-button').click();
|
||||||
|
|
||||||
const IQs = _converse.connection.IQ_stanzas;
|
const IQs = _converse.connection.IQ_stanzas;
|
||||||
@ -1792,10 +1792,10 @@ describe("Groupchats", function () {
|
|||||||
await u.waitUntil(() => new Promise(success => view.model.features.on('change', success)));
|
await u.waitUntil(() => new Promise(success => view.model.features.on('change', success)));
|
||||||
|
|
||||||
info_el.click();
|
info_el.click();
|
||||||
modal = _converse.api.modal.get('muc-details-modal');
|
modal = _converse.api.modal.get('converse-muc-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
features_list = modal.el.querySelector('.features-list');
|
features_list = modal.querySelector('.features-list');
|
||||||
features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
|
features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s);
|
||||||
expect(features_shown.join(' ')).toBe(
|
expect(features_shown.join(' ')).toBe(
|
||||||
'Password protected - This groupchat requires a password before entry '+
|
'Password protected - This groupchat requires a password before entry '+
|
||||||
|
@ -19,17 +19,17 @@ describe("A MUC", function () {
|
|||||||
const dropdown_item = view.querySelector(".open-nickname-modal");
|
const dropdown_item = view.querySelector(".open-nickname-modal");
|
||||||
dropdown_item.click();
|
dropdown_item.click();
|
||||||
|
|
||||||
const modal = _converse.api.modal.get('change-nickname-modal');
|
const modal = _converse.api.modal.get('converse-muc-nickname-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el));
|
await u.waitUntil(() => u.isVisible(modal));
|
||||||
|
|
||||||
const input = modal.el.querySelector('input[name="nick"]');
|
const input = modal.querySelector('input[name="nick"]');
|
||||||
expect(input.value).toBe(nick);
|
expect(input.value).toBe(nick);
|
||||||
|
|
||||||
const newnick = 'loverboy';
|
const newnick = 'loverboy';
|
||||||
input.value = newnick;
|
input.value = newnick;
|
||||||
modal.el.querySelector('input[type="submit"]')?.click();
|
modal.querySelector('input[type="submit"]')?.click();
|
||||||
|
|
||||||
await u.waitUntil(() => !u.isVisible(modal.el));
|
await u.waitUntil(() => !u.isVisible(modal));
|
||||||
|
|
||||||
const { sent_stanzas } = _converse.connection;
|
const { sent_stanzas } = _converse.connection;
|
||||||
const sent_stanza = sent_stanzas.pop()
|
const sent_stanza = sent_stanzas.pop()
|
||||||
@ -422,13 +422,13 @@ describe("A MUC", function () {
|
|||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
mock.closeControlBox(_converse);
|
mock.closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('add-chatroom-modal');
|
const modal = _converse.api.modal.get('converse-add-muc-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000)
|
await u.waitUntil(() => u.isVisible(modal), 1000)
|
||||||
const name_input = modal.el.querySelector('input[name="chatroom"]');
|
const name_input = modal.querySelector('input[name="chatroom"]');
|
||||||
name_input.value = 'lounge@montague.lit';
|
name_input.value = 'lounge@montague.lit';
|
||||||
expect(modal.el.querySelector('label[for="nickname"]')).toBe(null);
|
expect(modal.querySelector('label[for="nickname"]')).toBe(null);
|
||||||
expect(modal.el.querySelector('input[name="nickname"]')).toBe(null);
|
expect(modal.querySelector('input[name="nickname"]')).toBe(null);
|
||||||
modal.el.querySelector('form input[type="submit"]').click();
|
modal.querySelector('form input[type="submit"]').click();
|
||||||
await u.waitUntil(() => _converse.chatboxes.length > 1);
|
await u.waitUntil(() => _converse.chatboxes.length > 1);
|
||||||
const chatroom = _converse.chatboxes.get('lounge@montague.lit');
|
const chatroom = _converse.chatboxes.get('lounge@montague.lit');
|
||||||
expect(chatroom.get('nick')).toBe('romeo');
|
expect(chatroom.get('nick')).toBe('romeo');
|
||||||
@ -442,11 +442,11 @@ describe("A MUC", function () {
|
|||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
mock.closeControlBox(_converse);
|
mock.closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('add-chatroom-modal');
|
const modal = _converse.api.modal.get('converse-add-muc-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000)
|
await u.waitUntil(() => u.isVisible(modal), 1000)
|
||||||
const label_nick = modal.el.querySelector('label[for="nickname"]');
|
const label_nick = modal.querySelector('label[for="nickname"]');
|
||||||
expect(label_nick.textContent.trim()).toBe('Nickname:');
|
expect(label_nick.textContent.trim()).toBe('Nickname:');
|
||||||
const nick_input = modal.el.querySelector('input[name="nickname"]');
|
const nick_input = modal.querySelector('input[name="nickname"]');
|
||||||
expect(nick_input.value).toBe('romeo');
|
expect(nick_input.value).toBe('romeo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -458,11 +458,11 @@ describe("A MUC", function () {
|
|||||||
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
|
||||||
roomspanel.querySelector('.show-add-muc-modal').click();
|
roomspanel.querySelector('.show-add-muc-modal').click();
|
||||||
mock.closeControlBox(_converse);
|
mock.closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('add-chatroom-modal');
|
const modal = _converse.api.modal.get('converse-add-muc-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000)
|
await u.waitUntil(() => u.isVisible(modal), 1000)
|
||||||
const label_nick = modal.el.querySelector('label[for="nickname"]');
|
const label_nick = modal.querySelector('label[for="nickname"]');
|
||||||
expect(label_nick.textContent.trim()).toBe('Nickname:');
|
expect(label_nick.textContent.trim()).toBe('Nickname:');
|
||||||
const nick_input = modal.el.querySelector('input[name="nickname"]');
|
const nick_input = modal.querySelector('input[name="nickname"]');
|
||||||
expect(nick_input.value).toBe('st.nick');
|
expect(nick_input.value).toBe('st.nick');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import ModeratorToolsModal from './modals/moderator-tools.js';
|
import './modals/occupant.js';
|
||||||
import OccupantModal from './modals/occupant.js';
|
import './modals/moderator-tools.js';
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import tpl_spinner from 'templates/spinner.js';
|
import tpl_spinner from 'templates/spinner.js';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
@ -234,19 +234,19 @@ export function showModeratorToolsModal (muc, affiliation) {
|
|||||||
if (!muc.verifyRoles(['moderator'])) {
|
if (!muc.verifyRoles(['moderator'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let modal = api.modal.get(ModeratorToolsModal.id);
|
let modal = api.modal.get('converse-modtools-modal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.affiliation = affiliation;
|
modal.affiliation = affiliation;
|
||||||
modal.render();
|
modal.render();
|
||||||
} else {
|
} else {
|
||||||
modal = api.modal.create(ModeratorToolsModal, { affiliation, 'jid': muc.get('jid') });
|
modal = api.modal.create('converse-modtools-modal', { affiliation, 'jid': muc.get('jid') });
|
||||||
}
|
}
|
||||||
modal.show();
|
modal.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function showOccupantModal (ev, occupant) {
|
export function showOccupantModal (ev, occupant) {
|
||||||
api.modal.show(OccupantModal, { 'model': occupant }, ev);
|
api.modal.show('converse-muc-occupant-modal', { 'model': occupant }, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import './fingerprints.js';
|
import './fingerprints.js';
|
||||||
import './profile.js';
|
import './profile.js';
|
||||||
import 'modals/user-details.js';
|
import 'shared/modals/user-details.js';
|
||||||
import 'plugins/profile/index.js';
|
import 'plugins/profile/index.js';
|
||||||
import ConverseMixins from './mixins/converse.js';
|
import ConverseMixins from './mixins/converse.js';
|
||||||
import Device from './device.js';
|
import Device from './device.js';
|
||||||
|
@ -11,7 +11,7 @@ const fingerprint = (el) => html`
|
|||||||
const device_with_fingerprint = (el) => {
|
const device_with_fingerprint = (el) => {
|
||||||
const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following fingerprint');
|
const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following fingerprint');
|
||||||
return html`
|
return html`
|
||||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
<li class="fingerprint-removal-item list-group-item">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" value="${el.device.get('id')}"
|
<input type="checkbox" value="${el.device.get('id')}"
|
||||||
aria-label="${i18n_fingerprint_checkbox_label}"/>
|
aria-label="${i18n_fingerprint_checkbox_label}"/>
|
||||||
@ -26,7 +26,7 @@ const device_without_fingerprint = (el) => {
|
|||||||
const i18n_device_without_fingerprint = __('Device without a fingerprint');
|
const i18n_device_without_fingerprint = __('Device without a fingerprint');
|
||||||
const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following device');
|
const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following device');
|
||||||
return html`
|
return html`
|
||||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
<li class="fingerprint-removal-item list-group-item">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" value="${el.device.get('id')}"
|
<input type="checkbox" value="${el.device.get('id')}"
|
||||||
aria-label="${i18n_fingerprint_checkbox_label}"/>
|
aria-label="${i18n_fingerprint_checkbox_label}"/>
|
||||||
@ -49,7 +49,7 @@ const device_list = (el) => {
|
|||||||
const i18n_select_all = __('Select all');
|
const i18n_select_all = __('Select all');
|
||||||
return html`
|
return html`
|
||||||
<ul class="list-group fingerprints">
|
<ul class="list-group fingerprints">
|
||||||
<li class="list-group-item nopadding active">
|
<li class="list-group-item active">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" class="select-all" @change=${el.selectAll} title="${i18n_select_all}" aria-label="${i18n_other_devices_label}"/>
|
<input type="checkbox" class="select-all" @change=${el.selectAll} title="${i18n_select_all}" aria-label="${i18n_other_devices_label}"/>
|
||||||
${i18n_other_devices}
|
${i18n_other_devices}
|
||||||
|
@ -1047,8 +1047,8 @@ describe("The OMEMO module", function() {
|
|||||||
const view = _converse.chatboxviews.get(contact_jid);
|
const view = _converse.chatboxviews.get(contact_jid);
|
||||||
const show_modal_button = view.querySelector('.show-user-details-modal');
|
const show_modal_button = view.querySelector('.show-user-details-modal');
|
||||||
show_modal_button.click();
|
show_modal_button.click();
|
||||||
const modal = _converse.api.modal.get('user-details-modal');
|
const modal = _converse.api.modal.get('converse-user-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
@ -1068,7 +1068,7 @@ describe("The OMEMO module", function() {
|
|||||||
.c('device', {'id': '555'})
|
.c('device', {'id': '555'})
|
||||||
));
|
));
|
||||||
|
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, contact_jid, '555'));
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, contact_jid, '555'));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
@ -1097,21 +1097,21 @@ describe("The OMEMO module", function() {
|
|||||||
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'))
|
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'))
|
||||||
));
|
));
|
||||||
|
|
||||||
await u.waitUntil(() => modal.el.querySelectorAll('.fingerprints .fingerprint').length);
|
await u.waitUntil(() => modal.querySelectorAll('.fingerprints .fingerprint').length);
|
||||||
expect(modal.el.querySelectorAll('.fingerprints .fingerprint').length).toBe(1);
|
expect(modal.querySelectorAll('.fingerprints .fingerprint').length).toBe(1);
|
||||||
const el = modal.el.querySelector('.fingerprints .fingerprint');
|
const el = modal.querySelector('.fingerprints .fingerprint');
|
||||||
expect(el.textContent.trim()).toBe(
|
expect(el.textContent.trim()).toBe(
|
||||||
omemo.formatFingerprint(u.arrayBufferToHex(u.base64ToArrayBuffer('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI')))
|
omemo.formatFingerprint(u.arrayBufferToHex(u.base64ToArrayBuffer('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI')))
|
||||||
);
|
);
|
||||||
expect(modal.el.querySelectorAll('input[type="radio"]').length).toBe(2);
|
expect(modal.querySelectorAll('input[type="radio"]').length).toBe(2);
|
||||||
|
|
||||||
const devicelist = _converse.devicelists.get(contact_jid);
|
const devicelist = _converse.devicelists.get(contact_jid);
|
||||||
expect(devicelist.devices.get('555').get('trusted')).toBe(0);
|
expect(devicelist.devices.get('555').get('trusted')).toBe(0);
|
||||||
|
|
||||||
let trusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="1"]');
|
let trusted_radio = modal.querySelector('input[type="radio"][name="555"][value="1"]');
|
||||||
expect(trusted_radio.checked).toBe(true);
|
expect(trusted_radio.checked).toBe(true);
|
||||||
|
|
||||||
let untrusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="-1"]');
|
let untrusted_radio = modal.querySelector('input[type="radio"][name="555"][value="-1"]');
|
||||||
expect(untrusted_radio.checked).toBe(false);
|
expect(untrusted_radio.checked).toBe(false);
|
||||||
|
|
||||||
// Test that the device can be set to untrusted
|
// Test that the device can be set to untrusted
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import '../modal/index.js';
|
import '../modal/index.js';
|
||||||
|
import './modals/chat-status.js';
|
||||||
|
import './modals/profile.js';
|
||||||
|
import './modals/user-settings.js';
|
||||||
import './statusview.js';
|
import './statusview.js';
|
||||||
import '@converse/headless/plugins/status';
|
import '@converse/headless/plugins/status';
|
||||||
import '@converse/headless/plugins/vcard';
|
import '@converse/headless/plugins/vcard';
|
||||||
import './modals/chat-status.js';
|
|
||||||
import './modals/profile.js';
|
|
||||||
import { api, converse } from '@converse/headless/core';
|
import { api, converse } from '@converse/headless/core';
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,51 +1,37 @@
|
|||||||
import BootstrapModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_chat_status_modal from "../templates/chat-status-modal.js";
|
import tpl_chat_status_modal from "../templates/chat-status-modal.js";
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, converse } from "@converse/headless/core";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
const ChatStatusModal = BootstrapModal.extend({
|
export default class ChatStatusModal extends BaseModal {
|
||||||
id: "modal-status-change",
|
|
||||||
events: {
|
|
||||||
"submit form#set-xmpp-status": "onFormSubmitted",
|
|
||||||
"click .clear-input": "clearStatusMessage"
|
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
initialize () {
|
||||||
return tpl_chat_status_modal(
|
super.initialize();
|
||||||
Object.assign(
|
this.render();
|
||||||
this.model.toJSON(),
|
this.addEventListener('shown.bs.modal', () => {
|
||||||
this.model.vcard.toJSON(), {
|
this.querySelector('input[name="status_message"]').focus();
|
||||||
'label_away': __('Away'),
|
|
||||||
'label_busy': __('Busy'),
|
|
||||||
'label_cancel': __('Cancel'),
|
|
||||||
'label_close': __('Close'),
|
|
||||||
'label_custom_status': __('Custom status'),
|
|
||||||
'label_offline': __('Offline'),
|
|
||||||
'label_online': __('Online'),
|
|
||||||
'label_save': __('Save'),
|
|
||||||
'label_xa': __('Away for long'),
|
|
||||||
'modal_title': __('Change chat status'),
|
|
||||||
'placeholder_status_message': __('Personal status message')
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
afterRender () {
|
|
||||||
this.el.addEventListener('shown.bs.modal', () => {
|
|
||||||
this.el.querySelector('input[name="status_message"]').focus();
|
|
||||||
}, false);
|
}, false);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
renderModal () {
|
||||||
|
return tpl_chat_status_modal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Change chat status');
|
||||||
|
}
|
||||||
|
|
||||||
clearStatusMessage (ev) {
|
clearStatusMessage (ev) {
|
||||||
if (ev && ev.preventDefault) {
|
if (ev && ev.preventDefault) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
u.hideElement(this.el.querySelector('.clear-input'));
|
u.hideElement(this.querySelector('.clear-input'));
|
||||||
}
|
}
|
||||||
const roster_filter = this.el.querySelector('input[name="status_message"]');
|
const roster_filter = this.querySelector('input[name="status_message"]');
|
||||||
roster_filter.value = '';
|
roster_filter.value = '';
|
||||||
},
|
}
|
||||||
|
|
||||||
onFormSubmitted (ev) {
|
onFormSubmitted (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@ -56,9 +42,8 @@ const ChatStatusModal = BootstrapModal.extend({
|
|||||||
});
|
});
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
||||||
_converse.ChatStatusModal = ChatStatusModal;
|
_converse.ChatStatusModal = ChatStatusModal;
|
||||||
|
|
||||||
export default ChatStatusModal;
|
api.elements.define('converse-chat-status-modal', ChatStatusModal);
|
||||||
|
@ -1,52 +1,43 @@
|
|||||||
import BootstrapModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import bootstrap from "bootstrap.native";
|
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import tpl_profile_modal from "../templates/profile_modal.js";
|
import tpl_profile_modal from "../templates/profile_modal.js";
|
||||||
import Compress from 'client-compress';
|
import Compress from 'client-compress';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { _converse, api } from "@converse/headless/core";
|
||||||
|
|
||||||
const { sizzle } = converse.env;
|
const compress = new Compress({
|
||||||
|
targetSize: 0.1,
|
||||||
|
quality: 0.75,
|
||||||
|
maxWidth: 256,
|
||||||
|
maxHeight: 256
|
||||||
|
});
|
||||||
|
|
||||||
const options = {
|
export default class ProfileModal extends BaseModal {
|
||||||
targetSize: 0.1,
|
|
||||||
quality: 0.75,
|
|
||||||
maxWidth: 256,
|
|
||||||
maxHeight: 256
|
|
||||||
}
|
|
||||||
|
|
||||||
const compress = new Compress(options)
|
constructor (options) {
|
||||||
|
super(options);
|
||||||
|
this.tab = 'profile';
|
||||||
const ProfileModal = BootstrapModal.extend({
|
}
|
||||||
id: "user-profile-modal",
|
|
||||||
events: {
|
|
||||||
'submit .profile-form': 'onFormSubmitted'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
|
super.initialize();
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(this.model, 'change', this.render);
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
|
||||||
/**
|
/**
|
||||||
* Triggered when the _converse.ProfileModal has been created and initialized.
|
* Triggered when the _converse.ProfileModal has been created and initialized.
|
||||||
* @event _converse#profileModalInitialized
|
* @event _converse#profileModalInitialized
|
||||||
* @type { _converse.XMPPStatus }
|
* @type { _converse.XMPPStatus }
|
||||||
* @example _converse.api.listen.on('profileModalInitialized', status => { ... });
|
* @example _converse.api.listen.on('profileModalInitialized', status => { ... });
|
||||||
*/
|
*/
|
||||||
api.trigger('profileModalInitialized', this.model);
|
api.trigger('profileModalInitialized', this.model);
|
||||||
},
|
}
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
return tpl_profile_modal(Object.assign(
|
return tpl_profile_modal(this);
|
||||||
this.model.toJSON(),
|
}
|
||||||
this.model.vcard.toJSON(),
|
|
||||||
{ 'view': this }
|
|
||||||
));
|
|
||||||
},
|
|
||||||
|
|
||||||
afterRender () {
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
this.tabs = sizzle('.nav-item .nav-link', this.el).map(e => new bootstrap.Tab(e));
|
return __('Your Profile');
|
||||||
},
|
}
|
||||||
|
|
||||||
async setVCard (data) {
|
async setVCard (data) {
|
||||||
try {
|
try {
|
||||||
@ -60,7 +51,7 @@ const ProfileModal = BootstrapModal.extend({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
},
|
}
|
||||||
|
|
||||||
onFormSubmitted (ev) {
|
onFormSubmitted (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@ -95,8 +86,6 @@ const ProfileModal = BootstrapModal.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
_converse.ProfileModal = ProfileModal;
|
api.elements.define('converse-profile-modal', ProfileModal);
|
||||||
|
|
||||||
export default ProfileModal;
|
|
||||||
|
38
src/plugins/profile/modals/styles/profile.scss
Normal file
38
src/plugins/profile/modals/styles/profile.scss
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
converse-profile-modal {
|
||||||
|
.profile-form {
|
||||||
|
label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fingerprint-removal {
|
||||||
|
label {
|
||||||
|
display: flex;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
font-size: 95%;
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fingerprints {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fingerprint-trust {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 95%;
|
||||||
|
.fingerprint {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,41 @@
|
|||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { api } from "@converse/headless/core";
|
import { _converse, api } from "@converse/headless/core.js";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||||
|
|
||||||
|
|
||||||
const tpl_navigation = (o) => {
|
const tpl_navigation = (el) => {
|
||||||
const i18n_about = __('About');
|
const i18n_about = __('About');
|
||||||
const i18n_commands = __('Commands');
|
const i18n_commands = __('Commands');
|
||||||
return html`
|
return html`
|
||||||
<ul class="nav nav-pills justify-content-center">
|
<ul class="nav nav-pills justify-content-center">
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<a class="nav-link active" id="about-tab" href="#about-tabpanel" aria-controls="about-tabpanel" role="tab" data-toggle="tab" @click=${o.switchTab}>${i18n_about}</a>
|
<a class="nav-link ${el.tab === "about" ? "active" : ""}"
|
||||||
|
id="about-tab"
|
||||||
|
href="#about-tabpanel"
|
||||||
|
aria-controls="about-tabpanel"
|
||||||
|
role="tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
data-name="about"
|
||||||
|
@click=${ev => el.switchTab(ev)}>${i18n_about}</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<a class="nav-link" id="commands-tab" href="#commands-tabpanel" aria-controls="commands-tabpanel" role="tab" data-toggle="tab" @click=${o.switchTab}>${i18n_commands}</a>
|
<a class="nav-link ${el.tab === "commands" ? "active" : ""}"
|
||||||
|
id="commands-tab"
|
||||||
|
href="#commands-tabpanel"
|
||||||
|
aria-controls="commands-tabpanel"
|
||||||
|
role="tab"
|
||||||
|
data-toggle="tab"
|
||||||
|
data-name="commands"
|
||||||
|
@click=${ev => el.switchTab(ev)}>${i18n_commands}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
export default (el) => {
|
||||||
const i18n_modal_title = __('Settings');
|
|
||||||
const first_subtitle = __(
|
const first_subtitle = __(
|
||||||
'%1$s Open Source %2$s XMPP chat client brought to you by %3$s Opkode %2$s',
|
'%1$s Open Source %2$s XMPP chat client brought to you by %3$s Opkode %2$s',
|
||||||
'<a target="_blank" rel="nofollow" href="https://conversejs.org">',
|
'<a target="_blank" rel="nofollow" href="https://conversejs.org">',
|
||||||
@ -39,38 +51,31 @@ export default (o) => {
|
|||||||
const show_client_info = api.settings.get('show_client_info');
|
const show_client_info = api.settings.get('show_client_info');
|
||||||
const allow_adhoc_commands = api.settings.get('allow_adhoc_commands');
|
const allow_adhoc_commands = api.settings.get('allow_adhoc_commands');
|
||||||
const show_both_tabs = show_client_info && allow_adhoc_commands;
|
const show_both_tabs = show_client_info && allow_adhoc_commands;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
${ show_both_tabs ? tpl_navigation(el) : '' }
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="converse-modtools-modal-label">${i18n_modal_title}</h5>
|
|
||||||
${modal_header_close_button}
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
${ show_both_tabs ? tpl_navigation(o) : '' }
|
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane tab-pane--columns ${show_client_info ? 'active' : ''}"
|
${ show_client_info ? html`
|
||||||
id="about-tabpanel" role="tabpanel" aria-labelledby="about-tab">
|
<div class="tab-pane tab-pane--columns ${ el.tab === 'about' ? 'active' : ''}"
|
||||||
|
id="about-tabpanel" role="tabpanel" aria-labelledby="about-tab">
|
||||||
|
|
||||||
<span class="modal-alert"></span>
|
<span class="modal-alert"></span>
|
||||||
<br/>
|
<br/>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h6 class="brand-heading">Converse</h6>
|
<h6 class="brand-heading">Converse</h6>
|
||||||
<p class="brand-subtitle">${o.version_name}</p>
|
<p class="brand-subtitle">${_converse.VERSION_NAME}</p>
|
||||||
<p class="brand-subtitle">${unsafeHTML(DOMPurify.sanitize(first_subtitle))}</p>
|
<p class="brand-subtitle">${unsafeHTML(DOMPurify.sanitize(first_subtitle))}</p>
|
||||||
<p class="brand-subtitle">${unsafeHTML(DOMPurify.sanitize(second_subtitle))}</p>
|
<p class="brand-subtitle">${unsafeHTML(DOMPurify.sanitize(second_subtitle))}</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>` : '' }
|
||||||
|
|
||||||
<div class="tab-pane tab-pane--columns ${!show_client_info && allow_adhoc_commands ? 'active' : ''}"
|
${ allow_adhoc_commands ? html`
|
||||||
id="commands-tabpanel"
|
<div class="tab-pane tab-pane--columns ${ el.tab === 'commands' ? 'active' : ''}"
|
||||||
role="tabpanel"
|
id="commands-tabpanel"
|
||||||
aria-labelledby="commands-tab">
|
role="tabpanel"
|
||||||
<converse-adhoc-commands/>
|
aria-labelledby="commands-tab">
|
||||||
</div>
|
<converse-adhoc-commands/>
|
||||||
</div>
|
</div> ` : '' }
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
`};
|
`};
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
import BootstrapModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import tpl_user_settings_modal from "./templates/user-settings.js";
|
import tpl_user_settings_modal from "./templates/user-settings.js";
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import { api } from "@converse/headless/core";
|
||||||
|
|
||||||
let _converse;
|
export default class UserSettingsModal extends BaseModal {
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
constructor (options) {
|
||||||
id: "converse-client-info-modal",
|
super(options);
|
||||||
|
|
||||||
initialize (settings) {
|
const show_client_info = api.settings.get('show_client_info');
|
||||||
_converse = settings._converse;
|
const allow_adhoc_commands = api.settings.get('allow_adhoc_commands');
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
const show_both_tabs = show_client_info && allow_adhoc_commands;
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
if (show_both_tabs || show_client_info) {
|
||||||
return tpl_user_settings_modal(
|
this.tab = 'about';
|
||||||
Object.assign(
|
} else if (allow_adhoc_commands) {
|
||||||
this.model.toJSON(),
|
this.tab = 'commands';
|
||||||
this.model.vcard.toJSON(),
|
}
|
||||||
{ 'version_name': _converse.VERSION_NAME }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
renderModal () {
|
||||||
|
return tpl_user_settings_modal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Settings');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-user-settings-modal', UserSettingsModal);
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import UserSettingsModal from './modals/user-settings';
|
|
||||||
import tpl_profile from './templates/profile.js';
|
import tpl_profile from './templates/profile.js';
|
||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { _converse, api } from '@converse/headless/core';
|
import { _converse, api } from '@converse/headless/core';
|
||||||
|
|
||||||
class Profile extends CustomElement {
|
class Profile extends CustomElement {
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
this.model = _converse.xmppstatus;
|
this.model = _converse.xmppstatus;
|
||||||
this.listenTo(this.model, "change", () => this.requestUpdate());
|
this.listenTo(this.model, "change", () => this.requestUpdate());
|
||||||
@ -18,17 +16,17 @@ class Profile extends CustomElement {
|
|||||||
|
|
||||||
showProfileModal (ev) {
|
showProfileModal (ev) {
|
||||||
ev?.preventDefault();
|
ev?.preventDefault();
|
||||||
api.modal.show(_converse.ProfileModal, {model: this.model}, ev);
|
api.modal.show('converse-profile-modal', { model: this.model }, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
showStatusChangeModal (ev) {
|
showStatusChangeModal (ev) {
|
||||||
ev?.preventDefault();
|
ev?.preventDefault();
|
||||||
api.modal.show(_converse.ChatStatusModal, {model: this.model}, ev);
|
api.modal.show('converse-chat-status-modal', { model: this.model }, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
showUserSettingsModal(ev) {
|
showUserSettingsModal (ev) {
|
||||||
ev?.preventDefault();
|
ev?.preventDefault();
|
||||||
api.modal.show(UserSettingsModal, {model: this.model, _converse}, ev);
|
api.modal.show('converse-user-settings-modal', { model: this.model, _converse }, ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +1,52 @@
|
|||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js";
|
import { __ } from 'i18n';
|
||||||
|
|
||||||
|
|
||||||
export default (o) => html`
|
export default (el) => {
|
||||||
<div class="modal-dialog" role="document">
|
const label_away = __('Away');
|
||||||
<div class="modal-content">
|
const label_busy = __('Busy');
|
||||||
<div class="modal-header">
|
const label_online = __('Online');
|
||||||
<h5 class="modal-title" id="changeStatusModalLabel">${o.modal_title}</h5>
|
const label_save = __('Save');
|
||||||
${modal_header_close_button}
|
const label_xa = __('Away for long');
|
||||||
|
const placeholder_status_message = __('Personal status message');
|
||||||
|
const status = el.model.get('status');
|
||||||
|
const status_message = el.model.get('status_message');
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<form class="converse-form set-xmpp-status" id="set-xmpp-status" @submit=${ev => el.onFormSubmitted(ev)}>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="custom-control custom-radio">
|
||||||
|
<input ?checked=${status === 'online'}
|
||||||
|
type="radio" id="radio-online" value="online" name="chat_status" class="custom-control-input"/>
|
||||||
|
<label class="custom-control-label" for="radio-online">
|
||||||
|
<converse-icon size="1em" class="fa fa-circle chat-status chat-status--online"></converse-icon>${label_online}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="custom-control custom-radio">
|
||||||
<span class="modal-alert"></span>
|
<input ?checked=${status === 'busy'}
|
||||||
<form class="converse-form set-xmpp-status" id="set-xmpp-status">
|
type="radio" id="radio-busy" value="dnd" name="chat_status" class="custom-control-input"/>
|
||||||
<div class="form-group">
|
<label class="custom-control-label" for="radio-busy">
|
||||||
<div class="custom-control custom-radio">
|
<converse-icon size="1em" class="fa fa-minus-circle chat-status chat-status--busy"></converse-icon>${label_busy}</label>
|
||||||
<input ?checked=${o.status === 'online'}
|
</div>
|
||||||
type="radio" id="radio-online" value="online" name="chat_status" class="custom-control-input"/>
|
<div class="custom-control custom-radio">
|
||||||
<label class="custom-control-label" for="radio-online">
|
<input ?checked=${status === 'away'}
|
||||||
<converse-icon size="1em" class="fa fa-circle chat-status chat-status--online"></converse-icon>${o.label_online}</label>
|
type="radio" id="radio-away" value="away" name="chat_status" class="custom-control-input"/>
|
||||||
</div>
|
<label class="custom-control-label" for="radio-away">
|
||||||
<div class="custom-control custom-radio">
|
<converse-icon size="1em" class="fa fa-circle chat-status chat-status--away"></converse-icon>${label_away}</label>
|
||||||
<input ?checked=${o.status === 'busy'}
|
</div>
|
||||||
type="radio" id="radio-busy" value="dnd" name="chat_status" class="custom-control-input"/>
|
<div class="custom-control custom-radio">
|
||||||
<label class="custom-control-label" for="radio-busy">
|
<input ?checked=${status === 'xa'}
|
||||||
<converse-icon size="1em" class="fa fa-minus-circle chat-status chat-status--busy"></converse-icon>${o.label_busy}</label>
|
type="radio" id="radio-xa" value="xa" name="chat_status" class="custom-control-input"/>
|
||||||
</div>
|
<label class="custom-control-label" for="radio-xa">
|
||||||
<div class="custom-control custom-radio">
|
<converse-icon size="1em" class="far fa-circle chat-status chat-status--xa"></converse-icon>${label_xa}</label>
|
||||||
<input ?checked=${o.status === 'away'}
|
|
||||||
type="radio" id="radio-away" value="away" name="chat_status" class="custom-control-input"/>
|
|
||||||
<label class="custom-control-label" for="radio-away">
|
|
||||||
<converse-icon size="1em" class="fa fa-circle chat-status chat-status--away"></converse-icon>${o.label_away}</label>
|
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input ?checked=${o.status === 'xa'}
|
|
||||||
type="radio" id="radio-xa" value="xa" name="chat_status" class="custom-control-input"/>
|
|
||||||
<label class="custom-control-label" for="radio-xa">
|
|
||||||
<converse-icon size="1em" class="far fa-circle chat-status chat-status--xa"></converse-icon>${o.label_xa}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="btn-group w-100">
|
|
||||||
<input name="status_message" type="text" class="form-control"
|
|
||||||
value="${o.status_message || ''}" placeholder="${o.placeholder_status_message}"/>
|
|
||||||
<converse-icon size="1em" class="fa fa-times clear-input ${o.status_message ? '' : 'hidden'}"></converse-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary">${o.label_save}</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
`;
|
<div class="btn-group w-100">
|
||||||
|
<input name="status_message" type="text" class="form-control" autofocus
|
||||||
|
value="${status_message || ''}" placeholder="${placeholder_status_message}"/>
|
||||||
|
<converse-icon size="1em" class="fa fa-times clear-input ${status_message ? '' : 'hidden'}" @click=${ev => el.clearStatusMessage(ev)}></converse-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">${label_save}</button>
|
||||||
|
</form>`;
|
||||||
|
}
|
||||||
|
@ -2,16 +2,40 @@ import "shared/components/image-picker.js";
|
|||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse } from "@converse/headless/core";
|
import { _converse } from "@converse/headless/core";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js";
|
|
||||||
|
|
||||||
const omemo_page = () => html`
|
const omemo_page = (el) => html`
|
||||||
<div class="tab-pane" id="omemo-tabpanel" role="tabpanel" aria-labelledby="omemo-tab">
|
<div class="tab-pane ${ el.tab === 'omemo' ? 'active' : ''}" id="omemo-tabpanel" role="tabpanel" aria-labelledby="omemo-tab">
|
||||||
<converse-omemo-profile></converse-omemo-profile>
|
<converse-omemo-profile></converse-omemo-profile>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
|
const navigation = (el) => {
|
||||||
|
const i18n_omemo = __('OMEMO');
|
||||||
|
const i18n_profile = __('Profile');
|
||||||
|
|
||||||
export default (o) => {
|
return html`<ul class="nav nav-pills justify-content-center">
|
||||||
const heading_profile = __('Your Profile');
|
<li role="presentation" class="nav-item">
|
||||||
|
<a class="nav-link ${el.tab === "profile" ? "active" : ""}"
|
||||||
|
id="profile-tab"
|
||||||
|
href="#profile-tabpanel"
|
||||||
|
aria-controls="profile-tabpanel" role="tab"
|
||||||
|
@click=${ev => el.switchTab(ev)}
|
||||||
|
data-name="profile"
|
||||||
|
data-toggle="tab">${i18n_profile}</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation" class="nav-item">
|
||||||
|
<a class="nav-link ${el.tab === "omemo" ? "active" : ""}"
|
||||||
|
id="omemo-tab"
|
||||||
|
href="#omemo-tabpanel"
|
||||||
|
aria-controls="omemo-tabpanel" role="tab"
|
||||||
|
@click=${ev => el.switchTab(ev)}
|
||||||
|
data-name="omemo"
|
||||||
|
data-toggle="tab">${i18n_omemo}</a>
|
||||||
|
</li>
|
||||||
|
</ul>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (el) => {
|
||||||
|
const o = { ...el.model.toJSON(), ...el.model.vcard.toJSON() };
|
||||||
const i18n_email = __('Email');
|
const i18n_email = __('Email');
|
||||||
const i18n_fullname = __('Full Name');
|
const i18n_fullname = __('Full Name');
|
||||||
const i18n_jid = __('XMPP Address');
|
const i18n_jid = __('XMPP Address');
|
||||||
@ -20,74 +44,51 @@ export default (o) => {
|
|||||||
const i18n_save = __('Save and close');
|
const i18n_save = __('Save and close');
|
||||||
const i18n_role_help = __('Use commas to separate multiple roles. Your roles are shown next to your name on your chat messages.');
|
const i18n_role_help = __('Use commas to separate multiple roles. Your roles are shown next to your name on your chat messages.');
|
||||||
const i18n_url = __('URL');
|
const i18n_url = __('URL');
|
||||||
const i18n_omemo = __('OMEMO');
|
|
||||||
const i18n_profile = __('Profile');
|
|
||||||
|
|
||||||
const navigation =
|
|
||||||
html`<ul class="nav nav-pills justify-content-center">
|
|
||||||
<li role="presentation" class="nav-item">
|
|
||||||
<a class="nav-link active" id="profile-tab" href="#profile-tabpanel" aria-controls="profile-tabpanel" role="tab" data-toggle="tab">${i18n_profile}</a>
|
|
||||||
</li>
|
|
||||||
<li role="presentation" class="nav-item">
|
|
||||||
<a class="nav-link" id="omemo-tab" href="#omemo-tabpanel" aria-controls="omemo-tabpanel" role="tab" data-toggle="tab">${i18n_omemo}</a>
|
|
||||||
</li>
|
|
||||||
</ul>`;
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
${_converse.pluggable.plugins['converse-omemo']?.enabled(_converse) ? navigation(el) : ''}
|
||||||
<div class="modal-content">
|
<div class="tab-content">
|
||||||
<div class="modal-header">
|
<div class="tab-pane ${ el.tab === 'profile' ? 'active' : ''}" id="profile-tabpanel" role="tabpanel" aria-labelledby="profile-tab">
|
||||||
<h5 class="modal-title" id="user-profile-modal-label">${heading_profile}</h5>
|
<form class="converse-form converse-form--modal profile-form" action="#" @submit=${ev => el.onFormSubmitted(ev)}>
|
||||||
${modal_header_close_button}
|
<div class="row">
|
||||||
</div>
|
<div class="col-auto">
|
||||||
<div class="modal-body">
|
<converse-image-picker .data="${{image: o.image, image_type: o.image_type}}" width="128" height="128"></converse-image-picker>
|
||||||
<span class="modal-alert"></span>
|
</div>
|
||||||
${_converse.pluggable.plugins['converse-omemo']?.enabled(_converse) ? navigation : ''}
|
<div class="col">
|
||||||
<div class="tab-content">
|
<div class="form-group">
|
||||||
<div class="tab-pane active" id="profile-tabpanel" role="tabpanel" aria-labelledby="profile-tab">
|
<label class="col-form-label">${i18n_jid}:</label>
|
||||||
<form class="converse-form converse-form--modal profile-form" action="#">
|
<div>${o.jid}</div>
|
||||||
<div class="row">
|
</div>
|
||||||
<div class="col-auto">
|
|
||||||
<converse-image-picker .data="${{image: o.image, image_type: o.image_type}}" width="128" height="128"></converse-image-picker>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-form-label">${i18n_jid}:</label>
|
|
||||||
<div>${o.jid}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="vcard-fullname" class="col-form-label">${i18n_fullname}:</label>
|
|
||||||
<input id="vcard-fullname" type="text" class="form-control" name="fn" value="${o.fullname || ''}"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="vcard-nickname" class="col-form-label">${i18n_nickname}:</label>
|
|
||||||
<input id="vcard-nickname" type="text" class="form-control" name="nickname" value="${o.nickname || ''}"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="vcard-url" class="col-form-label">${i18n_url}:</label>
|
|
||||||
<input id="vcard-url" type="url" class="form-control" name="url" value="${o.url || ''}"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="vcard-email" class="col-form-label">${i18n_email}:</label>
|
|
||||||
<input id="vcard-email" type="email" class="form-control" name="email" value="${o.email || ''}"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="vcard-role" class="col-form-label">${i18n_role}:</label>
|
|
||||||
<input id="vcard-role" type="text" class="form-control" name="role" value="${o.role || ''}" aria-describedby="vcard-role-help"/>
|
|
||||||
<small id="vcard-role-help" class="form-text text-muted">${i18n_role_help}</small>
|
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
<div class="form-group">
|
|
||||||
<button type="submit" class="save-form btn btn-primary">${i18n_save}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
${ _converse.pluggable.plugins['converse-omemo']?.enabled(_converse) ? omemo_page() : '' }
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
|
<label for="vcard-fullname" class="col-form-label">${i18n_fullname}:</label>
|
||||||
|
<input id="vcard-fullname" type="text" class="form-control" name="fn" value="${o.fullname || ''}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="vcard-nickname" class="col-form-label">${i18n_nickname}:</label>
|
||||||
|
<input id="vcard-nickname" type="text" class="form-control" name="nickname" value="${o.nickname || ''}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="vcard-url" class="col-form-label">${i18n_url}:</label>
|
||||||
|
<input id="vcard-url" type="url" class="form-control" name="url" value="${o.url || ''}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="vcard-email" class="col-form-label">${i18n_email}:</label>
|
||||||
|
<input id="vcard-email" type="email" class="form-control" name="email" value="${o.email || ''}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="vcard-role" class="col-form-label">${i18n_role}:</label>
|
||||||
|
<input id="vcard-role" type="text" class="form-control" name="role" value="${o.role || ''}" aria-describedby="vcard-role-help"/>
|
||||||
|
<small id="vcard-role-help" class="form-text text-muted">${i18n_role_help}</small>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="save-form btn btn-primary">${i18n_save}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
${ _converse.pluggable.plugins['converse-omemo']?.enabled(_converse) ? omemo_page(el) : '' }
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import AddMUCModal from 'plugins/muc-views/modals/add-muc.js';
|
import 'plugins/muc-views/modals/add-muc.js';
|
||||||
import MUCListModal from 'plugins/muc-views/modals/muc-list.js';
|
import 'plugins/muc-views/modals/muc-list.js';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, api } from "@converse/headless/core";
|
import { _converse, api } from "@converse/headless/core";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
@ -80,12 +80,12 @@ export default (o) => {
|
|||||||
<div class="d-flex controlbox-padded">
|
<div class="d-flex controlbox-padded">
|
||||||
<span class="w-100 controlbox-heading controlbox-heading--groupchats">${i18n_heading_chatrooms}</span>
|
<span class="w-100 controlbox-heading controlbox-heading--groupchats">${i18n_heading_chatrooms}</span>
|
||||||
<a class="controlbox-heading__btn show-list-muc-modal"
|
<a class="controlbox-heading__btn show-list-muc-modal"
|
||||||
@click=${(ev) => api.modal.show(MUCListModal, { 'model': o.model }, ev)}
|
@click=${(ev) => api.modal.show('converse-muc-list-modal', { 'model': o.model }, ev)}
|
||||||
title="${i18n_title_list_rooms}" data-toggle="modal" data-target="#muc-list-modal">
|
title="${i18n_title_list_rooms}" data-toggle="modal" data-target="#muc-list-modal">
|
||||||
<converse-icon class="fa fa-list-ul right" size="1em"></converse-icon>
|
<converse-icon class="fa fa-list-ul right" size="1em"></converse-icon>
|
||||||
</a>
|
</a>
|
||||||
<a class="controlbox-heading__btn show-add-muc-modal"
|
<a class="controlbox-heading__btn show-add-muc-modal"
|
||||||
@click=${(ev) => api.modal.show(AddMUCModal, { 'model': o.model }, ev)}
|
@click=${(ev) => api.modal.show('converse-add-muc-modal', { 'model': o.model }, ev)}
|
||||||
title="${i18n_title_new_room}" data-toggle="modal" data-target="#add-chatrooms-modal">
|
title="${i18n_title_new_room}" data-toggle="modal" data-target="#add-chatrooms-modal">
|
||||||
<converse-icon class="fa fa-plus right" size="1em"></converse-icon>
|
<converse-icon class="fa fa-plus right" size="1em"></converse-icon>
|
||||||
</a>
|
</a>
|
||||||
|
@ -255,9 +255,9 @@ describe("A groupchat shown in the groupchats list", function () {
|
|||||||
const info_el = rooms_list.querySelector(".room-info");
|
const info_el = rooms_list.querySelector(".room-info");
|
||||||
info_el.click();
|
info_el.click();
|
||||||
|
|
||||||
const modal = _converse.api.modal.get('muc-details-modal');
|
const modal = _converse.api.modal.get('converse-muc-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
let els = modal.el.querySelectorAll('p.room-info');
|
let els = modal.querySelectorAll('p.room-info');
|
||||||
expect(els[0].textContent).toBe("Name: A Dark Cave")
|
expect(els[0].textContent).toBe("Name: A Dark Cave")
|
||||||
|
|
||||||
expect(els[1].querySelector('strong').textContent).toBe("XMPP address");
|
expect(els[1].querySelector('strong').textContent).toBe("XMPP address");
|
||||||
@ -266,7 +266,7 @@ describe("A groupchat shown in the groupchats list", function () {
|
|||||||
expect(els[2].querySelector('converse-rich-text').textContent).toBe("This is the description");
|
expect(els[2].querySelector('converse-rich-text').textContent).toBe("This is the description");
|
||||||
|
|
||||||
expect(els[3].textContent).toBe("Online users: 1")
|
expect(els[3].textContent).toBe("Online users: 1")
|
||||||
const features_list = modal.el.querySelector('.features-list');
|
const features_list = modal.querySelector('.features-list');
|
||||||
expect(features_list.textContent.replace(/(\n|\s{2,})/g, '')).toBe(
|
expect(features_list.textContent.replace(/(\n|\s{2,})/g, '')).toBe(
|
||||||
'Password protected - This groupchat requires a password before entry'+
|
'Password protected - This groupchat requires a password before entry'+
|
||||||
'Hidden - This groupchat is not publicly searchable'+
|
'Hidden - This groupchat is not publicly searchable'+
|
||||||
@ -287,11 +287,11 @@ describe("A groupchat shown in the groupchats list", function () {
|
|||||||
});
|
});
|
||||||
_converse.connection._dataRecv(mock.createRequest(presence));
|
_converse.connection._dataRecv(mock.createRequest(presence));
|
||||||
|
|
||||||
els = modal.el.querySelectorAll('p.room-info');
|
els = modal.querySelectorAll('p.room-info');
|
||||||
expect(els[3].textContent).toBe("Online users: 2")
|
expect(els[3].textContent).toBe("Online users: 2")
|
||||||
|
|
||||||
view.model.set({'subject': {'author': 'someone', 'text': 'Hatching dark plots'}});
|
view.model.set({'subject': {'author': 'someone', 'text': 'Hatching dark plots'}});
|
||||||
els = modal.el.querySelectorAll('p.room-info');
|
els = modal.querySelectorAll('p.room-info');
|
||||||
expect(els[0].textContent).toBe("Name: A Dark Cave")
|
expect(els[0].textContent).toBe("Name: A Dark Cave")
|
||||||
|
|
||||||
expect(els[1].querySelector('strong').textContent).toBe("XMPP address");
|
expect(els[1].querySelector('strong').textContent).toBe("XMPP address");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import RoomDetailsModal from 'plugins/muc-views/modals/muc-details.js';
|
import 'plugins/muc-views/modals/muc-details.js';
|
||||||
import RoomsListModel from './model.js';
|
import RoomsListModel from './model.js';
|
||||||
import tpl_roomslist from "./templates/roomslist.js";
|
import tpl_roomslist from "./templates/roomslist.js";
|
||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
@ -58,7 +58,7 @@ export class RoomsList extends CustomElement {
|
|||||||
const jid = ev.currentTarget.getAttribute('data-room-jid');
|
const jid = ev.currentTarget.getAttribute('data-room-jid');
|
||||||
const room = _converse.chatboxes.get(jid);
|
const room = _converse.chatboxes.get(jid);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
api.modal.show(RoomDetailsModal, {'model': room}, ev);
|
api.modal.show('converse-muc-details-modal', {'model': room}, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openRoom (ev) { // eslint-disable-line class-methods-use-this
|
async openRoom (ev) { // eslint-disable-line class-methods-use-this
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'shared/autocomplete/index.js';
|
import 'shared/autocomplete/index.js';
|
||||||
import BootstrapModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import compact from 'lodash-es/compact';
|
import compact from 'lodash-es/compact';
|
||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
import tpl_add_contact_modal from "./templates/add-contact.js";
|
import tpl_add_contact_modal from "./templates/add-contact.js";
|
||||||
@ -9,18 +9,22 @@ import { _converse, api, converse } from "@converse/headless/core";
|
|||||||
const { Strophe } = converse.env;
|
const { Strophe } = converse.env;
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
export default class AddContactModal extends BaseModal {
|
||||||
const AddContactModal = BootstrapModal.extend({
|
|
||||||
id: "add-contact-modal",
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
super.initialize();
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(this.model, 'change', () => this.render());
|
||||||
},
|
this.render();
|
||||||
|
this.addEventListener('shown.bs.modal', () => this.querySelector('input[name="jid"]')?.focus(), false);
|
||||||
|
}
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
return tpl_add_contact_modal(this);
|
return tpl_add_contact_modal(this);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Add a Contact');
|
||||||
|
}
|
||||||
|
|
||||||
afterRender () {
|
afterRender () {
|
||||||
if (typeof api.settings.get('xhr_user_search_url') === 'string') {
|
if (typeof api.settings.get('xhr_user_search_url') === 'string') {
|
||||||
@ -28,39 +32,37 @@ const AddContactModal = BootstrapModal.extend({
|
|||||||
} else {
|
} else {
|
||||||
this.initJIDAutoComplete();
|
this.initJIDAutoComplete();
|
||||||
}
|
}
|
||||||
const jid_input = this.el.querySelector('input[name="jid"]');
|
}
|
||||||
this.el.addEventListener('shown.bs.modal', () => jid_input.focus(), false);
|
|
||||||
},
|
|
||||||
|
|
||||||
initJIDAutoComplete () {
|
initJIDAutoComplete () {
|
||||||
if (!api.settings.get('autocomplete_add_contact')) {
|
if (!api.settings.get('autocomplete_add_contact')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const el = this.el.querySelector('.suggestion-box__jid').parentElement;
|
const el = this.querySelector('.suggestion-box__jid').parentElement;
|
||||||
this.jid_auto_complete = new _converse.AutoComplete(el, {
|
this.jid_auto_complete = new _converse.AutoComplete(el, {
|
||||||
'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`,
|
'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`,
|
||||||
'filter': _converse.FILTER_STARTSWITH,
|
'filter': _converse.FILTER_STARTSWITH,
|
||||||
'list': [...new Set(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))]
|
'list': [...new Set(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))]
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
initGroupAutoComplete () {
|
initGroupAutoComplete () {
|
||||||
if (!api.settings.get('autocomplete_add_contact')) {
|
if (!api.settings.get('autocomplete_add_contact')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const el = this.el.querySelector('.suggestion-box__jid').parentElement;
|
const el = this.querySelector('.suggestion-box__jid').parentElement;
|
||||||
this.jid_auto_complete = new _converse.AutoComplete(el, {
|
this.jid_auto_complete = new _converse.AutoComplete(el, {
|
||||||
'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`,
|
'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`,
|
||||||
'filter': _converse.FILTER_STARTSWITH,
|
'filter': _converse.FILTER_STARTSWITH,
|
||||||
'list': [...new Set(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))]
|
'list': [...new Set(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))]
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
initXHRAutoComplete () {
|
initXHRAutoComplete () {
|
||||||
if (!api.settings.get('autocomplete_add_contact')) {
|
if (!api.settings.get('autocomplete_add_contact')) {
|
||||||
return this.initXHRFetch();
|
return this.initXHRFetch();
|
||||||
}
|
}
|
||||||
const el = this.el.querySelector('.suggestion-box__name').parentElement;
|
const el = this.querySelector('.suggestion-box__name').parentElement;
|
||||||
this.name_auto_complete = new _converse.AutoComplete(el, {
|
this.name_auto_complete = new _converse.AutoComplete(el, {
|
||||||
'auto_evaluate': false,
|
'auto_evaluate': false,
|
||||||
'filter': _converse.FILTER_STARTSWITH,
|
'filter': _converse.FILTER_STARTSWITH,
|
||||||
@ -76,16 +78,16 @@ const AddContactModal = BootstrapModal.extend({
|
|||||||
this.name_auto_complete.evaluate();
|
this.name_auto_complete.evaluate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const input_el = this.el.querySelector('input[name="name"]');
|
const input_el = this.querySelector('input[name="name"]');
|
||||||
input_el.addEventListener('input', debounce(() => {
|
input_el.addEventListener('input', debounce(() => {
|
||||||
xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
||||||
xhr.send()
|
xhr.send()
|
||||||
} , 300));
|
} , 300));
|
||||||
this.name_auto_complete.on('suggestion-box-selectcomplete', ev => {
|
this.name_auto_complete.on('suggestion-box-selectcomplete', ev => {
|
||||||
this.el.querySelector('input[name="name"]').value = ev.text.label;
|
this.querySelector('input[name="name"]').value = ev.text.label;
|
||||||
this.el.querySelector('input[name="jid"]').value = ev.text.value;
|
this.querySelector('input[name="jid"]').value = ev.text.value;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
initXHRFetch () {
|
initXHRFetch () {
|
||||||
this.xhr = new window.XMLHttpRequest();
|
this.xhr = new window.XMLHttpRequest();
|
||||||
@ -94,25 +96,25 @@ const AddContactModal = BootstrapModal.extend({
|
|||||||
const r = this.xhr.responseText;
|
const r = this.xhr.responseText;
|
||||||
const list = JSON.parse(r).map(i => ({'label': i.fullname || i.jid, 'value': i.jid}));
|
const list = JSON.parse(r).map(i => ({'label': i.fullname || i.jid, 'value': i.jid}));
|
||||||
if (list.length !== 1) {
|
if (list.length !== 1) {
|
||||||
const el = this.el.querySelector('.invalid-feedback');
|
const el = this.querySelector('.invalid-feedback');
|
||||||
el.textContent = __('Sorry, could not find a contact with that name')
|
el.textContent = __('Sorry, could not find a contact with that name')
|
||||||
u.addClass('d-block', el);
|
u.addClass('d-block', el);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const jid = list[0].value;
|
const jid = list[0].value;
|
||||||
if (this.validateSubmission(jid)) {
|
if (this.validateSubmission(jid)) {
|
||||||
const form = this.el.querySelector('form');
|
const form = this.querySelector('form');
|
||||||
const name = list[0].label;
|
const name = list[0].label;
|
||||||
this.afterSubmission(form, jid, name);
|
this.afterSubmission(form, jid, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
validateSubmission (jid) {
|
validateSubmission (jid) {
|
||||||
const el = this.el.querySelector('.invalid-feedback');
|
const el = this.querySelector('.invalid-feedback');
|
||||||
if (!jid || compact(jid.split('@')).length < 2) {
|
if (!jid || compact(jid.split('@')).length < 2) {
|
||||||
u.addClass('is-invalid', this.el.querySelector('input[name="jid"]'));
|
u.addClass('is-invalid', this.querySelector('input[name="jid"]'));
|
||||||
u.addClass('d-block', el);
|
u.addClass('d-block', el);
|
||||||
return false;
|
return false;
|
||||||
} else if (_converse.roster.get(Strophe.getBareJidFromJid(jid))) {
|
} else if (_converse.roster.get(Strophe.getBareJidFromJid(jid))) {
|
||||||
@ -122,16 +124,16 @@ const AddContactModal = BootstrapModal.extend({
|
|||||||
}
|
}
|
||||||
u.removeClass('d-block', el);
|
u.removeClass('d-block', el);
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
afterSubmission (form, jid, name, group) {
|
afterSubmission (_form, jid, name, group) {
|
||||||
if (group && !Array.isArray(group)) {
|
if (group && !Array.isArray(group)) {
|
||||||
group = [group];
|
group = [group];
|
||||||
}
|
}
|
||||||
_converse.roster.addAndSubscribe(jid, name, group);
|
_converse.roster.addAndSubscribe(jid, name, group);
|
||||||
this.model.clear();
|
this.model.clear();
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
},
|
}
|
||||||
|
|
||||||
addContactFromForm (ev) {
|
addContactFromForm (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
@ -139,7 +141,7 @@ const AddContactModal = BootstrapModal.extend({
|
|||||||
const jid = (data.get('jid') || '').trim();
|
const jid = (data.get('jid') || '').trim();
|
||||||
|
|
||||||
if (!jid && typeof api.settings.get('xhr_user_search_url') === 'string') {
|
if (!jid && typeof api.settings.get('xhr_user_search_url') === 'string') {
|
||||||
const input_el = this.el.querySelector('input[name="name"]');
|
const input_el = this.querySelector('input[name="name"]');
|
||||||
this.xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
this.xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
||||||
this.xhr.send()
|
this.xhr.send()
|
||||||
return;
|
return;
|
||||||
@ -148,8 +150,6 @@ const AddContactModal = BootstrapModal.extend({
|
|||||||
this.afterSubmission(ev.target, jid, data.get('name'), data.get('group'));
|
this.afterSubmission(ev.target, jid, data.get('name'), data.get('group'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
_converse.AddContactModal = AddContactModal;
|
api.elements.define('converse-add-contact-modal', AddContactModal);
|
||||||
|
|
||||||
export default AddContactModal;
|
|
||||||
|
@ -2,7 +2,6 @@ import { __ } from 'i18n';
|
|||||||
import { api } from '@converse/headless/core.js';
|
import { api } from '@converse/headless/core.js';
|
||||||
import { getGroupsAutoCompleteList } from '@converse/headless/plugins/roster/utils.js';
|
import { getGroupsAutoCompleteList } from '@converse/headless/plugins/roster/utils.js';
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
|
|
||||||
|
|
||||||
|
|
||||||
export default (el) => {
|
export default (el) => {
|
||||||
@ -10,51 +9,40 @@ export default (el) => {
|
|||||||
const i18n_contact_placeholder = __('name@example.org');
|
const i18n_contact_placeholder = __('name@example.org');
|
||||||
const i18n_error_message = __('Please enter a valid XMPP address');
|
const i18n_error_message = __('Please enter a valid XMPP address');
|
||||||
const i18n_group = __('Group');
|
const i18n_group = __('Group');
|
||||||
const i18n_new_contact = __('Add a Contact');
|
|
||||||
const i18n_nickname = __('Name');
|
const i18n_nickname = __('Name');
|
||||||
const i18n_xmpp_address = __('XMPP Address');
|
const i18n_xmpp_address = __('XMPP Address');
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="modal-dialog" role="document">
|
<form class="converse-form add-xmpp-contact" @submit=${ev => el.addContactFromForm(ev)}>
|
||||||
<div class="modal-content">
|
<div class="modal-body">
|
||||||
<div class="modal-header">
|
<span class="modal-alert"></span>
|
||||||
<h5 class="modal-title" id="addContactModalLabel">${i18n_new_contact}</h5>
|
<div class="form-group add-xmpp-contact__jid">
|
||||||
${modal_header_close_button}
|
<label class="clearfix" for="jid">${i18n_xmpp_address}:</label>
|
||||||
</div>
|
<div class="suggestion-box suggestion-box__jid">
|
||||||
<form class="converse-form add-xmpp-contact" @submit=${ev => el.addContactFromForm(ev)}>
|
<ul class="suggestion-box__results suggestion-box__results--below" hidden=""></ul>
|
||||||
<div class="modal-body">
|
<input type="text" name="jid" ?required=${(!api.settings.get('xhr_user_search_url'))}
|
||||||
<span class="modal-alert"></span>
|
value="${el.model.get('jid') || ''}"
|
||||||
<div class="form-group add-xmpp-contact__jid">
|
class="form-control suggestion-box__input"
|
||||||
<label class="clearfix" for="jid">${i18n_xmpp_address}:</label>
|
placeholder="${i18n_contact_placeholder}"/>
|
||||||
<div class="suggestion-box suggestion-box__jid">
|
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
||||||
<ul class="suggestion-box__results suggestion-box__results--below" hidden=""></ul>
|
|
||||||
<input type="text" name="jid" ?required=${(!api.settings.get('xhr_user_search_url'))}
|
|
||||||
value="${el.model.get('jid') || ''}"
|
|
||||||
class="form-control suggestion-box__input"
|
|
||||||
placeholder="${i18n_contact_placeholder}"/>
|
|
||||||
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group add-xmpp-contact__name">
|
|
||||||
<label class="clearfix" for="name">${i18n_nickname}:</label>
|
|
||||||
<div class="suggestion-box suggestion-box__name">
|
|
||||||
<ul class="suggestion-box__results suggestion-box__results--above" hidden=""></ul>
|
|
||||||
<input type="text" name="name" value="${el.model.get('nickname') || ''}"
|
|
||||||
class="form-control suggestion-box__input"/>
|
|
||||||
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group add-xmpp-contact__group">
|
|
||||||
<label class="clearfix" for="name">${i18n_group}:</label>
|
|
||||||
<converse-autocomplete .list=${getGroupsAutoCompleteList()} name="group"></converse-autocomplete>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group"><div class="invalid-feedback">${i18n_error_message}</div></div>
|
|
||||||
<button type="submit" class="btn btn-primary">${i18n_add}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group add-xmpp-contact__name">
|
||||||
|
<label class="clearfix" for="name">${i18n_nickname}:</label>
|
||||||
|
<div class="suggestion-box suggestion-box__name">
|
||||||
|
<ul class="suggestion-box__results suggestion-box__results--above" hidden=""></ul>
|
||||||
|
<input type="text" name="name" value="${el.model.get('nickname') || ''}"
|
||||||
|
class="form-control suggestion-box__input"/>
|
||||||
|
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group add-xmpp-contact__group">
|
||||||
|
<label class="clearfix" for="name">${i18n_group}:</label>
|
||||||
|
<converse-autocomplete .list=${getGroupsAutoCompleteList()} name="group"></converse-autocomplete>
|
||||||
|
</div>
|
||||||
|
<div class="form-group"><div class="invalid-feedback">${i18n_error_message}</div></div>
|
||||||
|
<button type="submit" class="btn btn-primary">${i18n_add}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>`;
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,14 @@ export default class RosterView extends CustomElement {
|
|||||||
|
|
||||||
async initialize () {
|
async initialize () {
|
||||||
await api.waitUntil('rosterInitialized')
|
await api.waitUntil('rosterInitialized')
|
||||||
|
const { presences, roster } = _converse;
|
||||||
this.listenTo(_converse, 'rosterContactsFetched', () => this.requestUpdate());
|
this.listenTo(_converse, 'rosterContactsFetched', () => this.requestUpdate());
|
||||||
this.listenTo(_converse.presences, 'change:show', () => this.requestUpdate());
|
this.listenTo(presences, 'change:show', () => this.requestUpdate());
|
||||||
this.listenTo(_converse.roster, 'add', () => this.requestUpdate());
|
this.listenTo(roster, 'add', () => this.requestUpdate());
|
||||||
this.listenTo(_converse.roster, 'destroy', () => this.requestUpdate());
|
this.listenTo(roster, 'destroy', () => this.requestUpdate());
|
||||||
this.listenTo(_converse.roster, 'remove', () => this.requestUpdate());
|
this.listenTo(roster, 'remove', () => this.requestUpdate());
|
||||||
this.listenTo(_converse.roster, 'change', () => this.requestUpdate());
|
this.listenTo(roster, 'change', () => this.requestUpdate());
|
||||||
this.listenTo(_converse.roster.state, 'change', () => this.requestUpdate());
|
this.listenTo(roster.state, 'change', () => this.requestUpdate());
|
||||||
/**
|
/**
|
||||||
* Triggered once the _converse.RosterView instance has been created and initialized.
|
* Triggered once the _converse.RosterView instance has been created and initialized.
|
||||||
* @event _converse#rosterViewInitialized
|
* @event _converse#rosterViewInitialized
|
||||||
@ -42,7 +43,7 @@ export default class RosterView extends CustomElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showAddContactModal (ev) { // eslint-disable-line class-methods-use-this
|
showAddContactModal (ev) { // eslint-disable-line class-methods-use-this
|
||||||
api.modal.show(_converse.AddContactModal, {'model': new Model()}, ev);
|
api.modal.show('converse-add-contact-modal', {'model': new Model()}, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncContacts (ev) { // eslint-disable-line class-methods-use-this
|
async syncContacts (ev) { // eslint-disable-line class-methods-use-this
|
||||||
|
195
src/plugins/rosterview/tests/add-contact-modal.js
Normal file
195
src/plugins/rosterview/tests/add-contact-modal.js
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/*global mock, converse */
|
||||||
|
|
||||||
|
const u = converse.env.utils;
|
||||||
|
const Strophe = converse.env.Strophe;
|
||||||
|
const sizzle = converse.env.sizzle;
|
||||||
|
|
||||||
|
describe("The 'Add Contact' widget", function () {
|
||||||
|
|
||||||
|
it("opens up an add modal when you click on it",
|
||||||
|
mock.initConverse([], {}, async function (_converse) {
|
||||||
|
|
||||||
|
await mock.waitForRoster(_converse, 'all');
|
||||||
|
await mock.openControlBox(_converse);
|
||||||
|
|
||||||
|
const cbview = _converse.chatboxviews.get('controlbox');
|
||||||
|
cbview.querySelector('.add-contact').click()
|
||||||
|
const modal = _converse.api.modal.get('converse-add-contact-modal');
|
||||||
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
expect(modal.querySelector('form.add-xmpp-contact')).not.toBe(null);
|
||||||
|
|
||||||
|
const input_jid = modal.querySelector('input[name="jid"]');
|
||||||
|
const input_name = modal.querySelector('input[name="name"]');
|
||||||
|
input_jid.value = 'someone@';
|
||||||
|
|
||||||
|
const evt = new Event('input');
|
||||||
|
input_jid.dispatchEvent(evt);
|
||||||
|
expect(modal.querySelector('.suggestion-box li').textContent).toBe('someone@montague.lit');
|
||||||
|
input_jid.value = 'someone@montague.lit';
|
||||||
|
input_name.value = 'Someone';
|
||||||
|
modal.querySelector('button[type="submit"]').click();
|
||||||
|
|
||||||
|
const sent_IQs = _converse.connection.IQ_stanzas;
|
||||||
|
const sent_stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`)).pop());
|
||||||
|
expect(Strophe.serialize(sent_stanza)).toEqual(
|
||||||
|
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
||||||
|
`<query xmlns="jabber:iq:roster"><item jid="someone@montague.lit" name="Someone"/></query>`+
|
||||||
|
`</iq>`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("can be configured to not provide search suggestions",
|
||||||
|
mock.initConverse([], {'autocomplete_add_contact': false}, async function (_converse) {
|
||||||
|
|
||||||
|
await mock.waitForRoster(_converse, 'all', 0);
|
||||||
|
await mock.openControlBox(_converse);
|
||||||
|
const cbview = _converse.chatboxviews.get('controlbox');
|
||||||
|
cbview.querySelector('.add-contact').click()
|
||||||
|
const modal = _converse.api.modal.get('converse-add-contact-modal');
|
||||||
|
expect(modal.jid_auto_complete).toBe(undefined);
|
||||||
|
expect(modal.name_auto_complete).toBe(undefined);
|
||||||
|
|
||||||
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
expect(modal.querySelector('form.add-xmpp-contact')).not.toBe(null);
|
||||||
|
const input_jid = modal.querySelector('input[name="jid"]');
|
||||||
|
input_jid.value = 'someone@montague.lit';
|
||||||
|
modal.querySelector('button[type="submit"]').click();
|
||||||
|
|
||||||
|
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||||
|
const sent_stanza = await u.waitUntil(
|
||||||
|
() => IQ_stanzas.filter(s => sizzle(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`, s).length).pop()
|
||||||
|
);
|
||||||
|
expect(Strophe.serialize(sent_stanza)).toEqual(
|
||||||
|
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
||||||
|
`<query xmlns="jabber:iq:roster"><item jid="someone@montague.lit"/></query>`+
|
||||||
|
`</iq>`
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("integrates with xhr_user_search_url to search for contacts",
|
||||||
|
mock.initConverse([], { 'xhr_user_search_url': 'http://example.org/?' },
|
||||||
|
async function (_converse) {
|
||||||
|
|
||||||
|
await mock.waitForRoster(_converse, 'all', 0);
|
||||||
|
|
||||||
|
class MockXHR extends XMLHttpRequest {
|
||||||
|
open () {} // eslint-disable-line
|
||||||
|
responseText = ''
|
||||||
|
send () {
|
||||||
|
this.responseText = JSON.stringify([
|
||||||
|
{"jid": "marty@mcfly.net", "fullname": "Marty McFly"},
|
||||||
|
{"jid": "doc@brown.com", "fullname": "Doc Brown"}
|
||||||
|
]);
|
||||||
|
this.onload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const XMLHttpRequestBackup = window.XMLHttpRequest;
|
||||||
|
window.XMLHttpRequest = MockXHR;
|
||||||
|
|
||||||
|
await mock.openControlBox(_converse);
|
||||||
|
const cbview = _converse.chatboxviews.get('controlbox');
|
||||||
|
cbview.querySelector('.add-contact').click()
|
||||||
|
const modal = _converse.api.modal.get('converse-add-contact-modal');
|
||||||
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
|
// We only have autocomplete for the name input
|
||||||
|
expect(modal.jid_auto_complete).toBe(undefined);
|
||||||
|
expect(modal.name_auto_complete instanceof _converse.AutoComplete).toBe(true);
|
||||||
|
|
||||||
|
const input_el = modal.querySelector('input[name="name"]');
|
||||||
|
input_el.value = 'marty';
|
||||||
|
input_el.dispatchEvent(new Event('input'));
|
||||||
|
await u.waitUntil(() => modal.querySelector('.suggestion-box li'), 1000);
|
||||||
|
expect(modal.querySelectorAll('.suggestion-box li').length).toBe(1);
|
||||||
|
const suggestion = modal.querySelector('.suggestion-box li');
|
||||||
|
expect(suggestion.textContent).toBe('Marty McFly');
|
||||||
|
|
||||||
|
// Mock selection
|
||||||
|
modal.name_auto_complete.select(suggestion);
|
||||||
|
|
||||||
|
expect(input_el.value).toBe('Marty McFly');
|
||||||
|
expect(modal.querySelector('input[name="jid"]').value).toBe('marty@mcfly.net');
|
||||||
|
modal.querySelector('button[type="submit"]').click();
|
||||||
|
|
||||||
|
const sent_IQs = _converse.connection.IQ_stanzas;
|
||||||
|
const sent_stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`)).pop());
|
||||||
|
expect(Strophe.serialize(sent_stanza)).toEqual(
|
||||||
|
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
||||||
|
`<query xmlns="jabber:iq:roster"><item jid="marty@mcfly.net" name="Marty McFly"/></query>`+
|
||||||
|
`</iq>`);
|
||||||
|
window.XMLHttpRequest = XMLHttpRequestBackup;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("can be configured to not provide search suggestions for XHR search results",
|
||||||
|
mock.initConverse([],
|
||||||
|
{ 'autocomplete_add_contact': false,
|
||||||
|
'xhr_user_search_url': 'http://example.org/?' },
|
||||||
|
async function (_converse) {
|
||||||
|
|
||||||
|
await mock.waitForRoster(_converse, 'all');
|
||||||
|
await mock.openControlBox(_converse);
|
||||||
|
|
||||||
|
class MockXHR extends XMLHttpRequest {
|
||||||
|
open () {} // eslint-disable-line
|
||||||
|
responseText = ''
|
||||||
|
send () {
|
||||||
|
const value = modal.querySelector('input[name="name"]').value;
|
||||||
|
if (value === 'existing') {
|
||||||
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
|
this.responseText = JSON.stringify([{"jid": contact_jid, "fullname": mock.cur_names[0]}]);
|
||||||
|
} else if (value === 'romeo') {
|
||||||
|
this.responseText = JSON.stringify([{"jid": "romeo@montague.lit", "fullname": "Romeo Montague"}]);
|
||||||
|
} else if (value === 'ambiguous') {
|
||||||
|
this.responseText = JSON.stringify([
|
||||||
|
{"jid": "marty@mcfly.net", "fullname": "Marty McFly"},
|
||||||
|
{"jid": "doc@brown.com", "fullname": "Doc Brown"}
|
||||||
|
]);
|
||||||
|
} else if (value === 'insufficient') {
|
||||||
|
this.responseText = JSON.stringify([]);
|
||||||
|
} else {
|
||||||
|
this.responseText = JSON.stringify([{"jid": "marty@mcfly.net", "fullname": "Marty McFly"}]);
|
||||||
|
}
|
||||||
|
this.onload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const XMLHttpRequestBackup = window.XMLHttpRequest;
|
||||||
|
window.XMLHttpRequest = MockXHR;
|
||||||
|
|
||||||
|
const cbview = _converse.chatboxviews.get('controlbox');
|
||||||
|
cbview.querySelector('.add-contact').click()
|
||||||
|
const modal = _converse.api.modal.get('converse-add-contact-modal');
|
||||||
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
|
|
||||||
|
expect(modal.jid_auto_complete).toBe(undefined);
|
||||||
|
expect(modal.name_auto_complete).toBe(undefined);
|
||||||
|
|
||||||
|
const input_el = modal.querySelector('input[name="name"]');
|
||||||
|
input_el.value = 'ambiguous';
|
||||||
|
modal.querySelector('button[type="submit"]').click();
|
||||||
|
let feedback_el = modal.querySelector('.invalid-feedback');
|
||||||
|
expect(feedback_el.textContent).toBe('Sorry, could not find a contact with that name');
|
||||||
|
feedback_el.textContent = '';
|
||||||
|
|
||||||
|
input_el.value = 'insufficient';
|
||||||
|
modal.querySelector('button[type="submit"]').click();
|
||||||
|
feedback_el = modal.querySelector('.invalid-feedback');
|
||||||
|
expect(feedback_el.textContent).toBe('Sorry, could not find a contact with that name');
|
||||||
|
feedback_el.textContent = '';
|
||||||
|
|
||||||
|
input_el.value = 'existing';
|
||||||
|
modal.querySelector('button[type="submit"]').click();
|
||||||
|
feedback_el = modal.querySelector('.invalid-feedback');
|
||||||
|
expect(feedback_el.textContent).toBe('This contact has already been added');
|
||||||
|
|
||||||
|
input_el.value = 'Marty McFly';
|
||||||
|
modal.querySelector('button[type="submit"]').click();
|
||||||
|
|
||||||
|
const sent_IQs = _converse.connection.IQ_stanzas;
|
||||||
|
const sent_stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[type="set"] query[xmlns="${Strophe.NS.ROSTER}"]`)).pop());
|
||||||
|
expect(Strophe.serialize(sent_stanza)).toEqual(
|
||||||
|
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
||||||
|
`<query xmlns="jabber:iq:roster"><item jid="marty@mcfly.net" name="Marty McFly"/></query>`+
|
||||||
|
`</iq>`);
|
||||||
|
window.XMLHttpRequest = XMLHttpRequestBackup;
|
||||||
|
}));
|
||||||
|
});
|
@ -17,11 +17,11 @@ describe("A sent presence stanza", function () {
|
|||||||
const cbview = _converse.chatboxviews.get('controlbox');
|
const cbview = _converse.chatboxviews.get('controlbox');
|
||||||
const change_status_el = await u.waitUntil(() => cbview.querySelector('.change-status'));
|
const change_status_el = await u.waitUntil(() => cbview.querySelector('.change-status'));
|
||||||
change_status_el.click()
|
change_status_el.click()
|
||||||
let modal = _converse.api.modal.get('modal-status-change');
|
let modal = _converse.api.modal.get('converse-chat-status-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
const msg = 'My custom status';
|
const msg = 'My custom status';
|
||||||
modal.el.querySelector('input[name="status_message"]').value = msg;
|
modal.querySelector('input[name="status_message"]').value = msg;
|
||||||
modal.el.querySelector('[type="submit"]').click();
|
modal.querySelector('[type="submit"]').click();
|
||||||
|
|
||||||
const sent_stanzas = _converse.connection.sent_stanzas;
|
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||||
let sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
|
let sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop());
|
||||||
@ -31,14 +31,15 @@ describe("A sent presence stanza", function () {
|
|||||||
`<priority>0</priority>`+
|
`<priority>0</priority>`+
|
||||||
`<c hash="sha-1" node="https://conversejs.org" ver="TfHz9vOOfqIG0Z9lW5CuPaWGnrQ=" xmlns="http://jabber.org/protocol/caps"/>`+
|
`<c hash="sha-1" node="https://conversejs.org" ver="TfHz9vOOfqIG0Z9lW5CuPaWGnrQ=" xmlns="http://jabber.org/protocol/caps"/>`+
|
||||||
`</presence>`)
|
`</presence>`)
|
||||||
await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
|
await u.waitUntil(() => modal.getAttribute('aria-hidden') === "true");
|
||||||
await u.waitUntil(() => !u.isVisible(modal.el));
|
await u.waitUntil(() => !u.isVisible(modal));
|
||||||
|
|
||||||
cbview.querySelector('.change-status').click()
|
cbview.querySelector('.change-status').click()
|
||||||
modal = _converse.api.modal.get('modal-status-change');
|
modal = _converse.api.modal.get('converse-chat-status-modal');
|
||||||
await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "false", 1000);
|
await u.waitUntil(() => modal.getAttribute('aria-hidden') === "false", 1000);
|
||||||
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
|
modal.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
|
||||||
modal.el.querySelector('[type="submit"]').click();
|
modal.querySelector('[type="submit"]').click();
|
||||||
|
|
||||||
await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).length === 2);
|
await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).length === 2);
|
||||||
sent_presence = sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop();
|
sent_presence = sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop();
|
||||||
expect(Strophe.serialize(sent_presence))
|
expect(Strophe.serialize(sent_presence))
|
||||||
|
@ -55,12 +55,12 @@ describe("The Protocol", function () {
|
|||||||
spyOn(_converse.api.vcard, "get").and.callThrough();
|
spyOn(_converse.api.vcard, "get").and.callThrough();
|
||||||
|
|
||||||
cbview.querySelector('.add-contact').click()
|
cbview.querySelector('.add-contact').click()
|
||||||
const modal = _converse.api.modal.get('add-contact-modal');
|
const modal = _converse.api.modal.get('converse-add-contact-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
modal.delegateEvents();
|
modal.delegateEvents();
|
||||||
|
|
||||||
// Fill in the form and submit
|
// Fill in the form and submit
|
||||||
const form = modal.el.querySelector('form.add-xmpp-contact');
|
const form = modal.querySelector('form.add-xmpp-contact');
|
||||||
form.querySelector('input[name="jid"]').value = 'contact@example.org';
|
form.querySelector('input[name="jid"]').value = 'contact@example.org';
|
||||||
form.querySelector('input[name="name"]').value = 'Chris Contact';
|
form.querySelector('input[name="name"]').value = 'Chris Contact';
|
||||||
form.querySelector('input[name="group"]').value = 'My Buddies';
|
form.querySelector('input[name="group"]').value = 'My Buddies';
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
|
import log from "@converse/headless/log";
|
||||||
|
import { __ } from 'i18n';
|
||||||
import { _converse, api } from "@converse/headless/core";
|
import { _converse, api } from "@converse/headless/core";
|
||||||
|
|
||||||
|
export function removeContact (contact) {
|
||||||
|
contact.removeFromRoster(
|
||||||
|
() => contact.destroy(),
|
||||||
|
(e) => {
|
||||||
|
e && log.error(e);
|
||||||
|
api.alert('error', __('Error'), [
|
||||||
|
__('Sorry, there was an error while trying to remove %1$s as a contact.',
|
||||||
|
contact.getDisplayName())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function highlightRosterItem (chatbox) {
|
export function highlightRosterItem (chatbox) {
|
||||||
_converse.roster?.get(chatbox.get('jid'))?.trigger('highlight');
|
_converse.roster?.get(chatbox.get('jid'))?.trigger('highlight');
|
||||||
|
@ -55,6 +55,7 @@ export default class AutoCompleteComponent extends CustomElement {
|
|||||||
'name': { type: String },
|
'name': { type: String },
|
||||||
'placeholder': { type: String },
|
'placeholder': { type: String },
|
||||||
'triggers': { type: String },
|
'triggers': { type: String },
|
||||||
|
'required': { type: Boolean },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +79,7 @@ export default class AutoCompleteComponent extends CustomElement {
|
|||||||
<ul class="suggestion-box__results ${position_class}" hidden=""></ul>
|
<ul class="suggestion-box__results ${position_class}" hidden=""></ul>
|
||||||
<input
|
<input
|
||||||
?autofocus=${this.autofocus}
|
?autofocus=${this.autofocus}
|
||||||
|
?required=${this.required}
|
||||||
type="text"
|
type="text"
|
||||||
name="${this.name}"
|
name="${this.name}"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'shared/registry.js';
|
import 'shared/registry.js';
|
||||||
import ImageModal from 'modals/image.js';
|
import ImageModal from 'shared/modals/image.js';
|
||||||
import renderRichText from 'shared/directives/rich-text.js';
|
import renderRichText from 'shared/directives/rich-text.js';
|
||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { api } from "@converse/headless/core";
|
import { api } from "@converse/headless/core";
|
||||||
@ -31,7 +31,7 @@ export default class MessageBody extends CustomElement {
|
|||||||
|
|
||||||
onImgClick (ev) { // eslint-disable-line class-methods-use-this
|
onImgClick (ev) { // eslint-disable-line class-methods-use-this
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
api.modal.create(ImageModal, {'src': ev.target.src}, ev).show(ev);
|
api.modal.show('converse-image-modal', {'src': ev.target.src}, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
onImgLoad () {
|
onImgLoad () {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import './message-actions.js';
|
import './message-actions.js';
|
||||||
import './message-body.js';
|
import './message-body.js';
|
||||||
import 'shared/components/dropdown.js';
|
import 'shared/components/dropdown.js';
|
||||||
|
import 'shared/modals/message-versions.js';
|
||||||
|
import 'shared/modals/user-details.js';
|
||||||
import 'shared/registry';
|
import 'shared/registry';
|
||||||
|
import 'plugins/muc-views/modals/occupant.js';
|
||||||
import tpl_file_progress from './templates/file-progress.js';
|
import tpl_file_progress from './templates/file-progress.js';
|
||||||
import MessageVersionsModal from 'modals/message-versions.js';
|
|
||||||
import OccupantModal from 'plugins/muc-views/modals/occupant.js';
|
|
||||||
import UserDetailsModal from 'modals/user-details.js';
|
|
||||||
import log from '@converse/headless/log';
|
import log from '@converse/headless/log';
|
||||||
import tpl_info_message from './templates/info-message.js';
|
import tpl_info_message from './templates/info-message.js';
|
||||||
import tpl_mep_message from 'plugins/muc-views/templates/mep-message.js';
|
import tpl_mep_message from 'plugins/muc-views/templates/mep-message.js';
|
||||||
@ -214,20 +214,20 @@ export default class Message extends CustomElement {
|
|||||||
|
|
||||||
showUserModal (ev) {
|
showUserModal (ev) {
|
||||||
if (this.model.get('sender') === 'me') {
|
if (this.model.get('sender') === 'me') {
|
||||||
api.modal.show(_converse.ProfileModal, {model: this.model}, ev);
|
api.modal.show('converse-profile-modal', {model: this.model}, ev);
|
||||||
} else if (this.model.get('type') === 'groupchat') {
|
} else if (this.model.get('type') === 'groupchat') {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
api.modal.show(OccupantModal, { 'model': this.model.occupant, 'message': this.model }, ev);
|
api.modal.show('converse-muc-occupant-modal', { 'model': this.model.occupant, 'message': this.model }, ev);
|
||||||
} else {
|
} else {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const chatbox = this.model.collection.chatbox;
|
const chatbox = this.model.collection.chatbox;
|
||||||
api.modal.show(UserDetailsModal, { model: chatbox }, ev);
|
api.modal.show('converse-user-details-modal', { model: chatbox }, ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showMessageVersionsModal (ev) {
|
showMessageVersionsModal (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
api.modal.show(MessageVersionsModal, {'model': this.model}, ev);
|
api.modal.show('converse-message-versions-modal', {'model': this.model}, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSpoilerMessage (ev) {
|
toggleSpoilerMessage (ev) {
|
||||||
|
22
src/shared/modals/image.js
Normal file
22
src/shared/modals/image.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
|
import tpl_image_modal from "./templates/image.js";
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import { api } from "@converse/headless/core";
|
||||||
|
import { getFileName } from 'utils/html.js';
|
||||||
|
import { html } from "lit";
|
||||||
|
|
||||||
|
import './styles/image.scss';
|
||||||
|
|
||||||
|
|
||||||
|
export default class ImageModal extends BaseModal {
|
||||||
|
|
||||||
|
renderModal () {
|
||||||
|
return tpl_image_modal({ 'src': this.src });
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () {
|
||||||
|
return html`${__('Image: ')}<a target="_blank" rel="noopener" href="${this.src}">${getFileName(this.src)}</a>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-image-modal', ImageModal);
|
19
src/shared/modals/message-versions.js
Normal file
19
src/shared/modals/message-versions.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'shared/components/message-versions.js';
|
||||||
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import { html } from "lit";
|
||||||
|
import {api } from "@converse/headless/core";
|
||||||
|
|
||||||
|
|
||||||
|
export default class MessageVersionsModal extends BaseModal {
|
||||||
|
|
||||||
|
renderModal () {
|
||||||
|
return html`<converse-message-versions .model=${this.model}></converse-message-versions>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle () { // eslint-disable-line class-methods-use-this
|
||||||
|
return __('Message versions');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.elements.define('converse-message-versions-modal', MessageVersionsModal);
|
6
src/shared/modals/styles/image.scss
Normal file
6
src/shared/modals/styles/image.scss
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
converse-image-modal {
|
||||||
|
.chat-image--modal {
|
||||||
|
max-height: 99%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
3
src/shared/modals/templates/image.js
Normal file
3
src/shared/modals/templates/image.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { html } from "lit";
|
||||||
|
|
||||||
|
export default (o) => html`<img class="chat-image chat-image--modal" src="${o.src}">`;
|
69
src/shared/modals/templates/user-details.js
Normal file
69
src/shared/modals/templates/user-details.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import avatar from 'shared/avatar/templates/avatar.js';
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import { html } from 'lit';
|
||||||
|
import { api } from "@converse/headless/core";
|
||||||
|
|
||||||
|
const remove_button = (el) => {
|
||||||
|
const i18n_remove_contact = __('Remove as contact');
|
||||||
|
return html`
|
||||||
|
<button type="button" @click="${ev => el.removeContact(ev)}" class="btn btn-danger remove-contact">
|
||||||
|
<converse-icon
|
||||||
|
class="fas fa-trash-alt"
|
||||||
|
color="var(--text-color-lighten-15-percent)"
|
||||||
|
size="1em"
|
||||||
|
></converse-icon>
|
||||||
|
${i18n_remove_contact}
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tpl_footer = (el) => {
|
||||||
|
const is_roster_contact = el.model.contact !== undefined;
|
||||||
|
const i18n_refresh = __('Refresh');
|
||||||
|
const allow_contact_removal = api.settings.get('allow_contact_removal');
|
||||||
|
return html`
|
||||||
|
<button type="button" class="btn btn-info refresh-contact" @click=${ev => el.refreshContact(ev)}>
|
||||||
|
<converse-icon
|
||||||
|
class="fa fa-refresh"
|
||||||
|
color="var(--text-color-lighten-15-percent)"
|
||||||
|
size="1em"
|
||||||
|
></converse-icon>
|
||||||
|
${i18n_refresh}</button>
|
||||||
|
${ (allow_contact_removal && is_roster_contact) ? remove_button(el) : '' }
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const tpl_user_details_modal = (el) => {
|
||||||
|
const vcard = el.model?.vcard;
|
||||||
|
const vcard_json = vcard ? vcard.toJSON() : {};
|
||||||
|
const o = { ...el.model.toJSON(), ...vcard_json };
|
||||||
|
|
||||||
|
const i18n_address = __('XMPP Address');
|
||||||
|
const i18n_email = __('Email');
|
||||||
|
const i18n_full_name = __('Full Name');
|
||||||
|
const i18n_nickname = __('Nickname');
|
||||||
|
const i18n_profile = __('The User\'s Profile Image');
|
||||||
|
const i18n_role = __('Role');
|
||||||
|
const i18n_url = __('URL');
|
||||||
|
const avatar_data = {
|
||||||
|
'alt_text': i18n_profile,
|
||||||
|
'extra_classes': 'mb-3',
|
||||||
|
'height': '120',
|
||||||
|
'width': '120'
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="modal-body">
|
||||||
|
${ o.image ? html`<div class="mb-4">${avatar(Object.assign(o, avatar_data))}</div>` : '' }
|
||||||
|
${ o.fullname ? html`<p><label>${i18n_full_name}:</label> ${o.fullname}</p>` : '' }
|
||||||
|
<p><label>${i18n_address}:</label> <a href="xmpp:${o.jid}">${o.jid}</a></p>
|
||||||
|
${ o.nickname ? html`<p><label>${i18n_nickname}:</label> ${o.nickname}</p>` : '' }
|
||||||
|
${ o.url ? html`<p><label>${i18n_url}:</label> <a target="_blank" rel="noopener" href="${o.url}">${o.url}</a></p>` : '' }
|
||||||
|
${ o.email ? html`<p><label>${i18n_email}:</label> <a href="mailto:${o.email}">${o.email}</a></p>` : '' }
|
||||||
|
${ o.role ? html`<p><label>${i18n_role}:</label> ${o.role}</p>` : '' }
|
||||||
|
|
||||||
|
<converse-omemo-fingerprints jid=${o.jid}></converse-omemo-fingerprints>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
@ -17,19 +17,19 @@ describe("The User Details Modal", function () {
|
|||||||
const view = _converse.chatboxviews.get(contact_jid);
|
const view = _converse.chatboxviews.get(contact_jid);
|
||||||
let show_modal_button = view.querySelector('.show-user-details-modal');
|
let show_modal_button = view.querySelector('.show-user-details-modal');
|
||||||
show_modal_button.click();
|
show_modal_button.click();
|
||||||
const modal = _converse.api.modal.get('user-details-modal');
|
const modal = _converse.api.modal.get('converse-user-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal), 1000);
|
||||||
spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true));
|
spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true));
|
||||||
|
|
||||||
spyOn(view.model.contact, 'removeFromRoster').and.callFake(callback => callback());
|
spyOn(view.model.contact, 'removeFromRoster').and.callFake(callback => callback());
|
||||||
let remove_contact_button = modal.el.querySelector('button.remove-contact');
|
let remove_contact_button = modal.querySelector('button.remove-contact');
|
||||||
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
||||||
remove_contact_button.click();
|
remove_contact_button.click();
|
||||||
await u.waitUntil(() => modal.el.getAttribute('aria-hidden'), 1000);
|
await u.waitUntil(() => modal.getAttribute('aria-hidden'), 1000);
|
||||||
await u.waitUntil(() => !u.isVisible(modal.el));
|
await u.waitUntil(() => !u.isVisible(modal));
|
||||||
show_modal_button = view.querySelector('.show-user-details-modal');
|
show_modal_button = view.querySelector('.show-user-details-modal');
|
||||||
show_modal_button.click();
|
show_modal_button.click();
|
||||||
remove_contact_button = modal.el.querySelector('button.remove-contact');
|
remove_contact_button = modal.querySelector('button.remove-contact');
|
||||||
expect(remove_contact_button === null).toBeTruthy();
|
expect(remove_contact_button === null).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -44,15 +44,15 @@ describe("The User Details Modal", function () {
|
|||||||
const view = _converse.chatboxviews.get(contact_jid);
|
const view = _converse.chatboxviews.get(contact_jid);
|
||||||
let show_modal_button = view.querySelector('.show-user-details-modal');
|
let show_modal_button = view.querySelector('.show-user-details-modal');
|
||||||
show_modal_button.click();
|
show_modal_button.click();
|
||||||
let modal = _converse.api.modal.get('user-details-modal');
|
let modal = _converse.api.modal.get('converse-user-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 2000);
|
await u.waitUntil(() => u.isVisible(modal), 2000);
|
||||||
spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true));
|
spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true));
|
||||||
|
|
||||||
spyOn(view.model.contact, 'removeFromRoster').and.callFake((callback, errback) => errback());
|
spyOn(view.model.contact, 'removeFromRoster').and.callFake((callback, errback) => errback());
|
||||||
let remove_contact_button = modal.el.querySelector('button.remove-contact');
|
let remove_contact_button = modal.querySelector('button.remove-contact');
|
||||||
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
||||||
remove_contact_button.click();
|
remove_contact_button.click();
|
||||||
await u.waitUntil(() => !u.isVisible(modal.el))
|
await u.waitUntil(() => !u.isVisible(modal))
|
||||||
await u.waitUntil(() => u.isVisible(document.querySelector('.alert-danger')), 2000);
|
await u.waitUntil(() => u.isVisible(document.querySelector('.alert-danger')), 2000);
|
||||||
|
|
||||||
const header = document.querySelector('.alert-danger .modal-title');
|
const header = document.querySelector('.alert-danger .modal-title');
|
||||||
@ -62,14 +62,14 @@ describe("The User Details Modal", function () {
|
|||||||
document.querySelector('.alert-danger button.close').click();
|
document.querySelector('.alert-danger button.close').click();
|
||||||
show_modal_button = view.querySelector('.show-user-details-modal');
|
show_modal_button = view.querySelector('.show-user-details-modal');
|
||||||
show_modal_button.click();
|
show_modal_button.click();
|
||||||
modal = _converse.api.modal.get('user-details-modal');
|
modal = _converse.api.modal.get('converse-user-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 2000)
|
await u.waitUntil(() => u.isVisible(modal), 2000)
|
||||||
|
|
||||||
show_modal_button = view.querySelector('.show-user-details-modal');
|
show_modal_button = view.querySelector('.show-user-details-modal');
|
||||||
show_modal_button.click();
|
show_modal_button.click();
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 2000)
|
await u.waitUntil(() => u.isVisible(modal), 2000)
|
||||||
|
|
||||||
remove_contact_button = modal.el.querySelector('button.remove-contact');
|
remove_contact_button = modal.querySelector('button.remove-contact');
|
||||||
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
@ -1,36 +1,17 @@
|
|||||||
import BootstrapModal from "plugins/modal/base.js";
|
import BaseModal from "plugins/modal/modal.js";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import tpl_user_details_modal from "./templates/user-details.js";
|
import { tpl_user_details_modal, tpl_footer } from "./templates/user-details.js";
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { api, converse } from "@converse/headless/core";
|
||||||
|
import { removeContact } from 'plugins/rosterview/utils.js';
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
function removeContact (contact) {
|
export default class UserDetailsModal extends BaseModal {
|
||||||
contact.removeFromRoster(
|
|
||||||
() => contact.destroy(),
|
|
||||||
(e) => {
|
|
||||||
e && log.error(e);
|
|
||||||
api.alert('error', __('Error'), [
|
|
||||||
__('Sorry, there was an error while trying to remove %1$s as a contact.',
|
|
||||||
contact.getDisplayName())
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const UserDetailsModal = BootstrapModal.extend({
|
|
||||||
id: 'user-details-modal',
|
|
||||||
persistent: true,
|
|
||||||
|
|
||||||
events: {
|
|
||||||
'click button.refresh-contact': 'refreshContact',
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
super.initialize();
|
||||||
this.model.rosterContactAdded.then(() => this.registerContactEventHandlers());
|
this.model.rosterContactAdded.then(() => this.registerContactEventHandlers());
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(this.model, 'change', this.render);
|
||||||
this.registerContactEventHandlers();
|
this.registerContactEventHandlers();
|
||||||
@ -41,23 +22,19 @@ const UserDetailsModal = BootstrapModal.extend({
|
|||||||
* @example _converse.api.listen.on('userDetailsModalInitialized', (chatbox) => { ... });
|
* @example _converse.api.listen.on('userDetailsModalInitialized', (chatbox) => { ... });
|
||||||
*/
|
*/
|
||||||
api.trigger('userDetailsModalInitialized', this.model);
|
api.trigger('userDetailsModalInitialized', this.model);
|
||||||
},
|
}
|
||||||
|
|
||||||
toHTML () {
|
renderModal () {
|
||||||
const vcard = this.model?.vcard;
|
return tpl_user_details_modal(this);
|
||||||
const vcard_json = vcard ? vcard.toJSON() : {};
|
}
|
||||||
return tpl_user_details_modal(Object.assign(
|
|
||||||
this.model.toJSON(),
|
renderModalFooter () {
|
||||||
vcard_json, {
|
return tpl_footer(this);
|
||||||
'_converse': _converse,
|
}
|
||||||
'allow_contact_removal': api.settings.get('allow_contact_removal'),
|
|
||||||
'display_name': this.model.getDisplayName(),
|
getModalTitle () {
|
||||||
'is_roster_contact': this.model.contact !== undefined,
|
return this.model.getDisplayName();
|
||||||
'removeContact': ev => this.removeContact(ev),
|
}
|
||||||
'view': this,
|
|
||||||
'utils': u
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
registerContactEventHandlers () {
|
registerContactEventHandlers () {
|
||||||
if (this.model.contact !== undefined) {
|
if (this.model.contact !== undefined) {
|
||||||
@ -68,7 +45,7 @@ const UserDetailsModal = BootstrapModal.extend({
|
|||||||
this.render();
|
this.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async refreshContact (ev) {
|
async refreshContact (ev) {
|
||||||
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
||||||
@ -81,7 +58,7 @@ const UserDetailsModal = BootstrapModal.extend({
|
|||||||
this.alert(__('Sorry, something went wrong while trying to refresh'), 'danger');
|
this.alert(__('Sorry, something went wrong while trying to refresh'), 'danger');
|
||||||
}
|
}
|
||||||
u.removeClass('fa-spin', refresh_icon);
|
u.removeClass('fa-spin', refresh_icon);
|
||||||
},
|
}
|
||||||
|
|
||||||
async removeContact (ev) {
|
async removeContact (ev) {
|
||||||
ev?.preventDefault?.();
|
ev?.preventDefault?.();
|
||||||
@ -94,9 +71,7 @@ const UserDetailsModal = BootstrapModal.extend({
|
|||||||
setTimeout(() => removeContact(this.model.contact), 1);
|
setTimeout(() => removeContact(this.model.contact), 1);
|
||||||
this.modal.hide();
|
this.modal.hide();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
_converse.UserDetailsModal = UserDetailsModal;
|
api.elements.define('converse-user-details-modal', UserDetailsModal);
|
||||||
|
|
||||||
export default UserDetailsModal;
|
|
@ -140,13 +140,13 @@ async function openChatRoomViaModal (_converse, jid, nick='') {
|
|||||||
await openControlBox(_converse);
|
await openControlBox(_converse);
|
||||||
document.querySelector('converse-rooms-list .show-add-muc-modal').click();
|
document.querySelector('converse-rooms-list .show-add-muc-modal').click();
|
||||||
closeControlBox(_converse);
|
closeControlBox(_converse);
|
||||||
const modal = _converse.api.modal.get('add-chatroom-modal');
|
const modal = _converse.api.modal.get('converse-add-muc-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1500)
|
await u.waitUntil(() => u.isVisible(modal), 1500)
|
||||||
modal.el.querySelector('input[name="chatroom"]').value = jid;
|
modal.querySelector('input[name="chatroom"]').value = jid;
|
||||||
if (nick) {
|
if (nick) {
|
||||||
modal.el.querySelector('input[name="nickname"]').value = nick;
|
modal.querySelector('input[name="nickname"]').value = nick;
|
||||||
}
|
}
|
||||||
modal.el.querySelector('form input[type="submit"]').click();
|
modal.querySelector('form input[type="submit"]').click();
|
||||||
await u.waitUntil(() => _converse.chatboxviews.get(jid), 1000);
|
await u.waitUntil(() => _converse.chatboxviews.get(jid), 1000);
|
||||||
return _converse.chatboxviews.get(jid);
|
return _converse.chatboxviews.get(jid);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,8 @@ function slideOutWrapup (el) {
|
|||||||
el.style.height = '';
|
el.style.height = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileName (uri) {
|
export function getFileName (url) {
|
||||||
|
const uri = getURI(url);
|
||||||
try {
|
try {
|
||||||
return decodeURI(uri.filename());
|
return decodeURI(uri.filename());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<script src="3rdparty/libsignal-protocol.js"></script>
|
<script src="3rdparty/libsignal-protocol.js"></script>
|
||||||
<link rel="manifest" href="./manifest.json">
|
<link rel="manifest" href="./manifest.json">
|
||||||
<link rel="shortcut icon" type="image/ico" href="favicon.ico"/>
|
<link rel="shortcut icon" type="image/ico" href="favicon.ico"/>
|
||||||
|
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="reset"></body>
|
<body class="reset"></body>
|
||||||
<script>
|
<script>
|
||||||
|
Loading…
Reference in New Issue
Block a user