Move modals and their templates into ./modals/
This commit is contained in:
parent
34cba68432
commit
b18cc6bcc5
17
package-lock.json
generated
17
package-lock.json
generated
@ -3144,7 +3144,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"filesize": {
|
"filesize": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": false
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg=="
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
@ -3200,20 +3201,22 @@
|
|||||||
},
|
},
|
||||||
"localforage": {
|
"localforage": {
|
||||||
"version": "1.7.3",
|
"version": "1.7.3",
|
||||||
"resolved": false,
|
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz",
|
||||||
|
"integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lie": "3.1.1"
|
"lie": "3.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pluggable.js": {
|
"pluggable.js": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": false,
|
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-SBt6v6Tbp20Jf8hU0cpcc/+HBHGMY8/Q+yA6Ih0tBQE8tfdZ6U4PRG0iNvUUjLx/hVyOP53n0UfGBymlfaaXCg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.11"
|
"lodash": "^4.17.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"skeletor.js": {
|
"skeletor.js": {
|
||||||
"version": "0.0.1",
|
"version": "github:skeletorjs/skeletor#bf6d9c86f9fcf224fa9d9af5a25380b77aa4b561",
|
||||||
"from": "github:skeletorjs/skeletor#bf6d9c86f9fcf224fa9d9af5a25380b77aa4b561",
|
"from": "github:skeletorjs/skeletor#bf6d9c86f9fcf224fa9d9af5a25380b77aa4b561",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
@ -3221,7 +3224,11 @@
|
|||||||
},
|
},
|
||||||
"strophe.js": {
|
"strophe.js": {
|
||||||
"version": "github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f",
|
"version": "github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f",
|
||||||
"from": "strophe.js@github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f"
|
"from": "strophe.js@github:strophe/strophejs#c4a94e59877c06dc2395f4ccbd26f3fee67a4c9f",
|
||||||
|
"requires": {
|
||||||
|
"abab": "^2.0.3",
|
||||||
|
"xmldom": "^0.1.27"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"twemoji": {
|
"twemoji": {
|
||||||
"version": "12.1.5",
|
"version": "12.1.5",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*global mock */
|
/*global mock, converse */
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
@ -53,6 +53,7 @@ describe("The User Details Modal", function () {
|
|||||||
let remove_contact_button = modal.el.querySelector('button.remove-contact');
|
let remove_contact_button = modal.el.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(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');
|
||||||
|
@ -3,277 +3,133 @@
|
|||||||
* @copyright The Converse.js contributors
|
* @copyright The Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import { View } from '@converse/skeletor/src/view.js';
|
import Alert from './modals/alert.js';
|
||||||
|
import BootstrapModal from './modals/base.js';
|
||||||
|
import Confirm from './modals/confirm.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { render } from 'lit-html';
|
import { _converse, converse } from "@converse/headless/converse-core";
|
||||||
import { __ } from './i18n';
|
|
||||||
import bootstrap from "bootstrap.native";
|
|
||||||
import { converse } from "@converse/headless/converse-core";
|
|
||||||
import log from "@converse/headless/log";
|
|
||||||
import tpl_alert_component from "templates/alert.js";
|
|
||||||
import tpl_alert_modal from "templates/alert_modal.js";
|
|
||||||
import tpl_prompt from "templates/prompt.js";
|
|
||||||
|
|
||||||
const { sizzle } = converse.env;
|
|
||||||
const u = converse.env.utils;
|
|
||||||
|
|
||||||
let _converse;
|
|
||||||
|
|
||||||
|
|
||||||
export const BootstrapModal = View.extend({
|
|
||||||
className: "modal",
|
|
||||||
events: {
|
|
||||||
'click .nav-item .nav-link': 'switchTab'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
this.render()
|
|
||||||
|
|
||||||
this.el.setAttribute('tabindex', '-1');
|
|
||||||
this.el.setAttribute('role', 'dialog');
|
|
||||||
this.el.setAttribute('aria-hidden', 'true');
|
|
||||||
const label_id = this.el.querySelector('.modal-title').getAttribute('id');
|
|
||||||
label_id && this.el.setAttribute('aria-labelledby', label_id);
|
|
||||||
|
|
||||||
this.insertIntoDOM();
|
|
||||||
const Modal = bootstrap.Modal;
|
|
||||||
this.modal = new Modal(this.el, {
|
|
||||||
backdrop: true,
|
|
||||||
keyboard: true
|
|
||||||
});
|
|
||||||
this.el.addEventListener('hide.bs.modal', () => u.removeClass('selected', this.trigger_el), false);
|
|
||||||
},
|
|
||||||
|
|
||||||
insertIntoDOM () {
|
|
||||||
const container_el = _converse.chatboxviews.el.querySelector("#converse-modals");
|
|
||||||
container_el.insertAdjacentElement('beforeEnd', this.el);
|
|
||||||
},
|
|
||||||
|
|
||||||
switchTab (ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
sizzle('.nav-link.active', this.el).forEach(el => {
|
|
||||||
u.removeClass('active', this.el.querySelector(el.getAttribute('href')));
|
|
||||||
u.removeClass('active', el);
|
|
||||||
});
|
|
||||||
u.addClass('active', ev.target);
|
|
||||||
u.addClass('active', this.el.querySelector(ev.target.getAttribute('href')))
|
|
||||||
},
|
|
||||||
|
|
||||||
alert (message, type='primary') {
|
|
||||||
const body = this.el.querySelector('.modal-alert');
|
|
||||||
if (body === null) {
|
|
||||||
log.error("Could not find a .modal-alert element in the modal to show an alert message in!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// FIXME: Instead of adding the alert imperatively, we should
|
|
||||||
// find a way to let the modal rerender with an alert message
|
|
||||||
render(tpl_alert_component({'type': `alert-${type}`, 'message': message}), body);
|
|
||||||
const el = body.firstElementChild;
|
|
||||||
setTimeout(() => {
|
|
||||||
u.addClass('fade-out', el);
|
|
||||||
setTimeout(() => u.removeElement(el), 600);
|
|
||||||
}, 5000);
|
|
||||||
},
|
|
||||||
|
|
||||||
show (ev) {
|
|
||||||
if (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
this.trigger_el = ev.target;
|
|
||||||
this.trigger_el.classList.add('selected');
|
|
||||||
}
|
|
||||||
this.modal.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
converse.env.BootstrapModal = BootstrapModal; // expose to plugins
|
converse.env.BootstrapModal = BootstrapModal; // expose to plugins
|
||||||
|
|
||||||
export const Confirm = BootstrapModal.extend({
|
|
||||||
events: {
|
|
||||||
'submit .confirm': 'onConfimation'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
let alert;
|
||||||
this.confirmation = u.getResolveablePromise();
|
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
|
||||||
this.listenTo(this.model, 'change', this.render)
|
|
||||||
this.el.addEventListener('closed.bs.modal', () => this.confirmation.reject(), false);
|
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
const modal_api = {
|
||||||
return tpl_prompt(this.model.toJSON());
|
/**
|
||||||
},
|
* Show a confirm modal to the user.
|
||||||
|
* @method _converse.api.confirm
|
||||||
afterRender () {
|
* @param { String } title - The header text for the confirmation dialog
|
||||||
if (!this.close_handler_registered) {
|
* @param { (String[]|String) } messages - The text to show to the user
|
||||||
this.el.addEventListener('closed.bs.modal', () => {
|
* @param { Array<Field> } fields - An object representing a fields presented to the user.
|
||||||
if (!this.confirmation.isResolved) {
|
* @property { String } Field.label - The form label for the input field.
|
||||||
this.confirmation.reject()
|
* @property { String } Field.name - The name for the input field.
|
||||||
}
|
* @property { String } [Field.challenge] - A challenge value that must be provided by the user.
|
||||||
}, false);
|
* @property { String } [Field.placeholder] - The placeholder for the input field.
|
||||||
this.close_handler_registered = true;
|
* @property { Boolean} [Field.required] - Whether the field is required or not
|
||||||
|
* @returns { Promise<Array|false> } A promise which resolves with an array of
|
||||||
|
* filled in fields or `false` if the confirm dialog was closed or canceled.
|
||||||
|
*/
|
||||||
|
async confirm (title, messages=[], fields=[]) {
|
||||||
|
if (typeof messages === 'string') {
|
||||||
|
messages = [messages];
|
||||||
}
|
}
|
||||||
|
const model = new Model({title, messages, fields, 'type': 'confirm'})
|
||||||
|
const confirm = new Confirm({model});
|
||||||
|
confirm.show();
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await confirm.confirmation;
|
||||||
|
} catch (e) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
confirm.remove();
|
||||||
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
onConfimation (ev) {
|
/**
|
||||||
ev.preventDefault();
|
* Show a prompt modal to the user.
|
||||||
const form_data = new FormData(ev.target);
|
* @method _converse.api.prompt
|
||||||
const fields = (this.model.get('fields') || [])
|
* @param { String } title - The header text for the prompt
|
||||||
.map(field => {
|
* @param { (String[]|String) } messages - The prompt text to show to the user
|
||||||
const value = form_data.get(field.name).trim();
|
* @param { String } placeholder - The placeholder text for the prompt input
|
||||||
field.value = value;
|
* @returns { Promise<String|false> } A promise which resolves with the text provided by the
|
||||||
if (field.challenge) {
|
* user or `false` if the user canceled the prompt.
|
||||||
field.challenge_failed = (value !== field.challenge);
|
*/
|
||||||
}
|
async prompt (title, messages=[], placeholder='') {
|
||||||
return field;
|
if (typeof messages === 'string') {
|
||||||
|
messages = [messages];
|
||||||
|
}
|
||||||
|
const model = new Model({
|
||||||
|
title,
|
||||||
|
messages,
|
||||||
|
'fields': [{
|
||||||
|
'name': 'reason',
|
||||||
|
'placeholder': placeholder,
|
||||||
|
}],
|
||||||
|
'type': 'prompt'
|
||||||
|
})
|
||||||
|
const prompt = new Confirm({model});
|
||||||
|
prompt.show();
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = (await prompt.confirmation).pop()?.value;
|
||||||
|
} catch (e) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
prompt.remove();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show an alert modal to the user.
|
||||||
|
* @method _converse.api.alert
|
||||||
|
* @param { ('info'|'warn'|'error') } type - The type of alert.
|
||||||
|
* @param { String } title - The header text for the alert.
|
||||||
|
* @param { (String[]|String) } messages - The alert text to show to the user.
|
||||||
|
*/
|
||||||
|
alert (type, title, messages) {
|
||||||
|
if (typeof messages === 'string') {
|
||||||
|
messages = [messages];
|
||||||
|
}
|
||||||
|
let level;
|
||||||
|
if (type === 'error') {
|
||||||
|
level = 'alert-danger';
|
||||||
|
} else if (type === 'info') {
|
||||||
|
level = 'alert-info';
|
||||||
|
} else if (type === 'warn') {
|
||||||
|
level = 'alert-warning';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alert === undefined) {
|
||||||
|
const model = new Model({
|
||||||
|
'title': title,
|
||||||
|
'messages': messages,
|
||||||
|
'level': level,
|
||||||
|
'type': 'alert'
|
||||||
|
})
|
||||||
|
alert = new Alert({model});
|
||||||
|
} else {
|
||||||
|
alert.model.set({
|
||||||
|
'title': title,
|
||||||
|
'messages': messages,
|
||||||
|
'level': level
|
||||||
});
|
});
|
||||||
|
|
||||||
if (fields.filter(c => c.challenge_failed).length) {
|
|
||||||
this.model.set('fields', fields);
|
|
||||||
// Setting an array doesn't trigger a change event
|
|
||||||
this.model.trigger('change');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this.confirmation.resolve(fields);
|
alert.show();
|
||||||
this.modal.hide();
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
||||||
export const Alert = BootstrapModal.extend({
|
|
||||||
initialize () {
|
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
|
||||||
this.listenTo(this.model, 'change', this.render)
|
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
return tpl_alert_modal(Object.assign({__}, this.model.toJSON()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-modal', {
|
converse.plugins.add('converse-modal', {
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
_converse = this._converse
|
|
||||||
|
|
||||||
/************************ BEGIN Event Listeners ************************/
|
|
||||||
_converse.api.listen.on('disconnect', () => {
|
_converse.api.listen.on('disconnect', () => {
|
||||||
const container = document.querySelector("#converse-modals");
|
const container = document.querySelector("#converse-modals");
|
||||||
if (container) {
|
if (container) {
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Object.assign(_converse.api, modal_api);
|
||||||
|
|
||||||
/************************ BEGIN API ************************/
|
|
||||||
// We extend the default converse.js API to add methods specific to MUC chat rooms.
|
|
||||||
let alert;
|
|
||||||
|
|
||||||
Object.assign(_converse.api, {
|
|
||||||
/**
|
|
||||||
* Show a confirm modal to the user.
|
|
||||||
* @method _converse.api.confirm
|
|
||||||
* @param { String } title - The header text for the confirmation dialog
|
|
||||||
* @param { (String[]|String) } messages - The text to show to the user
|
|
||||||
* @param { Array<Field> } fields - An object representing a fields presented to the user.
|
|
||||||
* @property { String } Field.label - The form label for the input field.
|
|
||||||
* @property { String } Field.name - The name for the input field.
|
|
||||||
* @property { String } [Field.challenge] - A challenge value that must be provided by the user.
|
|
||||||
* @property { String } [Field.placeholder] - The placeholder for the input field.
|
|
||||||
* @property { Boolean} [Field.required] - Whether the field is required or not
|
|
||||||
* @returns { Promise<Array|false> } A promise which resolves with an array of
|
|
||||||
* filled in fields or `false` if the confirm dialog was closed or canceled.
|
|
||||||
*/
|
|
||||||
async confirm (title, messages=[], fields=[]) {
|
|
||||||
if (typeof messages === 'string') {
|
|
||||||
messages = [messages];
|
|
||||||
}
|
|
||||||
const model = new Model({title, messages, fields, 'type': 'confirm'})
|
|
||||||
const confirm = new Confirm({model});
|
|
||||||
confirm.show();
|
|
||||||
let result;
|
|
||||||
try {
|
|
||||||
result = await confirm.confirmation;
|
|
||||||
} catch (e) {
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
confirm.remove();
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a prompt modal to the user.
|
|
||||||
* @method _converse.api.prompt
|
|
||||||
* @param { String } title - The header text for the prompt
|
|
||||||
* @param { (String[]|String) } messages - The prompt text to show to the user
|
|
||||||
* @param { String } placeholder - The placeholder text for the prompt input
|
|
||||||
* @returns { Promise<String|false> } A promise which resolves with the text provided by the
|
|
||||||
* user or `false` if the user canceled the prompt.
|
|
||||||
*/
|
|
||||||
async prompt (title, messages=[], placeholder='') {
|
|
||||||
if (typeof messages === 'string') {
|
|
||||||
messages = [messages];
|
|
||||||
}
|
|
||||||
const model = new Model({
|
|
||||||
title,
|
|
||||||
messages,
|
|
||||||
'fields': [{
|
|
||||||
'name': 'reason',
|
|
||||||
'placeholder': placeholder,
|
|
||||||
}],
|
|
||||||
'type': 'prompt'
|
|
||||||
})
|
|
||||||
const prompt = new Confirm({model});
|
|
||||||
prompt.show();
|
|
||||||
let result;
|
|
||||||
try {
|
|
||||||
result = (await prompt.confirmation).pop()?.value;
|
|
||||||
} catch (e) {
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
prompt.remove();
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show an alert modal to the user.
|
|
||||||
* @method _converse.api.alert
|
|
||||||
* @param { ('info'|'warn'|'error') } type - The type of alert.
|
|
||||||
* @param { String } title - The header text for the alert.
|
|
||||||
* @param { (String[]|String) } messages - The alert text to show to the user.
|
|
||||||
*/
|
|
||||||
alert (type, title, messages) {
|
|
||||||
if (typeof messages === 'string') {
|
|
||||||
messages = [messages];
|
|
||||||
}
|
|
||||||
let level;
|
|
||||||
if (type === 'error') {
|
|
||||||
level = 'alert-danger';
|
|
||||||
} else if (type === 'info') {
|
|
||||||
level = 'alert-info';
|
|
||||||
} else if (type === 'warn') {
|
|
||||||
level = 'alert-warning';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alert === undefined) {
|
|
||||||
const model = new Model({
|
|
||||||
'title': title,
|
|
||||||
'messages': messages,
|
|
||||||
'level': level,
|
|
||||||
'type': 'alert'
|
|
||||||
})
|
|
||||||
alert = new Alert({model});
|
|
||||||
} else {
|
|
||||||
alert.model.set({
|
|
||||||
'title': title,
|
|
||||||
'messages': messages,
|
|
||||||
'level': level
|
|
||||||
});
|
|
||||||
}
|
|
||||||
alert.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,22 +3,16 @@
|
|||||||
* @copyright The Converse.js contributors
|
* @copyright The Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
|
import "modals/profile.js";
|
||||||
|
import "modals/chat-status.js";
|
||||||
import "@converse/headless/converse-status";
|
import "@converse/headless/converse-status";
|
||||||
import "@converse/headless/converse-vcard";
|
import "@converse/headless/converse-vcard";
|
||||||
import "converse-modal";
|
import "converse-modal";
|
||||||
import UserSettingsModal from "modals/user-settings";
|
import UserSettingsModal from "modals/user-settings";
|
||||||
import bootstrap from "bootstrap.native";
|
|
||||||
import log from "@converse/headless/log";
|
|
||||||
import sizzle from 'sizzle';
|
|
||||||
import tpl_chat_status_modal from "templates/chat_status_modal";
|
|
||||||
import tpl_profile from "templates/profile.js";
|
import tpl_profile from "templates/profile.js";
|
||||||
import tpl_profile_modal from "templates/profile_modal";
|
|
||||||
import { BootstrapModal } from "./converse-modal.js";
|
|
||||||
import { __ } from './i18n';
|
import { __ } from './i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/converse-core";
|
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
const u = converse.env.utils;
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-profile', {
|
converse.plugins.add('converse-profile', {
|
||||||
|
|
||||||
@ -35,146 +29,6 @@ converse.plugins.add('converse-profile', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
_converse.ProfileModal = BootstrapModal.extend({
|
|
||||||
id: "user-profile-modal",
|
|
||||||
events: {
|
|
||||||
'submit .profile-form': 'onFormSubmitted'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
this.listenTo(this.model, 'change', this.render);
|
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
|
||||||
/**
|
|
||||||
* Triggered when the _converse.ProfileModal has been created and initialized.
|
|
||||||
* @event _converse#profileModalInitialized
|
|
||||||
* @type { _converse.XMPPStatus }
|
|
||||||
* @example _converse.api.listen.on('profileModalInitialized', status => { ... });
|
|
||||||
*/
|
|
||||||
api.trigger('profileModalInitialized', this.model);
|
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
return tpl_profile_modal(Object.assign(
|
|
||||||
this.model.toJSON(),
|
|
||||||
this.model.vcard.toJSON(),
|
|
||||||
this.getAvatarData(),
|
|
||||||
{ 'view': this }
|
|
||||||
));
|
|
||||||
},
|
|
||||||
|
|
||||||
getAvatarData () {
|
|
||||||
const image_type = this.model.vcard.get('image_type');
|
|
||||||
const image_data = this.model.vcard.get('image');
|
|
||||||
const image = "data:" + image_type + ";base64," + image_data;
|
|
||||||
return {
|
|
||||||
'height': 128,
|
|
||||||
'width': 128,
|
|
||||||
image,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
afterRender () {
|
|
||||||
this.tabs = sizzle('.nav-item .nav-link', this.el).map(e => new bootstrap.Tab(e));
|
|
||||||
},
|
|
||||||
|
|
||||||
async setVCard (data) {
|
|
||||||
try {
|
|
||||||
await api.vcard.set(_converse.bare_jid, data);
|
|
||||||
} catch (err) {
|
|
||||||
log.fatal(err);
|
|
||||||
this.alert([
|
|
||||||
__("Sorry, an error happened while trying to save your profile data."),
|
|
||||||
__("You can check your browser's developer console for any error output.")
|
|
||||||
].join(" "));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.modal.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
onFormSubmitted (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
const reader = new FileReader();
|
|
||||||
const form_data = new FormData(ev.target);
|
|
||||||
const image_file = form_data.get('image');
|
|
||||||
const data = {
|
|
||||||
'fn': form_data.get('fn'),
|
|
||||||
'nickname': form_data.get('nickname'),
|
|
||||||
'role': form_data.get('role'),
|
|
||||||
'email': form_data.get('email'),
|
|
||||||
'url': form_data.get('url'),
|
|
||||||
};
|
|
||||||
if (!image_file.size) {
|
|
||||||
Object.assign(data, {
|
|
||||||
'image': this.model.vcard.get('image'),
|
|
||||||
'image_type': this.model.vcard.get('image_type')
|
|
||||||
});
|
|
||||||
this.setVCard(data);
|
|
||||||
} else {
|
|
||||||
reader.onloadend = () => {
|
|
||||||
Object.assign(data, {
|
|
||||||
'image': btoa(reader.result),
|
|
||||||
'image_type': image_file.type
|
|
||||||
});
|
|
||||||
this.setVCard(data);
|
|
||||||
};
|
|
||||||
reader.readAsBinaryString(image_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
_converse.ChatStatusModal = BootstrapModal.extend({
|
|
||||||
id: "modal-status-change",
|
|
||||||
events: {
|
|
||||||
"submit form#set-xmpp-status": "onFormSubmitted",
|
|
||||||
"click .clear-input": "clearStatusMessage"
|
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
return tpl_chat_status_modal(
|
|
||||||
Object.assign(
|
|
||||||
this.model.toJSON(),
|
|
||||||
this.model.vcard.toJSON(), {
|
|
||||||
'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);
|
|
||||||
},
|
|
||||||
|
|
||||||
clearStatusMessage (ev) {
|
|
||||||
if (ev && ev.preventDefault) {
|
|
||||||
ev.preventDefault();
|
|
||||||
u.hideElement(this.el.querySelector('.clear-input'));
|
|
||||||
}
|
|
||||||
const roster_filter = this.el.querySelector('input[name="status_message"]');
|
|
||||||
roster_filter.value = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
onFormSubmitted (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
const data = new FormData(ev.target);
|
|
||||||
this.model.save({
|
|
||||||
'status_message': data.get('status_message'),
|
|
||||||
'status': data.get('chat_status')
|
|
||||||
});
|
|
||||||
this.modal.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_converse.XMPPStatusView = _converse.ViewWithAvatar.extend({
|
_converse.XMPPStatusView = _converse.ViewWithAvatar.extend({
|
||||||
tagName: "div",
|
tagName: "div",
|
||||||
events: {
|
events: {
|
||||||
|
@ -6,23 +6,21 @@
|
|||||||
import "@converse/headless/converse-chatboxes";
|
import "@converse/headless/converse-chatboxes";
|
||||||
import "@converse/headless/converse-roster";
|
import "@converse/headless/converse-roster";
|
||||||
import "converse-modal";
|
import "converse-modal";
|
||||||
|
import "modals/add-contact.js";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import tpl_add_contact_modal from "templates/add_contact_modal.js";
|
|
||||||
import tpl_group_header from "templates/group_header.html";
|
import tpl_group_header from "templates/group_header.html";
|
||||||
import tpl_pending_contact from "templates/pending_contact.html";
|
import tpl_pending_contact from "templates/pending_contact.html";
|
||||||
import tpl_requesting_contact from "templates/requesting_contact.html";
|
import tpl_requesting_contact from "templates/requesting_contact.html";
|
||||||
import tpl_roster from "templates/roster.html";
|
import tpl_roster from "templates/roster.html";
|
||||||
import tpl_roster_filter from "templates/roster_filter.js";
|
import tpl_roster_filter from "templates/roster_filter.js";
|
||||||
import tpl_roster_item from "templates/roster_item.html";
|
import tpl_roster_item from "templates/roster_item.html";
|
||||||
import { BootstrapModal } from "./converse-modal.js";
|
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { OrderedListView } from "@converse/skeletor/src/overview";
|
import { OrderedListView } from "@converse/skeletor/src/overview";
|
||||||
import { View } from '@converse/skeletor/src/view.js';
|
import { View } from '@converse/skeletor/src/view.js';
|
||||||
import { __ } from './i18n';
|
import { __ } from './i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/converse-core";
|
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||||
import { compact, debounce, has, without } from "lodash-es";
|
import { debounce, has, without } from "lodash-es";
|
||||||
|
|
||||||
const { Strophe } = converse.env;
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
@ -55,136 +53,6 @@ converse.plugins.add('converse-rosterview', {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
_converse.AddContactModal = BootstrapModal.extend({
|
|
||||||
id: "add-contact-modal",
|
|
||||||
events: {
|
|
||||||
'submit form': 'addContactFromForm'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
|
||||||
this.listenTo(this.model, 'change', this.render);
|
|
||||||
},
|
|
||||||
|
|
||||||
toHTML () {
|
|
||||||
const label_nickname = api.settings.get('xhr_user_search_url') ? __('Contact name') : __('Optional nickname');
|
|
||||||
return tpl_add_contact_modal(Object.assign(this.model.toJSON(), { _converse, label_nickname }));
|
|
||||||
},
|
|
||||||
|
|
||||||
afterRender () {
|
|
||||||
if (typeof api.settings.get('xhr_user_search_url') === 'string') {
|
|
||||||
this.initXHRAutoComplete();
|
|
||||||
} else {
|
|
||||||
this.initJIDAutoComplete();
|
|
||||||
}
|
|
||||||
const jid_input = this.el.querySelector('input[name="jid"]');
|
|
||||||
this.el.addEventListener('shown.bs.modal', () => jid_input.focus(), false);
|
|
||||||
},
|
|
||||||
|
|
||||||
initJIDAutoComplete () {
|
|
||||||
if (!api.settings.get('autocomplete_add_contact')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const el = this.el.querySelector('.suggestion-box__jid').parentElement;
|
|
||||||
this.jid_auto_complete = new _converse.AutoComplete(el, {
|
|
||||||
'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`,
|
|
||||||
'filter': _converse.FILTER_STARTSWITH,
|
|
||||||
'list': [...new Set(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))]
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
initXHRAutoComplete () {
|
|
||||||
if (!api.settings.get('autocomplete_add_contact')) {
|
|
||||||
return this.initXHRFetch();
|
|
||||||
}
|
|
||||||
const el = this.el.querySelector('.suggestion-box__name').parentElement;
|
|
||||||
this.name_auto_complete = new _converse.AutoComplete(el, {
|
|
||||||
'auto_evaluate': false,
|
|
||||||
'filter': _converse.FILTER_STARTSWITH,
|
|
||||||
'list': []
|
|
||||||
});
|
|
||||||
const xhr = new window.XMLHttpRequest();
|
|
||||||
// `open` must be called after `onload` for mock/testing purposes.
|
|
||||||
xhr.onload = () => {
|
|
||||||
if (xhr.responseText) {
|
|
||||||
const r = xhr.responseText;
|
|
||||||
this.name_auto_complete.list = JSON.parse(r).map(i => ({'label': i.fullname || i.jid, 'value': i.jid}));
|
|
||||||
this.name_auto_complete.auto_completing = true;
|
|
||||||
this.name_auto_complete.evaluate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const input_el = this.el.querySelector('input[name="name"]');
|
|
||||||
input_el.addEventListener('input', debounce(() => {
|
|
||||||
xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
|
||||||
xhr.send()
|
|
||||||
} , 300));
|
|
||||||
this.name_auto_complete.on('suggestion-box-selectcomplete', ev => {
|
|
||||||
this.el.querySelector('input[name="name"]').value = ev.text.label;
|
|
||||||
this.el.querySelector('input[name="jid"]').value = ev.text.value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
initXHRFetch () {
|
|
||||||
this.xhr = new window.XMLHttpRequest();
|
|
||||||
this.xhr.onload = () => {
|
|
||||||
if (this.xhr.responseText) {
|
|
||||||
const r = this.xhr.responseText;
|
|
||||||
const list = JSON.parse(r).map(i => ({'label': i.fullname || i.jid, 'value': i.jid}));
|
|
||||||
if (list.length !== 1) {
|
|
||||||
const el = this.el.querySelector('.invalid-feedback');
|
|
||||||
el.textContent = __('Sorry, could not find a contact with that name')
|
|
||||||
u.addClass('d-block', el);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const jid = list[0].value;
|
|
||||||
if (this.validateSubmission(jid)) {
|
|
||||||
const form = this.el.querySelector('form');
|
|
||||||
const name = list[0].label;
|
|
||||||
this.afterSubmission(form, jid, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
validateSubmission (jid) {
|
|
||||||
const el = this.el.querySelector('.invalid-feedback');
|
|
||||||
if (!jid || compact(jid.split('@')).length < 2) {
|
|
||||||
u.addClass('is-invalid', this.el.querySelector('input[name="jid"]'));
|
|
||||||
u.addClass('d-block', el);
|
|
||||||
return false;
|
|
||||||
} else if (_converse.roster.get(Strophe.getBareJidFromJid(jid))) {
|
|
||||||
el.textContent = __('This contact has already been added')
|
|
||||||
u.addClass('d-block', el);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
u.removeClass('d-block', el);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
afterSubmission (form, jid, name) {
|
|
||||||
_converse.roster.addAndSubscribe(jid, name);
|
|
||||||
this.model.clear();
|
|
||||||
this.modal.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
addContactFromForm (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
const data = new FormData(ev.target),
|
|
||||||
jid = (data.get('jid') || '').trim();
|
|
||||||
|
|
||||||
if (!jid && typeof api.settings.get('xhr_user_search_url') === 'string') {
|
|
||||||
const input_el = this.el.querySelector('input[name="name"]');
|
|
||||||
this.xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
|
||||||
this.xhr.send()
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.validateSubmission(jid)) {
|
|
||||||
this.afterSubmission(ev.target, jid, data.get('name'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
_converse.RosterFilter = Model.extend({
|
_converse.RosterFilter = Model.extend({
|
||||||
initialize () {
|
initialize () {
|
||||||
this.set({
|
this.set({
|
||||||
@ -195,6 +63,7 @@ converse.plugins.add('converse-rosterview', {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
_converse.RosterFilterView = View.extend({
|
_converse.RosterFilterView = View.extend({
|
||||||
tagName: 'span',
|
tagName: 'span',
|
||||||
|
|
||||||
@ -980,4 +849,3 @@ converse.plugins.add('converse-rosterview', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
142
src/modals/add-contact.js
Normal file
142
src/modals/add-contact.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
|
import tpl_add_contact_modal from "./templates/add-contact.js";
|
||||||
|
import { __ } from '../i18n';
|
||||||
|
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||||
|
import { compact, debounce } from "lodash-es";
|
||||||
|
|
||||||
|
const { Strophe } = converse.env;
|
||||||
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
|
const AddContactModal = BootstrapModal.extend({
|
||||||
|
id: "add-contact-modal",
|
||||||
|
events: {
|
||||||
|
'submit form': 'addContactFromForm'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||||
|
this.listenTo(this.model, 'change', this.render);
|
||||||
|
},
|
||||||
|
|
||||||
|
toHTML () {
|
||||||
|
const label_nickname = api.settings.get('xhr_user_search_url') ? __('Contact name') : __('Optional nickname');
|
||||||
|
return tpl_add_contact_modal(Object.assign(this.model.toJSON(), { _converse, label_nickname }));
|
||||||
|
},
|
||||||
|
|
||||||
|
afterRender () {
|
||||||
|
if (typeof api.settings.get('xhr_user_search_url') === 'string') {
|
||||||
|
this.initXHRAutoComplete();
|
||||||
|
} else {
|
||||||
|
this.initJIDAutoComplete();
|
||||||
|
}
|
||||||
|
const jid_input = this.el.querySelector('input[name="jid"]');
|
||||||
|
this.el.addEventListener('shown.bs.modal', () => jid_input.focus(), false);
|
||||||
|
},
|
||||||
|
|
||||||
|
initJIDAutoComplete () {
|
||||||
|
if (!api.settings.get('autocomplete_add_contact')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const el = this.el.querySelector('.suggestion-box__jid').parentElement;
|
||||||
|
this.jid_auto_complete = new _converse.AutoComplete(el, {
|
||||||
|
'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`,
|
||||||
|
'filter': _converse.FILTER_STARTSWITH,
|
||||||
|
'list': [...new Set(_converse.roster.map(item => Strophe.getDomainFromJid(item.get('jid'))))]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
initXHRAutoComplete () {
|
||||||
|
if (!api.settings.get('autocomplete_add_contact')) {
|
||||||
|
return this.initXHRFetch();
|
||||||
|
}
|
||||||
|
const el = this.el.querySelector('.suggestion-box__name').parentElement;
|
||||||
|
this.name_auto_complete = new _converse.AutoComplete(el, {
|
||||||
|
'auto_evaluate': false,
|
||||||
|
'filter': _converse.FILTER_STARTSWITH,
|
||||||
|
'list': []
|
||||||
|
});
|
||||||
|
const xhr = new window.XMLHttpRequest();
|
||||||
|
// `open` must be called after `onload` for mock/testing purposes.
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.responseText) {
|
||||||
|
const r = xhr.responseText;
|
||||||
|
this.name_auto_complete.list = JSON.parse(r).map(i => ({'label': i.fullname || i.jid, 'value': i.jid}));
|
||||||
|
this.name_auto_complete.auto_completing = true;
|
||||||
|
this.name_auto_complete.evaluate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const input_el = this.el.querySelector('input[name="name"]');
|
||||||
|
input_el.addEventListener('input', debounce(() => {
|
||||||
|
xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
||||||
|
xhr.send()
|
||||||
|
} , 300));
|
||||||
|
this.name_auto_complete.on('suggestion-box-selectcomplete', ev => {
|
||||||
|
this.el.querySelector('input[name="name"]').value = ev.text.label;
|
||||||
|
this.el.querySelector('input[name="jid"]').value = ev.text.value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
initXHRFetch () {
|
||||||
|
this.xhr = new window.XMLHttpRequest();
|
||||||
|
this.xhr.onload = () => {
|
||||||
|
if (this.xhr.responseText) {
|
||||||
|
const r = this.xhr.responseText;
|
||||||
|
const list = JSON.parse(r).map(i => ({'label': i.fullname || i.jid, 'value': i.jid}));
|
||||||
|
if (list.length !== 1) {
|
||||||
|
const el = this.el.querySelector('.invalid-feedback');
|
||||||
|
el.textContent = __('Sorry, could not find a contact with that name')
|
||||||
|
u.addClass('d-block', el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const jid = list[0].value;
|
||||||
|
if (this.validateSubmission(jid)) {
|
||||||
|
const form = this.el.querySelector('form');
|
||||||
|
const name = list[0].label;
|
||||||
|
this.afterSubmission(form, jid, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
validateSubmission (jid) {
|
||||||
|
const el = this.el.querySelector('.invalid-feedback');
|
||||||
|
if (!jid || compact(jid.split('@')).length < 2) {
|
||||||
|
u.addClass('is-invalid', this.el.querySelector('input[name="jid"]'));
|
||||||
|
u.addClass('d-block', el);
|
||||||
|
return false;
|
||||||
|
} else if (_converse.roster.get(Strophe.getBareJidFromJid(jid))) {
|
||||||
|
el.textContent = __('This contact has already been added')
|
||||||
|
u.addClass('d-block', el);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
u.removeClass('d-block', el);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
afterSubmission (form, jid, name) {
|
||||||
|
_converse.roster.addAndSubscribe(jid, name);
|
||||||
|
this.model.clear();
|
||||||
|
this.modal.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
addContactFromForm (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
const data = new FormData(ev.target),
|
||||||
|
jid = (data.get('jid') || '').trim();
|
||||||
|
|
||||||
|
if (!jid && typeof api.settings.get('xhr_user_search_url') === 'string') {
|
||||||
|
const input_el = this.el.querySelector('input[name="name"]');
|
||||||
|
this.xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true);
|
||||||
|
this.xhr.send()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.validateSubmission(jid)) {
|
||||||
|
this.afterSubmission(ev.target, jid, data.get('name'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_converse.AddContactModal = AddContactModal;
|
||||||
|
|
||||||
|
export default AddContactModal;
|
@ -1,5 +1,5 @@
|
|||||||
import tpl_add_chatroom_modal from "templates/add_chatroom_modal.js";
|
import tpl_add_muc from "./templates/add-muc.js";
|
||||||
import { BootstrapModal } from "../converse-modal.js";
|
import BootstrapModal from "./base.js";
|
||||||
import { Strophe } from 'strophe.js/src/strophe';
|
import { Strophe } from 'strophe.js/src/strophe';
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/converse-core";
|
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||||
@ -8,6 +8,7 @@ const u = converse.env.utils;
|
|||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default BootstrapModal.extend({
|
||||||
|
persistent: true,
|
||||||
id: 'add-chatroom-modal',
|
id: 'add-chatroom-modal',
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
@ -28,7 +29,7 @@ export default BootstrapModal.extend({
|
|||||||
const muc_domain = this.model.get('muc_domain') || api.settings.get('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');
|
placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
|
||||||
}
|
}
|
||||||
return tpl_add_chatroom_modal(Object.assign(this.model.toJSON(), {
|
return tpl_add_muc(Object.assign(this.model.toJSON(), {
|
||||||
'_converse': _converse,
|
'_converse': _converse,
|
||||||
'label_room_address': api.settings.get('muc_domain') ? __('Groupchat name') : __('Groupchat address'),
|
'label_room_address': api.settings.get('muc_domain') ? __('Groupchat name') : __('Groupchat address'),
|
||||||
'chatroom_placeholder': placeholder,
|
'chatroom_placeholder': placeholder,
|
||||||
|
18
src/modals/alert.js
Normal file
18
src/modals/alert.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
|
import tpl_alert_modal from "./templates/alert.js";
|
||||||
|
import { __ } from '../i18n';
|
||||||
|
|
||||||
|
|
||||||
|
const Alert = BootstrapModal.extend({
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||||
|
this.listenTo(this.model, 'change', this.render)
|
||||||
|
},
|
||||||
|
|
||||||
|
toHTML () {
|
||||||
|
return tpl_alert_modal(Object.assign({__}, this.model.toJSON()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Alert;
|
84
src/modals/base.js
Normal file
84
src/modals/base.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import bootstrap from "bootstrap.native";
|
||||||
|
import log from "@converse/headless/log";
|
||||||
|
import tpl_alert_component from "templates/alert.js";
|
||||||
|
import { View } from '@converse/skeletor/src/view.js';
|
||||||
|
import { _converse, converse } from "@converse/headless/converse-core";
|
||||||
|
import { render } from 'lit-html';
|
||||||
|
|
||||||
|
const { sizzle } = converse.env;
|
||||||
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
|
const BaseModal = View.extend({
|
||||||
|
className: "modal",
|
||||||
|
persistent: false, // Whether this modal should persist in the DOM once it's been closed
|
||||||
|
events: {
|
||||||
|
'click .nav-item .nav-link': 'switchTab'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
this.render()
|
||||||
|
|
||||||
|
this.el.setAttribute('tabindex', '-1');
|
||||||
|
this.el.setAttribute('role', 'dialog');
|
||||||
|
this.el.setAttribute('aria-hidden', 'true');
|
||||||
|
const label_id = this.el.querySelector('.modal-title').getAttribute('id');
|
||||||
|
label_id && this.el.setAttribute('aria-labelledby', label_id);
|
||||||
|
|
||||||
|
this.insertIntoDOM();
|
||||||
|
const Modal = bootstrap.Modal;
|
||||||
|
this.modal = new Modal(this.el, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true
|
||||||
|
});
|
||||||
|
this.el.addEventListener('hide.bs.modal', () => this.onHide(), false);
|
||||||
|
},
|
||||||
|
|
||||||
|
onHide () {
|
||||||
|
u.removeClass('selected', this.trigger_el);
|
||||||
|
!this.persistent && this.remove();
|
||||||
|
},
|
||||||
|
|
||||||
|
insertIntoDOM () {
|
||||||
|
const container_el = _converse.chatboxviews.el.querySelector("#converse-modals");
|
||||||
|
container_el.insertAdjacentElement('beforeEnd', this.el);
|
||||||
|
},
|
||||||
|
|
||||||
|
switchTab (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
sizzle('.nav-link.active', this.el).forEach(el => {
|
||||||
|
u.removeClass('active', this.el.querySelector(el.getAttribute('href')));
|
||||||
|
u.removeClass('active', el);
|
||||||
|
});
|
||||||
|
u.addClass('active', ev.target);
|
||||||
|
u.addClass('active', this.el.querySelector(ev.target.getAttribute('href')))
|
||||||
|
},
|
||||||
|
|
||||||
|
alert (message, type='primary') {
|
||||||
|
const body = this.el.querySelector('.modal-alert');
|
||||||
|
if (body === null) {
|
||||||
|
log.error("Could not find a .modal-alert element in the modal to show an alert message in!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// FIXME: Instead of adding the alert imperatively, we should
|
||||||
|
// find a way to let the modal rerender with an alert message
|
||||||
|
render(tpl_alert_component({'type': `alert-${type}`, 'message': message}), body);
|
||||||
|
const el = body.firstElementChild;
|
||||||
|
setTimeout(() => {
|
||||||
|
u.addClass('fade-out', el);
|
||||||
|
setTimeout(() => u.removeElement(el), 600);
|
||||||
|
}, 5000);
|
||||||
|
},
|
||||||
|
|
||||||
|
show (ev) {
|
||||||
|
if (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
this.trigger_el = ev.target;
|
||||||
|
this.trigger_el.classList.add('selected');
|
||||||
|
}
|
||||||
|
this.modal.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default BaseModal;
|
64
src/modals/chat-status.js
Normal file
64
src/modals/chat-status.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
|
import tpl_chat_status_modal from "./templates/chat-status.js";
|
||||||
|
import { __ } from '../i18n';
|
||||||
|
import { _converse, converse } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
|
const ChatStatusModal = BootstrapModal.extend({
|
||||||
|
id: "modal-status-change",
|
||||||
|
events: {
|
||||||
|
"submit form#set-xmpp-status": "onFormSubmitted",
|
||||||
|
"click .clear-input": "clearStatusMessage"
|
||||||
|
},
|
||||||
|
|
||||||
|
toHTML () {
|
||||||
|
return tpl_chat_status_modal(
|
||||||
|
Object.assign(
|
||||||
|
this.model.toJSON(),
|
||||||
|
this.model.vcard.toJSON(), {
|
||||||
|
'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);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearStatusMessage (ev) {
|
||||||
|
if (ev && ev.preventDefault) {
|
||||||
|
ev.preventDefault();
|
||||||
|
u.hideElement(this.el.querySelector('.clear-input'));
|
||||||
|
}
|
||||||
|
const roster_filter = this.el.querySelector('input[name="status_message"]');
|
||||||
|
roster_filter.value = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
onFormSubmitted (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
const data = new FormData(ev.target);
|
||||||
|
this.model.save({
|
||||||
|
'status_message': data.get('status_message'),
|
||||||
|
'status': data.get('chat_status')
|
||||||
|
});
|
||||||
|
this.modal.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
_converse.ChatStatusModal = ChatStatusModal;
|
||||||
|
|
||||||
|
export default ChatStatusModal;
|
59
src/modals/confirm.js
Normal file
59
src/modals/confirm.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import BootstrapModal from './base.js';
|
||||||
|
import tpl_prompt from "./templates/prompt.js";
|
||||||
|
import { converse } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
|
const Confirm = BootstrapModal.extend({
|
||||||
|
events: {
|
||||||
|
'submit .confirm': 'onConfimation'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
this.confirmation = u.getResolveablePromise();
|
||||||
|
BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||||
|
this.listenTo(this.model, 'change', this.render)
|
||||||
|
this.el.addEventListener('closed.bs.modal', () => this.confirmation.reject(), false);
|
||||||
|
},
|
||||||
|
|
||||||
|
toHTML () {
|
||||||
|
return tpl_prompt(this.model.toJSON());
|
||||||
|
},
|
||||||
|
|
||||||
|
afterRender () {
|
||||||
|
if (!this.close_handler_registered) {
|
||||||
|
this.el.addEventListener('closed.bs.modal', () => {
|
||||||
|
if (!this.confirmation.isResolved) {
|
||||||
|
this.confirmation.reject()
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
this.close_handler_registered = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onConfimation (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
const form_data = new FormData(ev.target);
|
||||||
|
const fields = (this.model.get('fields') || [])
|
||||||
|
.map(field => {
|
||||||
|
const value = form_data.get(field.name).trim();
|
||||||
|
field.value = value;
|
||||||
|
if (field.challenge) {
|
||||||
|
field.challenge_failed = (value !== field.challenge);
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fields.filter(c => c.challenge_failed).length) {
|
||||||
|
this.model.set('fields', fields);
|
||||||
|
// Setting an array doesn't trigger a change event
|
||||||
|
this.model.trigger('change');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.confirmation.resolve(fields);
|
||||||
|
this.modal.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Confirm;
|
@ -1,5 +1,5 @@
|
|||||||
import { BootstrapModal } from "../converse-modal.js";
|
import BootstrapModal from "./base.js";
|
||||||
import tpl_image_modal from "../templates/image_modal.js";
|
import tpl_image_modal from "./templates/image.js";
|
||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default BootstrapModal.extend({
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { BootstrapModal } from "../converse-modal.js";
|
import BootstrapModal from "./base.js";
|
||||||
import tpl_message_versions_modal from "../templates/message_versions_modal.js";
|
import tpl_message_versions_modal from "./templates/message-versions.js";
|
||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default BootstrapModal.extend({
|
||||||
// FIXME: this isn't globally unique
|
|
||||||
id: "message-versions-modal",
|
|
||||||
toHTML () {
|
toHTML () {
|
||||||
return tpl_message_versions_modal(this.model.toJSON());
|
return tpl_message_versions_modal(this.model.toJSON());
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import sizzle from "sizzle";
|
import sizzle from "sizzle";
|
||||||
import tpl_moderator_tools_modal from "../templates/moderator_tools_modal.js";
|
import tpl_moderator_tools_modal from "./templates/moderator-tools.js";
|
||||||
import { AFFILIATIONS, ROLES } from "@converse/headless/converse-muc.js";
|
import { AFFILIATIONS, ROLES } from "@converse/headless/converse-muc.js";
|
||||||
import { BootstrapModal } from "../converse-modal.js";
|
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../i18n';
|
||||||
import { api, converse } from "@converse/headless/converse-core";
|
import { api, converse } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ let _converse;
|
|||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default BootstrapModal.extend({
|
||||||
id: "converse-modtools-modal",
|
persistent: true,
|
||||||
|
|
||||||
initialize (attrs) {
|
initialize (attrs) {
|
||||||
_converse = attrs._converse;
|
_converse = attrs._converse;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BootstrapModal } from "../converse-modal.js";
|
import BootstrapModal from "./base.js";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../i18n';
|
||||||
import { api, converse } from "@converse/headless/converse-core";
|
import { api, converse } from "@converse/headless/converse-core";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BootstrapModal } from "../converse-modal.js";
|
import BootstrapModal from "./base.js";
|
||||||
|
import tpl_muc_details from "./templates/muc-details.js";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../i18n';
|
||||||
import tpl_chatroom_details_modal from "../templates/chatroom_details_modal.js";
|
|
||||||
|
|
||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default BootstrapModal.extend({
|
||||||
@ -15,7 +15,7 @@ export default BootstrapModal.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
toHTML () {
|
toHTML () {
|
||||||
return tpl_chatroom_details_modal(Object.assign(
|
return tpl_muc_details(Object.assign(
|
||||||
this.model.toJSON(), {
|
this.model.toJSON(), {
|
||||||
'config': this.model.config.toJSON(),
|
'config': this.model.config.toJSON(),
|
||||||
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
|
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import tpl_muc_invite_modal from "templates/muc_invite_modal.js";
|
import BootstrapModal from "./base.js";
|
||||||
import { BootstrapModal } from "../converse-modal.js";
|
import tpl_muc_invite_modal from "./templates/muc-invite.js";
|
||||||
import { _converse, converse } from "@converse/headless/converse-core";
|
import { _converse, converse } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
@ -49,5 +49,3 @@ export default BootstrapModal.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import sizzle from 'sizzle';
|
import sizzle from 'sizzle';
|
||||||
import st from "@converse/headless/utils/stanza";
|
import st from "@converse/headless/utils/stanza";
|
||||||
import tpl_list_chatrooms_modal from "templates/list_chatrooms_modal.js";
|
import tpl_list_chatrooms_modal from "./templates/muc-list.js";
|
||||||
import tpl_room_description from "templates/room_description.html";
|
import tpl_room_description from "templates/room_description.html";
|
||||||
import tpl_spinner from "templates/spinner.js";
|
import tpl_spinner from "templates/spinner.js";
|
||||||
import { BootstrapModal } from "../converse-modal.js";
|
|
||||||
import { Strophe, $iq } from 'strophe.js/src/strophe';
|
import { Strophe, $iq } from 'strophe.js/src/strophe';
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/converse-core";
|
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||||
@ -83,6 +83,7 @@ function toggleRoomInfo (ev) {
|
|||||||
|
|
||||||
export default BootstrapModal.extend({
|
export default BootstrapModal.extend({
|
||||||
id: "list-chatrooms-modal",
|
id: "list-chatrooms-modal",
|
||||||
|
persistent: true,
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
import tpl_occupant_modal from "./templates/occupant.js";
|
import tpl_occupant_modal from "./templates/occupant.js";
|
||||||
import { BootstrapModal } from "../converse-modal.js";
|
|
||||||
import { _converse, api } from "@converse/headless/converse-core";
|
import { _converse, api } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
|
|
||||||
const OccupantModal = BootstrapModal.extend({
|
const OccupantModal = BootstrapModal.extend({
|
||||||
id: "muc-occupant-modal",
|
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
BootstrapModal.prototype.initialize.apply(this, arguments);
|
BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(this.model, 'change', this.render);
|
||||||
/**
|
/**
|
||||||
* Triggered once the OccupantModal has been initialized
|
* Triggered once the OccupantModal has been initialized
|
||||||
* @event _converse#userDetailsModalInitialized
|
* @event _converse#userDetailsModalInitialized
|
||||||
* @type { _converse.ChatBox }
|
* @type { _converse.ChatBox }
|
||||||
* @example _converse.api.listen.on('userDetailsModalInitialized', chatbox => { ... });
|
* @example _converse.api.listen.on('userDetailsModalInitialized', chatbox => { ... });
|
||||||
*/
|
*/
|
||||||
api.trigger('occupantModalInitialized', this.model);
|
api.trigger('occupantModalInitialized', this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
99
src/modals/profile.js
Normal file
99
src/modals/profile.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
|
import bootstrap from "bootstrap.native";
|
||||||
|
import log from "@converse/headless/log";
|
||||||
|
import sizzle from 'sizzle';
|
||||||
|
import tpl_profile_modal from "./templates/profile.js";
|
||||||
|
import { __ } from '../i18n';
|
||||||
|
import { _converse, api } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
|
|
||||||
|
const ProfileModal = BootstrapModal.extend({
|
||||||
|
id: "user-profile-modal",
|
||||||
|
events: {
|
||||||
|
'submit .profile-form': 'onFormSubmitted'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
this.listenTo(this.model, 'change', this.render);
|
||||||
|
BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||||
|
/**
|
||||||
|
* Triggered when the _converse.ProfileModal has been created and initialized.
|
||||||
|
* @event _converse#profileModalInitialized
|
||||||
|
* @type { _converse.XMPPStatus }
|
||||||
|
* @example _converse.api.listen.on('profileModalInitialized', status => { ... });
|
||||||
|
*/
|
||||||
|
api.trigger('profileModalInitialized', this.model);
|
||||||
|
},
|
||||||
|
|
||||||
|
toHTML () {
|
||||||
|
return tpl_profile_modal(Object.assign(
|
||||||
|
this.model.toJSON(),
|
||||||
|
this.model.vcard.toJSON(),
|
||||||
|
this.getAvatarData(),
|
||||||
|
{ 'view': this }
|
||||||
|
));
|
||||||
|
},
|
||||||
|
|
||||||
|
getAvatarData () {
|
||||||
|
const image_type = this.model.vcard.get('image_type');
|
||||||
|
const image_data = this.model.vcard.get('image');
|
||||||
|
const image = "data:" + image_type + ";base64," + image_data;
|
||||||
|
return {
|
||||||
|
'height': 128,
|
||||||
|
'width': 128,
|
||||||
|
image,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
afterRender () {
|
||||||
|
this.tabs = sizzle('.nav-item .nav-link', this.el).map(e => new bootstrap.Tab(e));
|
||||||
|
},
|
||||||
|
|
||||||
|
async setVCard (data) {
|
||||||
|
try {
|
||||||
|
await api.vcard.set(_converse.bare_jid, data);
|
||||||
|
} catch (err) {
|
||||||
|
log.fatal(err);
|
||||||
|
this.alert([
|
||||||
|
__("Sorry, an error happened while trying to save your profile data."),
|
||||||
|
__("You can check your browser's developer console for any error output.")
|
||||||
|
].join(" "));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.modal.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
onFormSubmitted (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
const reader = new FileReader();
|
||||||
|
const form_data = new FormData(ev.target);
|
||||||
|
const image_file = form_data.get('image');
|
||||||
|
const data = {
|
||||||
|
'fn': form_data.get('fn'),
|
||||||
|
'nickname': form_data.get('nickname'),
|
||||||
|
'role': form_data.get('role'),
|
||||||
|
'email': form_data.get('email'),
|
||||||
|
'url': form_data.get('url'),
|
||||||
|
};
|
||||||
|
if (!image_file.size) {
|
||||||
|
Object.assign(data, {
|
||||||
|
'image': this.model.vcard.get('image'),
|
||||||
|
'image_type': this.model.vcard.get('image_type')
|
||||||
|
});
|
||||||
|
this.setVCard(data);
|
||||||
|
} else {
|
||||||
|
reader.onloadend = () => {
|
||||||
|
Object.assign(data, {
|
||||||
|
'image': btoa(reader.result),
|
||||||
|
'image_type': image_file.type
|
||||||
|
});
|
||||||
|
this.setVCard(data);
|
||||||
|
};
|
||||||
|
reader.readAsBinaryString(image_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_converse.ProfileModal = ProfileModal;
|
||||||
|
|
||||||
|
export default ProfileModal;
|
@ -1,6 +1,6 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { modal_header_close_button } from "./buttons"
|
import { modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
export default (o) => {
|
@ -1,5 +1,5 @@
|
|||||||
import xss from "xss/dist/xss";
|
import xss from "xss/dist/xss";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { modal_header_close_button } from "./buttons"
|
import { modal_header_close_button } from "./buttons"
|
||||||
import { unsafeHTML } from "lit-html/directives/unsafe-html.js";
|
import { unsafeHTML } from "lit-html/directives/unsafe-html.js";
|
@ -1,5 +1,5 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { modal_header_close_button } from "./buttons"
|
import { modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
export default (o) => html`
|
export default (o) => html`
|
@ -1,8 +1,7 @@
|
|||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
|
|
||||||
|
|
||||||
export const modal_close_button = html`<button type="button" class="btn btn-secondary" data-dismiss="modal">${__('Close')}</button>`;
|
export const modal_close_button = html`<button type="button" class="btn btn-secondary" data-dismiss="modal">${__('Close')}</button>`;
|
||||||
|
|
||||||
export const modal_header_close_button = html`<button type="button" class="close" data-dismiss="modal" aria-label="${__('Close')}"><span aria-hidden="true">×</span></button>`;
|
export const modal_header_close_button = html`<button type="button" class="close" data-dismiss="modal" aria-label="${__('Close')}"><span aria-hidden="true">×</span></button>`;
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { modal_header_close_button } from "./buttons"
|
import { modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
export default (o) => html`
|
export default (o) => html`
|
@ -1,6 +1,6 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
import { modal_close_button, modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
export default (o) => {
|
@ -1,7 +1,7 @@
|
|||||||
import { html } from "lit-html";
|
|
||||||
import { __ } from '../i18n';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
import { __ } from '../../i18n';
|
||||||
|
import { html } from "lit-html";
|
||||||
|
import { modal_close_button, modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
export default (o) => html`
|
export default (o) => html`
|
@ -1,7 +1,7 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import spinner from "./spinner.js";
|
import spinner from "../../templates/spinner.js";
|
||||||
import { modal_header_close_button } from "./buttons"
|
import { modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
function getRoleHelpText (role) {
|
function getRoleHelpText (role) {
|
@ -1,6 +1,6 @@
|
|||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
import { modal_close_button, modal_header_close_button } from "./buttons.js"
|
||||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||||
import xss from "xss/dist/xss";
|
import xss from "xss/dist/xss";
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { modal_header_close_button } from "./buttons"
|
import { modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
export default (o) => {
|
@ -1,8 +1,8 @@
|
|||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { repeat } from 'lit-html/directives/repeat.js';
|
import { repeat } from 'lit-html/directives/repeat.js';
|
||||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
import { modal_close_button, modal_header_close_button } from "./buttons.js"
|
||||||
import spinner from "./spinner.js";
|
import spinner from "../../templates/spinner.js";
|
||||||
|
|
||||||
|
|
||||||
const form = (o) => {
|
const form = (o) => {
|
@ -1,5 +1,5 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { modal_close_button, modal_header_close_button } from "../../templates/buttons"
|
import { modal_close_button, modal_header_close_button } from "./buttons.js"
|
||||||
import { renderAvatar } from '../../templates/directives/avatar';
|
import { renderAvatar } from '../../templates/directives/avatar';
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import "../components/image_picker.js";
|
import "../../components/image_picker.js";
|
||||||
import spinner from "./spinner.js";
|
import spinner from "../../templates/spinner.js";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { _converse, converse } from "@converse/headless/converse-core";
|
import { _converse, converse } from "@converse/headless/converse-core";
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { modal_header_close_button } from "./buttons";
|
import { modal_header_close_button } from "./buttons.js";
|
||||||
|
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
|
|
||||||
|
|
||||||
const tpl_field = (f) => html`
|
const tpl_field = (f) => html`
|
@ -1,7 +1,7 @@
|
|||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import avatar from "./avatar.js";
|
import avatar from "../../templates/avatar.js";
|
||||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
import { modal_close_button, modal_header_close_button } from "./buttons.js"
|
||||||
|
|
||||||
|
|
||||||
const device_fingerprint = (o) => {
|
const device_fingerprint = (o) => {
|
@ -1,9 +1,9 @@
|
|||||||
import '../components/adhoc-commands.js';
|
import '../../components/adhoc-commands.js';
|
||||||
import xss from "xss/dist/xss";
|
import xss from "xss/dist/xss";
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../../i18n';
|
||||||
import { api } from "@converse/headless/converse-core";
|
import { api } from "@converse/headless/converse-core";
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { modal_header_close_button } from "./buttons"
|
import { modal_header_close_button } from "./buttons.js"
|
||||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
|
import BootstrapModal from "./base.js";
|
||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import tpl_user_details_modal from "../templates/user_details_modal.js";
|
import tpl_user_details_modal from "./templates/user-details.js";
|
||||||
import { BootstrapModal } from "../converse-modal.js";
|
|
||||||
import { __ } from '../i18n';
|
import { __ } from '../i18n';
|
||||||
import { _converse, api, converse } from "@converse/headless/converse-core";
|
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ const u = converse.env.utils;
|
|||||||
|
|
||||||
|
|
||||||
const UserDetailsModal = BootstrapModal.extend({
|
const UserDetailsModal = BootstrapModal.extend({
|
||||||
id: "user-details-modal",
|
persistent: true,
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click button.refresh-contact': 'refreshContact',
|
'click button.refresh-contact': 'refreshContact',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BootstrapModal } from "../converse-modal.js";
|
import BootstrapModal from "./base.js";
|
||||||
import tpl_user_settings_modal from "templates/user_settings_modal";
|
import tpl_user_settings_modal from "./templates/user-settings.js";
|
||||||
|
|
||||||
let _converse;
|
let _converse;
|
||||||
|
|
||||||
@ -21,4 +21,3 @@ export default BootstrapModal.extend({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user