Replace modal templates with lit-html components
This commit is contained in:
parent
a8104d7498
commit
ad93407907
@ -259,7 +259,7 @@
|
||||
"spaced-comment": "off",
|
||||
"strict": "off",
|
||||
"symbol-description": "error",
|
||||
"template-curly-spacing": "error",
|
||||
"template-curly-spacing": "off",
|
||||
"unicode-bom": [
|
||||
"error",
|
||||
"never"
|
||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -2217,9 +2217,9 @@
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.0.tgz",
|
||||
"integrity": "sha512-n1GUYFgKm5glcy0E+U5jnqAFY2p04rnK4A0YhuM70C7Vm9Vyx+xYwd/WOTEr8nUJcbPSR/XL+/26+rirY6jJQA==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.1.tgz",
|
||||
"integrity": "sha512-89LOYH+d/vsbDX785NOfLxTW88GjNd0lWRz1DVPVsZgg9Yett5O+3MOvwo7iHgvUwbFz0mf/yPIjBkUbs4kxoQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": ">= 8"
|
||||
@ -8841,6 +8841,12 @@
|
||||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"lit-html": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.1.2.tgz",
|
||||
"integrity": "sha512-FFlUMKHKi+qG1x1iHNZ1hrtc/zHmfYTyrSvs3/wBTvaNtpZjOZGWzU7efGYVpgp6KvWeKF6ql9/KsCq6Z/mEDA==",
|
||||
"dev": true
|
||||
},
|
||||
"load-json-file": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz",
|
||||
@ -16088,8 +16094,8 @@
|
||||
"dev": true
|
||||
},
|
||||
"skeletor.js": {
|
||||
"version": "github:skeletorjs/skeletor#abc4e9d25d30159e9cffc14bf5f7ffe17b3665eb",
|
||||
"from": "github:skeletorjs/skeletor#abc4e9d25d30159e9cffc14bf5f7ffe17b3665eb",
|
||||
"version": "github:skeletorjs/skeletor#29a6d8f707076e865133b8f36f07c76ba4b4b582",
|
||||
"from": "github:skeletorjs/skeletor#29a6d8f707076e865133b8f36f07c76ba4b4b582",
|
||||
"requires": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
|
@ -88,6 +88,7 @@
|
||||
"jasmine-core": "2.99.1",
|
||||
"jsdoc": "^3.6.2",
|
||||
"lerna": "^3.20.2",
|
||||
"lit-html": "^1.1.2",
|
||||
"lodash-template-webpack-loader": "jcbrand/lodash-template-webpack-loader",
|
||||
"mini-css-extract-plugin": "^0.7.0",
|
||||
"minimist": "^1.2.0",
|
||||
|
@ -41,7 +41,7 @@
|
||||
.set-xmpp-status {
|
||||
margin: 1em;
|
||||
.custom-control-label {
|
||||
margin-top: 0.25em;
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,27 +199,25 @@
|
||||
cbview.el.querySelector('.add-contact').click()
|
||||
const modal = _converse.rosterview.add_contact_modal;
|
||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||
const sendIQ = _converse.connection.sendIQ;
|
||||
let sent_stanza, IQ_id;
|
||||
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
|
||||
sent_stanza = iq;
|
||||
IQ_id = sendIQ.bind(this)(iq, callback, errback);
|
||||
});
|
||||
expect(modal.el.querySelector('form.add-xmpp-contact')).not.toBe(null);
|
||||
|
||||
expect(!_.isNull(modal.el.querySelector('form.add-xmpp-contact'))).toBeTruthy();
|
||||
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();
|
||||
expect(sent_stanza.toLocaleString()).toEqual(
|
||||
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
|
||||
`<query xmlns="jabber:iq:roster"><item jid="someone@montague.lit" name="Someone"/></query>`+
|
||||
`</iq>`);
|
||||
|
||||
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>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
@ -228,6 +226,7 @@
|
||||
['rosterGroupsFetched'], {'autocomplete_add_contact': false},
|
||||
async function (done, _converse) {
|
||||
|
||||
await test_utils.waitForRoster(_converse, 'all', 0);
|
||||
test_utils.openControlBox(_converse);
|
||||
const cbview = _converse.chatboxviews.get('controlbox');
|
||||
cbview.el.querySelector('.add-contact').click()
|
||||
@ -236,14 +235,14 @@
|
||||
expect(modal.name_auto_complete).toBe(undefined);
|
||||
|
||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||
expect(!_.isNull(modal.el.querySelector('form.add-xmpp-contact'))).toBeTruthy();
|
||||
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(`query[xmlns="${Strophe.NS.ROSTER}"]`, s).length).pop()
|
||||
() => 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">`+
|
||||
@ -260,6 +259,8 @@
|
||||
{ 'xhr_user_search_url': 'http://example.org/?' },
|
||||
async function (done, _converse) {
|
||||
|
||||
await test_utils.waitForRoster(_converse, 'all', 0);
|
||||
|
||||
const xhr = {
|
||||
'open': function open () {},
|
||||
'send': function () {
|
||||
@ -287,12 +288,6 @@
|
||||
input_el.value = 'marty';
|
||||
input_el.dispatchEvent(new Event('input'));
|
||||
await u.waitUntil(() => modal.el.querySelector('.suggestion-box li'), 1000);
|
||||
const sendIQ = _converse.connection.sendIQ;
|
||||
let sent_stanza, IQ_id;
|
||||
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
|
||||
sent_stanza = iq;
|
||||
IQ_id = sendIQ.bind(this)(iq, callback, errback);
|
||||
});
|
||||
expect(modal.el.querySelectorAll('.suggestion-box li').length).toBe(1);
|
||||
const suggestion = modal.el.querySelector('.suggestion-box li');
|
||||
expect(suggestion.textContent).toBe('Marty McFly');
|
||||
@ -303,8 +298,11 @@
|
||||
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();
|
||||
expect(sent_stanza.toLocaleString()).toEqual(
|
||||
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
|
||||
|
||||
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;
|
||||
@ -355,13 +353,6 @@
|
||||
expect(modal.jid_auto_complete).toBe(undefined);
|
||||
expect(modal.name_auto_complete).toBe(undefined);
|
||||
|
||||
const sendIQ = _converse.connection.sendIQ;
|
||||
let sent_stanza, IQ_id;
|
||||
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
|
||||
sent_stanza = iq;
|
||||
IQ_id = sendIQ.bind(this)(iq, callback, errback);
|
||||
});
|
||||
|
||||
const input_el = modal.el.querySelector('input[name="name"]');
|
||||
input_el.value = 'ambiguous';
|
||||
modal.el.querySelector('button[type="submit"]').click();
|
||||
@ -382,8 +373,11 @@
|
||||
|
||||
input_el.value = 'Marty McFly';
|
||||
modal.el.querySelector('button[type="submit"]').click();
|
||||
expect(sent_stanza.toLocaleString()).toEqual(
|
||||
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
|
||||
|
||||
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;
|
||||
|
@ -1506,7 +1506,7 @@
|
||||
const older_msgs = modal.el.querySelectorAll('.older-msg');
|
||||
expect(older_msgs.length).toBe(2);
|
||||
expect(older_msgs[0].childNodes[0].nodeName).toBe('TIME');
|
||||
expect(older_msgs[0].childNodes[1].textContent).toBe(': But soft, what light through yonder airlock breaks?');
|
||||
expect(older_msgs[0].childNodes[2].textContent).toBe('But soft, what light through yonder airlock breaks?');
|
||||
expect(view.model.messages.models.length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
@ -430,6 +430,8 @@
|
||||
'type': 'groupchat',
|
||||
'id': msg_id,
|
||||
}).c('body').t('But soft, what light through yonder airlock breaks?').tree());
|
||||
|
||||
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length);
|
||||
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(view.el.querySelector('.chat-msg__text').textContent)
|
||||
.toBe('But soft, what light through yonder airlock breaks?');
|
||||
@ -463,10 +465,10 @@
|
||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||
const older_msgs = modal.el.querySelectorAll('.older-msg');
|
||||
expect(older_msgs.length).toBe(2);
|
||||
expect(older_msgs[0].childNodes[1].textContent).toBe(': But soft, what light through yonder airlock breaks?');
|
||||
expect(older_msgs[0].childNodes[2].textContent).toBe('But soft, what light through yonder airlock breaks?');
|
||||
expect(older_msgs[0].childNodes[0].nodeName).toBe('TIME');
|
||||
expect(older_msgs[1].childNodes[0].nodeName).toBe('TIME');
|
||||
expect(older_msgs[1].childNodes[1].textContent).toBe(': But soft, what light through yonder chimney breaks?');
|
||||
expect(older_msgs[1].childNodes[2].textContent).toBe('But soft, what light through yonder chimney breaks?');
|
||||
done();
|
||||
}));
|
||||
|
||||
|
@ -28,9 +28,7 @@
|
||||
const modal = view.user_details_modal;
|
||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||
spyOn(window, 'confirm').and.returnValue(true);
|
||||
spyOn(view.model.contact, 'removeFromRoster').and.callFake(function (callback) {
|
||||
callback();
|
||||
});
|
||||
spyOn(view.model.contact, 'removeFromRoster').and.callFake(callback => callback());
|
||||
let remove_contact_button = modal.el.querySelector('button.remove-contact');
|
||||
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
||||
remove_contact_button.click();
|
||||
|
@ -8,16 +8,17 @@
|
||||
* @module converse-chatboxviews
|
||||
*/
|
||||
import "@converse/headless/converse-chatboxes";
|
||||
import "backbone.nativeview";
|
||||
import { HTMLView } from 'skeletor.js/src/htmlview.js';
|
||||
import { Overview } from "skeletor.js/src/overview";
|
||||
import { View } from "skeletor.js/src/view";
|
||||
import { result } from "lodash";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import tpl_avatar from "templates/avatar.svg";
|
||||
import tpl_background_logo from "templates/background_logo.html";
|
||||
import tpl_chatboxes from "templates/chatboxes.html";
|
||||
|
||||
const { Backbone, _, utils } = converse.env;
|
||||
const u = utils;
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
const AvatarMixin = {
|
||||
|
||||
@ -65,7 +66,7 @@ converse.plugins.add('converse-chatboxviews', {
|
||||
});
|
||||
|
||||
_converse.ViewWithAvatar = View.extend(AvatarMixin);
|
||||
_converse.VDOMViewWithAvatar = Backbone.VDOMView.extend(AvatarMixin);
|
||||
_converse.HTMLViewWithAvatar = HTMLView.extend(AvatarMixin);
|
||||
|
||||
|
||||
_converse.ChatBoxViews = Overview.extend({
|
||||
@ -91,7 +92,7 @@ converse.plugins.add('converse-chatboxviews', {
|
||||
el.innerHTML = '';
|
||||
this.setElement(el, false);
|
||||
} else {
|
||||
this.setElement(_.result(this, 'el'), false);
|
||||
this.setElement(result(this, 'el'), false);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -12,7 +12,6 @@ import "converse-message-view";
|
||||
import "converse-modal";
|
||||
import { debounce, get, isString } from "lodash";
|
||||
import { Overview } from "skeletor.js/src/overview";
|
||||
import { View } from "skeletor.js/src/view";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import log from "@converse/headless/log";
|
||||
import tpl_chatbox from "templates/chatbox.html";
|
||||
@ -27,7 +26,7 @@ import tpl_spoiler_button from "templates/spoiler_button.html";
|
||||
import tpl_status_message from "templates/status_message.html";
|
||||
import tpl_toolbar from "templates/toolbar.html";
|
||||
import tpl_toolbar_fileupload from "templates/toolbar_fileupload.html";
|
||||
import tpl_user_details_modal from "templates/user_details_modal.html";
|
||||
import tpl_user_details_modal from "templates/user_details_modal.js";
|
||||
import xss from "xss/dist/xss";
|
||||
|
||||
|
||||
@ -130,6 +129,7 @@ converse.plugins.add('converse-chatview', {
|
||||
|
||||
|
||||
_converse.UserDetailsModal = _converse.BootstrapModal.extend({
|
||||
id: "user-details-modal",
|
||||
|
||||
events: {
|
||||
'click button.remove-contact': 'removeContact',
|
||||
@ -157,7 +157,6 @@ converse.plugins.add('converse-chatview', {
|
||||
return tpl_user_details_modal(Object.assign(
|
||||
this.model.toJSON(),
|
||||
vcard_json, {
|
||||
'__': __,
|
||||
'view': this,
|
||||
'_converse': _converse,
|
||||
'allow_contact_removal': _converse.allow_contact_removal,
|
||||
@ -616,7 +615,7 @@ converse.plugins.add('converse-chatview', {
|
||||
* content area of the chat box.
|
||||
* @private
|
||||
* @method _converse.ChatBoxView#insertMessage
|
||||
* @param { Backbone.View } message - The message Backbone.View
|
||||
* @param { View } message - The message View
|
||||
*/
|
||||
insertMessage (view) {
|
||||
if (view.model.get('type') === 'error') {
|
||||
@ -1386,8 +1385,7 @@ converse.plugins.add('converse-chatview', {
|
||||
* Get the view of an already open chat.
|
||||
* @method _converse.api.chatviews.get
|
||||
* @param { Array.string | string } jids
|
||||
* @returns {ChatBoxView} A [Backbone.View](http://backbonejs.org/#View) instance.
|
||||
* The chat should already be open, otherwise `undefined` will be returned.
|
||||
* @returns { _converse.ChatBoxView|undefined } The chat should already be open, otherwise `undefined` will be returned.
|
||||
* @example
|
||||
* // To return a single view, provide the JID of the contact:
|
||||
* _converse.api.chatviews.get('buddy@example.com')
|
||||
|
@ -17,7 +17,7 @@ import tpl_csn from "templates/csn.html";
|
||||
import tpl_file_progress from "templates/file_progress.html";
|
||||
import tpl_info from "templates/info.html";
|
||||
import tpl_message from "templates/message.html";
|
||||
import tpl_message_versions_modal from "templates/message_versions_modal.html";
|
||||
import tpl_message_versions_modal from "templates/message_versions_modal.js";
|
||||
import tpl_spinner from "templates/spinner.html";
|
||||
import xss from "xss/dist/xss";
|
||||
|
||||
@ -73,12 +73,9 @@ converse.plugins.add('converse-message-view', {
|
||||
});
|
||||
|
||||
_converse.MessageVersionsModal = _converse.BootstrapModal.extend({
|
||||
id: "message-versions-modal",
|
||||
toHTML () {
|
||||
return tpl_message_versions_modal(Object.assign(
|
||||
this.model.toJSON(), {
|
||||
'__': __,
|
||||
'dayjs': dayjs
|
||||
}));
|
||||
return tpl_message_versions_modal(this.model.toJSON());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -16,7 +16,7 @@ import tpl_chats_panel from "templates/chats_panel.html";
|
||||
import tpl_toggle_chats from "templates/toggle_chats.html";
|
||||
import tpl_trimmed_chat from "templates/trimmed_chat.html";
|
||||
|
||||
const { _ , Backbone, dayjs } = converse.env;
|
||||
const { _ , dayjs } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
|
@ -1,21 +1,20 @@
|
||||
// Converse.js
|
||||
// https://conversejs.org
|
||||
//
|
||||
// Copyright (c) 2013-2019, the Converse.js developers
|
||||
// Licensed under the Mozilla Public License (MPLv2)
|
||||
/**
|
||||
* @module converse-modal
|
||||
* @copyright The Converse.js developers
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import "backbone.vdomview";
|
||||
import bootstrap from "bootstrap.native";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import { HTMLView } from 'skeletor.js/src/htmlview.js';
|
||||
import { Model } from 'skeletor.js/src/model.js';
|
||||
import { isString } from "lodash";
|
||||
import tpl_alert from "templates/alert.html";
|
||||
import tpl_alert_modal from "templates/alert_modal.html";
|
||||
import tpl_prompt from "templates/prompt.html";
|
||||
import { render } from 'lit-html';
|
||||
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 { Backbone, sizzle } = converse.env;
|
||||
const { sizzle } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
@ -25,15 +24,24 @@ converse.plugins.add('converse-modal', {
|
||||
const { _converse } = this;
|
||||
const { __ } = _converse;
|
||||
|
||||
_converse.BootstrapModal = Backbone.VDOMView.extend({
|
||||
|
||||
_converse.BootstrapModal = HTMLView.extend({
|
||||
className: "modal",
|
||||
events: {
|
||||
'click .nav-item .nav-link': 'switchTab'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.render().insertIntoDOM();
|
||||
this.modal = new bootstrap.Modal(this.el, {
|
||||
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: 'static',
|
||||
keyboard: true
|
||||
});
|
||||
@ -57,14 +65,14 @@ converse.plugins.add('converse-modal', {
|
||||
},
|
||||
|
||||
alert (message, type='primary') {
|
||||
const body = this.el.querySelector('.modal-body');
|
||||
body.insertAdjacentHTML(
|
||||
'afterBegin',
|
||||
tpl_alert({
|
||||
'type': `alert-${type}`,
|
||||
'message': message
|
||||
})
|
||||
);
|
||||
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);
|
||||
@ -95,7 +103,7 @@ converse.plugins.add('converse-modal', {
|
||||
},
|
||||
|
||||
toHTML () {
|
||||
return tpl_prompt(Object.assign({__}, this.model.toJSON()));
|
||||
return tpl_prompt(this.model.toJSON());
|
||||
},
|
||||
|
||||
afterRender () {
|
||||
@ -119,7 +127,7 @@ converse.plugins.add('converse-modal', {
|
||||
|
||||
_converse.Prompt = _converse.Confirm.extend({
|
||||
toHTML () {
|
||||
return tpl_prompt(Object.assign({__}, this.model.toJSON()));
|
||||
return tpl_prompt(this.model.toJSON());
|
||||
},
|
||||
|
||||
onConfimation (ev) {
|
||||
@ -138,8 +146,7 @@ converse.plugins.add('converse-modal', {
|
||||
},
|
||||
|
||||
toHTML () {
|
||||
return tpl_alert_modal(
|
||||
Object.assign({__}, this.model.toJSON()));
|
||||
return tpl_alert_modal(Object.assign({__}, this.model.toJSON()));
|
||||
}
|
||||
});
|
||||
|
||||
@ -158,7 +165,6 @@ converse.plugins.add('converse-modal', {
|
||||
let alert, prompt, confirm;
|
||||
|
||||
Object.assign(_converse.api, {
|
||||
|
||||
/**
|
||||
* Show a confirm modal to the user.
|
||||
* @method _converse.api.confirm
|
||||
|
@ -14,12 +14,12 @@ import { OrderedListView } from "skeletor.js/src/overview";
|
||||
import { View } from "skeletor.js/src/view";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import log from "@converse/headless/log";
|
||||
import tpl_add_chatroom_modal from "templates/add_chatroom_modal.html";
|
||||
import tpl_add_chatroom_modal from "templates/add_chatroom_modal.js";
|
||||
import tpl_chatarea from "templates/chatarea.html";
|
||||
import tpl_chatroom from "templates/chatroom.html";
|
||||
import tpl_chatroom_bottom_panel from "templates/chatroom_bottom_panel.html";
|
||||
import tpl_chatroom_destroyed from "templates/chatroom_destroyed.html";
|
||||
import tpl_chatroom_details_modal from "templates/chatroom_details_modal.html";
|
||||
import tpl_chatroom_details_modal from "templates/chatroom_details_modal.js";
|
||||
import tpl_chatroom_disconnect from "templates/chatroom_disconnect.html";
|
||||
import tpl_chatroom_features from "templates/chatroom_features.html";
|
||||
import tpl_chatroom_form from "templates/chatroom_form.html";
|
||||
@ -29,8 +29,8 @@ import tpl_chatroom_nickname_form from "templates/chatroom_nickname_form.html";
|
||||
import tpl_chatroom_password_form from "templates/chatroom_password_form.html";
|
||||
import tpl_chatroom_sidebar from "templates/chatroom_sidebar.html";
|
||||
import tpl_info from "templates/info.html";
|
||||
import tpl_list_chatrooms_modal from "templates/list_chatrooms_modal.html";
|
||||
import tpl_moderator_tools_modal from "templates/moderator_tools_modal.html";
|
||||
import tpl_list_chatrooms_modal from "templates/list_chatrooms_modal.js";
|
||||
import tpl_moderator_tools_modal from "templates/moderator_tools_modal.js";
|
||||
import tpl_occupant from "templates/occupant.html";
|
||||
import tpl_room_description from "templates/room_description.html";
|
||||
import tpl_room_item from "templates/room_item.html";
|
||||
@ -226,7 +226,7 @@ converse.plugins.add('converse-muc-views', {
|
||||
|
||||
|
||||
_converse.ModeratorToolsModal = _converse.BootstrapModal.extend({
|
||||
|
||||
id: "converse-modtools-modal",
|
||||
events: {
|
||||
'submit .affiliation-form': 'assignAffiliation',
|
||||
'submit .role-form': 'assignRole',
|
||||
@ -271,7 +271,6 @@ converse.plugins.add('converse-muc-views', {
|
||||
allowed_roles.sort();
|
||||
|
||||
return tpl_moderator_tools_modal(Object.assign(this.model.toJSON(), {
|
||||
__,
|
||||
allowed_affiliations,
|
||||
allowed_roles,
|
||||
'affiliations': [...AFFILIATIONS, 'none'],
|
||||
@ -389,6 +388,7 @@ converse.plugins.add('converse-muc-views', {
|
||||
|
||||
|
||||
_converse.ListChatRoomsModal = _converse.BootstrapModal.extend({
|
||||
id: "list-chatrooms-modal",
|
||||
|
||||
events: {
|
||||
'submit form': 'showRooms',
|
||||
@ -409,9 +409,6 @@ converse.plugins.add('converse-muc-views', {
|
||||
toHTML () {
|
||||
const muc_domain = this.model.get('muc_domain') || _converse.muc_domain;
|
||||
return tpl_list_chatrooms_modal(Object.assign(this.model.toJSON(), {
|
||||
'heading_list_chatrooms': __('Query for Groupchats'),
|
||||
'label_server_address': __('Server address'),
|
||||
'label_query': __('Show groupchats'),
|
||||
'show_form': !_converse.locked_muc_domain,
|
||||
'server_placeholder': muc_domain ? muc_domain : __('conference.example.org')
|
||||
}));
|
||||
@ -523,6 +520,7 @@ converse.plugins.add('converse-muc-views', {
|
||||
|
||||
|
||||
_converse.AddChatRoomModal = _converse.BootstrapModal.extend({
|
||||
id: 'add-chatroom-modal',
|
||||
|
||||
events: {
|
||||
'submit form.add-chatroom': 'openChatRoom',
|
||||
@ -543,7 +541,6 @@ converse.plugins.add('converse-muc-views', {
|
||||
placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org');
|
||||
}
|
||||
return tpl_add_chatroom_modal(Object.assign(this.model.toJSON(), {
|
||||
'__': _converse.__,
|
||||
'_converse': _converse,
|
||||
'label_room_address': _converse.muc_domain ? __('Groupchat name') : __('Groupchat address'),
|
||||
'chatroom_placeholder': placeholder,
|
||||
@ -616,6 +613,7 @@ converse.plugins.add('converse-muc-views', {
|
||||
|
||||
|
||||
_converse.RoomDetailsModal = _converse.BootstrapModal.extend({
|
||||
id: "room-details-modal",
|
||||
|
||||
initialize () {
|
||||
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||
@ -627,12 +625,10 @@ converse.plugins.add('converse-muc-views', {
|
||||
toHTML () {
|
||||
return tpl_chatroom_details_modal(Object.assign(
|
||||
this.model.toJSON(), {
|
||||
'__': __,
|
||||
'config': this.model.config.toJSON(),
|
||||
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
|
||||
'features': this.model.features.toJSON(),
|
||||
'num_occupants': this.model.occupants.length,
|
||||
'topic': u.addHyperlinks(xss.filterXSS(get(this.model.get('subject'), 'text'), {'whiteList': {}}))
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
// Converse.js (A browser based XMPP chat client)
|
||||
// https://conversejs.org
|
||||
//
|
||||
// Copyright (c) 2013-2017, Jan-Carel Brand <jc@opkode.com>
|
||||
// Licensed under the Mozilla Public License (MPLv2)
|
||||
//
|
||||
/**
|
||||
* @module converse-profile
|
||||
* @copyright The Converse.js developers
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import "@converse/headless/converse-status";
|
||||
import "@converse/headless/converse-vcard";
|
||||
@ -14,12 +10,12 @@ import "formdata-polyfill";
|
||||
import bootstrap from "bootstrap.native";
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import log from "@converse/headless/log";
|
||||
import tpl_chat_status_modal from "templates/chat_status_modal.html";
|
||||
import tpl_client_info_modal from "templates/client_info_modal.html";
|
||||
import tpl_profile_modal from "templates/profile_modal.html";
|
||||
import tpl_profile_view from "templates/profile_view.html";
|
||||
import sizzle from 'sizzle';
|
||||
import tpl_chat_status_modal from "templates/chat_status_modal";
|
||||
import tpl_client_info_modal from "templates/client_info_modal";
|
||||
import tpl_profile from "templates/profile.js";
|
||||
import tpl_profile_modal from "templates/profile_modal";
|
||||
|
||||
const { sizzle } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
@ -40,6 +36,7 @@ converse.plugins.add('converse-profile', {
|
||||
|
||||
|
||||
_converse.ProfileModal = _converse.BootstrapModal.extend({
|
||||
id: "user-profile-modal",
|
||||
events: {
|
||||
'change input[type="file"': "updateFilePreview",
|
||||
'click .change-avatar': "openFileSelection",
|
||||
@ -62,20 +59,7 @@ converse.plugins.add('converse-profile', {
|
||||
return tpl_profile_modal(Object.assign(
|
||||
this.model.toJSON(),
|
||||
this.model.vcard.toJSON(), {
|
||||
'__': __,
|
||||
'_converse': _converse,
|
||||
'alt_avatar': __('Your avatar image'),
|
||||
'heading_profile': __('Your Profile'),
|
||||
'label_close': __('Close'),
|
||||
'label_email': __('Email'),
|
||||
'label_fullname': __('Full Name'),
|
||||
'label_jid': __('XMPP Address (JID)'),
|
||||
'label_nickname': __('Nickname'),
|
||||
'label_role': __('Role'),
|
||||
'label_role_help': __(
|
||||
'Use commas to separate multiple roles. '+
|
||||
'Your roles are shown next to your name on your chat messages.'),
|
||||
'label_url': __('URL'),
|
||||
'utils': u,
|
||||
'view': this
|
||||
}));
|
||||
@ -146,6 +130,7 @@ converse.plugins.add('converse-profile', {
|
||||
|
||||
|
||||
_converse.ChatStatusModal = _converse.BootstrapModal.extend({
|
||||
id: "modal-status-change",
|
||||
events: {
|
||||
"submit form#set-xmpp-status": "onFormSubmitted",
|
||||
"click .clear-input": "clearStatusMessage"
|
||||
@ -157,9 +142,9 @@ converse.plugins.add('converse-profile', {
|
||||
this.model.toJSON(),
|
||||
this.model.vcard.toJSON(), {
|
||||
'label_away': __('Away'),
|
||||
'label_close': __('Close'),
|
||||
'label_busy': __('Busy'),
|
||||
'label_cancel': __('Cancel'),
|
||||
'label_close': __('Close'),
|
||||
'label_custom_status': __('Custom status'),
|
||||
'label_offline': __('Offline'),
|
||||
'label_online': __('Online'),
|
||||
@ -197,31 +182,20 @@ converse.plugins.add('converse-profile', {
|
||||
});
|
||||
|
||||
_converse.ClientInfoModal = _converse.BootstrapModal.extend({
|
||||
id: "converse-client-info-modal",
|
||||
|
||||
toHTML () {
|
||||
return tpl_client_info_modal(
|
||||
Object.assign(
|
||||
this.model.toJSON(),
|
||||
this.model.vcard.toJSON(), {
|
||||
'__': __,
|
||||
'modal_title': __('About'),
|
||||
'version_name': _converse.VERSION_NAME,
|
||||
'first_subtitle': __( '%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>',
|
||||
'<a target="_blank" rel="nofollow" href="https://opkode.com">'
|
||||
),
|
||||
'second_subtitle': __('%1$s Translate %2$s it into your own language',
|
||||
'<a target="_blank" rel="nofollow" href="https://hosted.weblate.org/projects/conversejs/#languages">',
|
||||
'</a>'
|
||||
)
|
||||
}
|
||||
this.model.vcard.toJSON(),
|
||||
{ 'version_name': _converse.VERSION_NAME }
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
_converse.XMPPStatusView = _converse.VDOMViewWithAvatar.extend({
|
||||
_converse.XMPPStatusView = _converse.HTMLViewWithAvatar.extend({
|
||||
tagName: "div",
|
||||
events: {
|
||||
"click a.show-profile": "showProfileModal",
|
||||
@ -237,20 +211,14 @@ converse.plugins.add('converse-profile', {
|
||||
|
||||
toHTML () {
|
||||
const chat_status = this.model.get('status') || 'offline';
|
||||
return tpl_profile_view(Object.assign(
|
||||
return tpl_profile(Object.assign(
|
||||
this.model.toJSON(),
|
||||
this.model.vcard.toJSON(), {
|
||||
'__': __,
|
||||
_converse,
|
||||
chat_status,
|
||||
'fullname': this.model.vcard.get('fullname') || _converse.bare_jid,
|
||||
'status_message': this.model.get('status_message') ||
|
||||
__("I am %1$s", this.getPrettyStatus(chat_status)),
|
||||
'chat_status': chat_status,
|
||||
'_converse': _converse,
|
||||
'title_change_settings': __('Change settings'),
|
||||
'title_change_status': __('Click to change your chat status'),
|
||||
'title_log_out': __('Log out'),
|
||||
'info_details': __('Show details about this chat client'),
|
||||
'title_your_profile': __('Your profile')
|
||||
__("I am %1$s", this.getPrettyStatus(chat_status))
|
||||
}));
|
||||
},
|
||||
|
||||
@ -306,7 +274,6 @@ converse.plugins.add('converse-profile', {
|
||||
|
||||
|
||||
/******************** Event Handlers ********************/
|
||||
|
||||
_converse.api.listen.on('controlBoxPaneInitialized', async view => {
|
||||
await _converse.api.waitUntil('VCardsInitialized');
|
||||
_converse.xmppstatusview = new _converse.XMPPStatusView({'model': _converse.xmppstatus});
|
||||
@ -314,4 +281,3 @@ converse.plugins.add('converse-profile', {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
// Converse.js
|
||||
// https://conversejs.org
|
||||
//
|
||||
// Copyright (c) 2013-2019, the Converse.js developers
|
||||
// Licensed under the Mozilla Public License (MPLv2)
|
||||
/**
|
||||
* @module converse-rosterview
|
||||
* @copyright 2013-2019, the Converse.js developers
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import "@converse/headless/converse-chatboxes";
|
||||
import "@converse/headless/converse-roster";
|
||||
@ -16,7 +13,7 @@ import { OrderedListView } from "skeletor.js/src/overview";
|
||||
import SHA1 from 'strophe.js/src/sha1';
|
||||
import converse from "@converse/headless/converse-core";
|
||||
import log from "@converse/headless/log";
|
||||
import tpl_add_contact_modal from "templates/add_contact_modal.html";
|
||||
import tpl_add_contact_modal from "templates/add_contact_modal.js";
|
||||
import tpl_group_header from "templates/group_header.html";
|
||||
import tpl_pending_contact from "templates/pending_contact.html";
|
||||
import tpl_requesting_contact from "templates/requesting_contact.html";
|
||||
@ -61,6 +58,7 @@ converse.plugins.add('converse-rosterview', {
|
||||
|
||||
|
||||
_converse.AddContactModal = _converse.BootstrapModal.extend({
|
||||
id: "add-contact-modal",
|
||||
events: {
|
||||
'submit form': 'addContactFromForm'
|
||||
},
|
||||
@ -74,12 +72,7 @@ converse.plugins.add('converse-rosterview', {
|
||||
const label_nickname = _converse.xhr_user_search_url ? __('Contact name') : __('Optional nickname');
|
||||
return tpl_add_contact_modal(Object.assign(this.model.toJSON(), {
|
||||
'_converse': _converse,
|
||||
'heading_new_contact': __('Add a Contact'),
|
||||
'label_xmpp_address': __('XMPP Address'),
|
||||
'label_nickname': label_nickname,
|
||||
'contact_placeholder': __('name@example.org'),
|
||||
'label_add': __('Add'),
|
||||
'error_message': __('Please enter a valid XMPP address')
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -3,8 +3,10 @@
|
||||
* @copyright The Converse.js developers
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import { __, i18n } from './i18n';
|
||||
import { assignIn, debounce, get, invoke, isFunction, isObject, isString, pick } from 'lodash';
|
||||
import { Collection } from "skeletor.js/src/collection";
|
||||
import { Events } from 'skeletor.js/src/events.js';
|
||||
import { Model } from 'skeletor.js/src/model.js';
|
||||
import { Router } from 'skeletor.js/src/router.js';
|
||||
import 'strophe.js/src/websocket';
|
||||
@ -15,7 +17,6 @@ import Backbone from 'backbone';
|
||||
import Storage from 'skeletor.js/src/storage.js';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import dayjs from 'dayjs';
|
||||
import i18n from './i18n';
|
||||
import log from '@converse/headless/log';
|
||||
import pluggable from 'pluggable.js/src/pluggable';
|
||||
import sizzle from 'sizzle';
|
||||
@ -114,7 +115,7 @@ const _converse = {
|
||||
|
||||
_converse.VERSION_NAME = "v6.0.1dev";
|
||||
|
||||
Object.assign(_converse, Backbone.Events);
|
||||
Object.assign(_converse, Events);
|
||||
|
||||
_converse.router = new Router();
|
||||
|
||||
@ -258,12 +259,7 @@ _converse.default_settings = {
|
||||
* @memberOf _converse
|
||||
* @param { String } str - The string to translate
|
||||
*/
|
||||
_converse.__ = function (str) {
|
||||
if (i18n === undefined) {
|
||||
return str;
|
||||
}
|
||||
return i18n.translate.apply(i18n, arguments);
|
||||
};
|
||||
_converse.__ = __;
|
||||
|
||||
|
||||
/**
|
||||
@ -287,8 +283,6 @@ _converse.___ = function (str) {
|
||||
}
|
||||
|
||||
|
||||
const __ = _converse.__;
|
||||
|
||||
const PROMISES = [
|
||||
'afterResourceBinding',
|
||||
'connectionInitialized',
|
||||
@ -1019,7 +1013,7 @@ _converse.initialize = async function (settings, callback) {
|
||||
);
|
||||
|
||||
/* Localisation */
|
||||
if (i18n === undefined || _converse.isTestEnv()) {
|
||||
if (_converse.isTestEnv()) {
|
||||
_converse.locale = 'en';
|
||||
} else {
|
||||
try {
|
||||
@ -1027,6 +1021,7 @@ _converse.initialize = async function (settings, callback) {
|
||||
await i18n.fetchTranslations(_converse);
|
||||
} catch (e) {
|
||||
log.fatal(e.message);
|
||||
_converse.locale = 'en';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ let jed_instance;
|
||||
/**
|
||||
* @namespace i18n
|
||||
*/
|
||||
export default {
|
||||
export const i18n = {
|
||||
|
||||
getLocale (preferred_locale, available_locales) {
|
||||
return getLocale(preferred_locale, preferred => isConverseLocale(preferred, available_locales));
|
||||
@ -105,3 +105,8 @@ export default {
|
||||
jed_instance = new Jed(data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const __ = function () {
|
||||
return i18n.translate.apply(i18n, arguments);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
},
|
||||
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
|
||||
"devDependencies": {
|
||||
"skeletor.js": "skeletorjs/skeletor#abc4e9d25d30159e9cffc14bf5f7ffe17b3665eb",
|
||||
"skeletor.js": "skeletorjs/skeletor#29a6d8f707076e865133b8f36f07c76ba4b4b582",
|
||||
"backbone": "1.4",
|
||||
"backbone.browserStorage": "conversejs/backbone.browserStorage#674ba3aa0e4d0f0b0dcac48fcc7dea531012828f",
|
||||
"filesize": "^4.1.2",
|
||||
|
@ -1,6 +1,6 @@
|
||||
function CustomEvent ( event, params ) {
|
||||
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
||||
var evt = document.createEvent( 'CustomEvent' );
|
||||
const evt = document.createEvent( 'CustomEvent' );
|
||||
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
||||
return evt;
|
||||
}
|
||||
@ -25,12 +25,12 @@ if (!String.prototype.includes) {
|
||||
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function (searchString, position) {
|
||||
var subjectString = this.toString();
|
||||
const subjectString = this.toString();
|
||||
if (position === undefined || position > subjectString.length) {
|
||||
position = subjectString.length;
|
||||
}
|
||||
position -= searchString.length;
|
||||
var lastIndex = subjectString.indexOf(searchString, position);
|
||||
const lastIndex = subjectString.indexOf(searchString, position);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
||||
@ -44,7 +44,7 @@ if (!String.prototype.startsWith) {
|
||||
|
||||
if (!String.prototype.splitOnce) {
|
||||
String.prototype.splitOnce = function (delimiter) {
|
||||
var components = this.split(delimiter);
|
||||
const components = this.split(delimiter);
|
||||
return [components.shift(), components.join(delimiter)];
|
||||
};
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
<div class="modal" id="add-chatroom-modal" tabindex="-1" role="dialog" aria-labelledby="add-chatroom-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"
|
||||
id="add-chatroom-modal-label">{{{o.__('Enter a new Groupchat')}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="converse-form add-chatroom">
|
||||
<div class="form-group">
|
||||
<label for="chatroom">{{{o.label_room_address}}}:</label>
|
||||
{[ if (o.muc_roomid_policy_error_msg) { ]}
|
||||
<label class="roomid-policy-error">{{{o.muc_roomid_policy_error_msg}}}</label>
|
||||
{[ } ]}
|
||||
<input type="text" required="required" name="chatroom" class="form-control roomjid-input" placeholder="{{{o.chatroom_placeholder}}}"/>
|
||||
</div>
|
||||
{[ if (o.muc_roomid_policy_hint) { ]}
|
||||
<div class="form-group">
|
||||
{{o.muc_roomid_policy_hint}}
|
||||
</div>
|
||||
{[ } ]}
|
||||
{[ if (!o._converse.locked_muc_nickname) { ]}
|
||||
<div class="form-group" >
|
||||
<label for="nickname">{{{o.__('Nickname')}}}:</label>
|
||||
<input type="text" pattern=".*\S+.*" title="{{{o.__('This field is required')}}}" required="required" name="nickname" value="{{{o.nick}}}" class="form-control"/>
|
||||
</div>
|
||||
{[ } ]}
|
||||
<input type="submit" class="btn btn-primary" name="join" value="{{{o.__('Join')}}}" {[ if (o.muc_roomid_policy_error_msg) { ]} disabled=true {[ } ]}/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
42
src/templates/add_chatroom_modal.js
Normal file
42
src/templates/add_chatroom_modal.js
Normal file
@ -0,0 +1,42 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { modal_header_close_button } from "./buttons"
|
||||
|
||||
|
||||
const i18n_join = __('Join');
|
||||
const i18n_enter = __('Enter a new Groupchat');
|
||||
const i18n_nickname = __('Nickname');
|
||||
const i18n_required_field = __('This field is required');
|
||||
|
||||
|
||||
const nickname_input = (o) => 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) => 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>` : '' }
|
||||
<input type="text" required="required" name="chatroom" class="form-control roomjid-input" placeholder="${o.chatroom_placeholder}"/>
|
||||
</div>
|
||||
${ o.muc_roomid_policy_hint ? html`<div class="form-group">{{o.muc_roomid_policy_hint}}</div>` : '' }
|
||||
${ !o._converse.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>
|
||||
`;
|
@ -1,41 +1,52 @@
|
||||
<!-- Add contact Modal -->
|
||||
<div class="modal" id="add-contact-modal" tabindex="-1" role="dialog" aria-labelledby="addContactModalLabel" aria-hidden="true">
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { modal_header_close_button } from "./buttons"
|
||||
|
||||
const i18n_contact_placeholder = __('name@example.org');
|
||||
const i18n_add = __('Add');
|
||||
const i18n_error_message = __('Please enter a valid XMPP address');
|
||||
const i18n_new_contact = __('Add a Contact');
|
||||
const i18n_xmpp_address = __('XMPP Address');
|
||||
const i18n_nickname = __('Nickname');
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addContactModalLabel">{{{o.heading_new_contact}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h5 class="modal-title" id="addContactModalLabel">${i18n_new_contact}</h5>
|
||||
${modal_header_close_button}
|
||||
</div>
|
||||
<form class="converse-form add-xmpp-contact">
|
||||
<div class="modal-body">
|
||||
<span class="modal-alert"></span>
|
||||
<div class="form-group add-xmpp-contact__jid">
|
||||
<label class="clearfix" for="jid">{{{o.label_xmpp_address}}}:</label>
|
||||
<label class="clearfix" for="jid">${i18n_xmpp_address}:</label>
|
||||
<div class="suggestion-box suggestion-box__jid">
|
||||
<ul class="suggestion-box__results suggestion-box__results--above" hidden=""></ul>
|
||||
<input type="text" name="jid"
|
||||
{[ if (!o._converse.xhr_user_search_url) { ]} required="required" {[ } ]}
|
||||
value="{{{o.jid}}}"
|
||||
class="form-control suggestion-box__input"
|
||||
placeholder="{{{o.contact_placeholder}}}"/>
|
||||
<input type="text" name="jid" ?required=${(!o._converse.xhr_user_search_url)}
|
||||
value="${o.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">{{{o.label_nickname}}}:</label>
|
||||
<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="{{{o.nickname}}}"
|
||||
class="form-control suggestion-box__input"
|
||||
placeholder="{{{o.nickname_placeholder}}}"/>
|
||||
<input type="text" name="name" value="${o.nickname || ''}"
|
||||
class="form-control suggestion-box__input"
|
||||
placeholder="${i18n_nickname}"/>
|
||||
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="invalid-feedback">{{{o.error_message}}}</div>
|
||||
<div class="invalid-feedback">${i18n_error_message}</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">{{{o.label_add}}}</button>
|
||||
<button type="submit" class="btn btn-primary">${i18n_add}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1 +0,0 @@
|
||||
<div class="alert {{{o.type}}}" role="alert"><p>{{{o.message}}}</p></div>
|
3
src/templates/alert.js
Normal file
3
src/templates/alert.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`<div class="alert ${o.type}" role="alert"><p>${o.message}</p></div>`
|
@ -1,16 +0,0 @@
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header {{{o.level}}}">
|
||||
<h5 class="modal-title">{{{o.title}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">{[o.messages.forEach(function (message) { ]}
|
||||
<p>{{{message}}}</p>
|
||||
{[ }) ]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
18
src/templates/alert_modal.js
Normal file
18
src/templates/alert_modal.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { html } from "lit-html";
|
||||
import { modal_header_close_button } from "./buttons"
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header ${o.level}">
|
||||
<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>
|
||||
`;
|
5
src/templates/avatar.js
Normal file
5
src/templates/avatar.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`
|
||||
<img alt="${o.alt_text}" class="img-thumbnail avatar align-self-center ${o.extra_classes}"
|
||||
height="100px" width="100px" src="data:${o.image_type};base64,${o.image}"/>`;
|
9
src/templates/buttons.js
Normal file
9
src/templates/buttons.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { html } from "lit-html";
|
||||
|
||||
const i18n_close = __('Close');
|
||||
|
||||
export const modal_close_button = html`<button type="button" class="btn btn-secondary" data-dismiss="modal">${i18n_close}</button>`;
|
||||
|
||||
export const modal_header_close_button = html`<button type="button" class="close" data-dismiss="modal" aria-label="${i18n_close}"><span aria-hidden="true">×</span></button>`;
|
||||
|
@ -1,51 +1,53 @@
|
||||
<!-- Change status Modal -->
|
||||
<div class="modal" id="modal-status-change" tabindex="-1" role="dialog" aria-labelledby="changeStatusModalLabel" aria-hidden="true">
|
||||
import { html } from "lit-html";
|
||||
import { modal_header_close_button } from "./buttons"
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="changeStatusModalLabel">{{{o.modal_title}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h5 class="modal-title" id="changeStatusModalLabel">${o.modal_title}</h5>
|
||||
${modal_header_close_button}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="modal-alert"></span>
|
||||
<form class="converse-form set-xmpp-status" id="set-xmpp-status">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-radio">
|
||||
<input {[ if (o.status === 'online') { ]} checked="checked" {[ } ]}
|
||||
type="radio" id="radio-online" value="online" name="chat_status" class="custom-control-input"/>
|
||||
<input ?checked=${o.status === 'online'}
|
||||
type="radio" id="radio-online" value="online" name="chat_status" class="custom-control-input"/>
|
||||
<label class="custom-control-label" for="radio-online">
|
||||
<span class="fa fa-circle chat-status chat-status--online"></span>{{{o.label_online}}}</label>
|
||||
<span class="fa fa-circle chat-status chat-status--online"></span>${o.label_online}</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio">
|
||||
<input {[ if (o.status === 'busy') { ]} checked="checked" {[ } ]}
|
||||
type="radio" id="radio-busy" value="dnd" name="chat_status" class="custom-control-input"/>
|
||||
<input ?checked=${o.status === 'busy'}
|
||||
type="radio" id="radio-busy" value="dnd" name="chat_status" class="custom-control-input"/>
|
||||
<label class="custom-control-label" for="radio-busy">
|
||||
<span class="fa fa-minus-circle chat-status chat-status--busy"></span>{{{o.label_busy}}}</label>
|
||||
<span class="fa fa-minus-circle chat-status chat-status--busy"></span>${o.label_busy}</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio">
|
||||
<input {[ if (o.status === 'away') { ]} checked="checked" {[ } ]}
|
||||
type="radio" id="radio-away" value="away" name="chat_status" class="custom-control-input"/>
|
||||
<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">
|
||||
<span class="fa fa-circle chat-status chat-status--away"></span>{{{o.label_away}}}</label>
|
||||
<span class="fa fa-circle chat-status chat-status--away"></span>${o.label_away}</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio">
|
||||
<input {[ if (o.status === 'xa') { ]} checked="checked" {[ } ]}
|
||||
type="radio" id="radio-xa" value="xa" name="chat_status" class="custom-control-input"/>
|
||||
<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">
|
||||
<span class="far fa-circle chat-status chat-status--xa"></span>{{{o.label_xa}}}</label>
|
||||
<span class="far fa-circle chat-status chat-status--xa"></span>${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}}}"/>
|
||||
<span class="clear-input fa fa-times {[ if (!o.status_message) { ]} hidden {[ } ]}"></span>
|
||||
<input name="status_message" type="text" class="form-control"
|
||||
value="${o.status_message || ''}" placeholder="${o.placeholder_status_message}"/>
|
||||
<span class="clear-input fa fa-times ${o.status_message ? '' : 'hidden'}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">{{{o.label_save}}}</button>
|
||||
<button type="submit" class="btn btn-primary">${o.label_save}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,70 +0,0 @@
|
||||
<div class="modal" id="room-details-modal" tabindex="-1" role="dialog" aria-labelledby="room-details-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="room-details-modal-label">{{{o.display_name}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="room-info">
|
||||
<p class="room-info"><strong>{{{o.__('Name')}}}</strong>: {{{o.name}}}</p>
|
||||
<p class="room-info"><strong>{{{o.__('Groupchat address (JID)')}}}</strong>: {{{o.jid}}}</p>
|
||||
<p class="room-info"><strong>{{{o.__('Description')}}}</strong>: {{{o.config.description}}}</p>
|
||||
{[ if (o.subject) { ]}
|
||||
<p class="room-info"><strong>{{{o.__('Topic')}}}</strong>: {{o.topic}}</p> <!-- Sanitized in converse-muc-views. We want to render links. -->
|
||||
<p class="room-info"><strong>{{{o.__('Topic author')}}}</strong>: {{{o.subject && o.subject.author}}}</p>
|
||||
{[ } ]}
|
||||
<p class="room-info"><strong>{{{o.__('Online users')}}}</strong>: {{{o.num_occupants}}}</p>
|
||||
<p class="room-info"><strong>{{{o.__('Features')}}}</strong>:
|
||||
<div class="chatroom-features">
|
||||
<ul class="features-list">
|
||||
{[ if (o.features.passwordprotected) { ]}
|
||||
<li class="feature" ><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}} - <em>{{{ o.__('This groupchat requires a password before entry') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.unsecured) { ]}
|
||||
<li class="feature" ><span class="fa fa-unlock"></span>{{{ o.__('No password required') }}} - <em>{{{ o.__('This groupchat does not require a password upon entry') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.hidden) { ]}
|
||||
<li class="feature" ><span class="fa fa-eye-slash"></span>{{{ o.__('Hidden') }}} - <em>{{{ o.__('This groupchat is not publicly searchable') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.public_room) { ]}
|
||||
<li class="feature" ><span class="fa fa-eye"></span>{{{ o.__('Public') }}} - <em>{{{ o.__('This groupchat is publicly searchable') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.membersonly) { ]}
|
||||
<li class="feature" ><span class="fa fa-address-book"></span>{{{ o.__('Members only') }}} - <em>{{{ o.__('This groupchat is restricted to members only') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.open) { ]}
|
||||
<li class="feature" ><span class="fa fa-globe"></span>{{{ o.__('Open') }}} - <em>{{{ o.__('Anyone can join this groupchat') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.persistent) { ]}
|
||||
<li class="feature" ><span class="fa fa-save"></span>{{{ o.__('Persistent') }}} - <em>{{{ o.__('This groupchat persists even if it\'s unoccupied') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.temporary) { ]}
|
||||
<li class="feature" ><span class="fa fa-snowflake-o"></span>{{{ o.__('Temporary') }}} - <em>{{{ o.__('This groupchat will disappear once the last person leaves') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.nonanonymous) { ]}
|
||||
<li class="feature" ><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}} - <em>{{{ o.__('All other groupchat participants can see your XMPP address') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.semianonymous) { ]}
|
||||
<li class="feature" ><span class="fa fa-user-secret"></span>{{{ o.__('Semi-anonymous') }}} - <em>{{{ o.__('Only moderators can see your XMPP address') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.moderated) { ]}
|
||||
<li class="feature" ><span class="fa fa-gavel"></span>{{{ o.__('Moderated') }}} - <em>{{{ o.__('Participants entering this groupchat need to request permission to write') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.unmoderated) { ]}
|
||||
<li class="feature" ><span class="fa fa-info-circle"></span>{{{ o.__('Not moderated') }}} - <em>{{{ o.__('Participants entering this groupchat can write right away') }}}</em></li>
|
||||
{[ } ]}
|
||||
{[ if (o.features.mam_enabled) { ]}
|
||||
<li class="feature" ><span class="fa fa-database"></span>{{{ o.__('Message archiving') }}} - <em>{{{ o.__('Messages are archived on the server') }}}</em></li>
|
||||
{[ } ]}
|
||||
</ul>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-warning" data-dismiss="modal">{{{o.__('Close')}}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
88
src/templates/chatroom_details_modal.js
Normal file
88
src/templates/chatroom_details_modal.js
Normal file
@ -0,0 +1,88 @@
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { html } from "lit-html";
|
||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
import xss from "xss/dist/xss";
|
||||
|
||||
|
||||
const i18n_address = __('Groupchat address (JID)');
|
||||
const i18n_archiving = __('Message archiving');
|
||||
const i18n_archiving_help = __('Messages are archived on the server');
|
||||
const i18n_close = __('Close');
|
||||
const i18n_desc = __('Description');
|
||||
const i18n_features = __('Features');
|
||||
const i18n_hidden = __('Hidden');
|
||||
const i18n_hidden_help = __('This groupchat is not publicly searchable');
|
||||
const i18n_members_help = __('This groupchat is restricted to members only');
|
||||
const i18n_members_only = __('Members only');
|
||||
const i18n_moderated = __('Moderated');
|
||||
const i18n_moderated_help = __('Participants entering this groupchat need to request permission to write');
|
||||
const i18n_name = __('Name');
|
||||
const i18n_no_pass_help = __('This groupchat does not require a password upon entry');
|
||||
const i18n_no_password_required = __('No password required');
|
||||
const i18n_not_anonymous = __('Not anonymous');
|
||||
const i18n_not_anonymous_help = __('All other groupchat participants can see your XMPP address');
|
||||
const i18n_not_moderated = __('Not moderated');
|
||||
const i18n_not_moderated_help = __('Participants entering this groupchat can write right away');
|
||||
const i18n_online_users = __('Online users');
|
||||
const i18n_open = __('Open');
|
||||
const i18n_open_help = __('Anyone can join this groupchat');
|
||||
const i18n_password_help = __('This groupchat requires a password before entry');
|
||||
const i18n_password_protected = __('Password protected');
|
||||
const i18n_persistent = __('Persistent');
|
||||
const i18n_persistent_help = __('This groupchat persists even if it\'s unoccupied');
|
||||
const i18n_public = __('Public');
|
||||
const i18n_semi_anon = __('Semi-anonymous');
|
||||
const i18n_semi_anon_help = __('Only moderators can see your XMPP address');
|
||||
const i18n_temporary = __('Temporary');
|
||||
const i18n_temporary_help = __('This groupchat will disappear once the last person leaves');
|
||||
const i18n_topic = __('Topic');
|
||||
const i18n_topic_author = __('Topic author');
|
||||
|
||||
|
||||
const subject = (o) => html`
|
||||
<p class="room-info"><strong>${i18n_topic}</strong>: ${unsafeHTML(xss.filterXSS(o.subject.text, {'whitelist': {}}))}</p>
|
||||
<p class="room-info"><strong>${i18n_topic_author}</strong>: ${o.subject && o.subject.author}</p>
|
||||
`;
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="room-details-modal-label">${o.display_name}</h5>
|
||||
${modal_header_close_button}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<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>: ${o.jid}</p>
|
||||
<p class="room-info"><strong>${i18n_desc}</strong>: ${o.config.description}</p>
|
||||
${ (o.subject) ? subject(o) : '' }
|
||||
<p class="room-info"><strong>${i18n_online_users}</strong>: ${o.num_occupants}</p>
|
||||
<p class="room-info"><strong>${i18n_features}</strong>:
|
||||
<div class="chatroom-features">
|
||||
<ul class="features-list">
|
||||
${ o.features.passwordprotected ? html`<li class="feature" ><span class="fa fa-lock"></span>${i18n_password_protected} - <em>${i18n_password_help}</em></li>` : '' }
|
||||
${ o.features.unsecured ? html`<li class="feature" ><span class="fa fa-unlock"></span>${i18n_no_password_required} - <em>${i18n_no_pass_help}</em></li>` : '' }
|
||||
${ o.features.hidden ? html`<li class="feature" ><span class="fa fa-eye-slash"></span>${i18n_hidden} - <em>${i18n_hidden_help}</em></li>` : '' }
|
||||
${ o.features.public_room ? html`<li class="feature" ><span class="fa fa-eye"></span>${i18n_public} - <em>${o.__('This groupchat is publicly searchable') }</em></li>` : '' }
|
||||
${ o.features.membersonly ? html`<li class="feature" ><span class="fa fa-address-book"></span>${i18n_members_only} - <em>${i18n_members_help}</em></li>` : '' }
|
||||
${ o.features.open ? html`<li class="feature" ><span class="fa fa-globe"></span>${i18n_open} - <em>${i18n_open_help}</em></li>` : '' }
|
||||
${ o.features.persistent ? html`<li class="feature" ><span class="fa fa-save"></span>${i18n_persistent} - <em>${i18n_persistent_help}</em></li>` : '' }
|
||||
${ o.features.temporary ? html`<li class="feature" ><span class="fa fa-snowflake-o"></span>${i18n_temporary} - <em>${i18n_temporary_help}</em></li>` : '' }
|
||||
${ o.features.nonanonymous ? html`<li class="feature" ><span class="fa fa-id-card"></span>${i18n_not_anonymous} - <em>${i18n_not_anonymous_help}</em></li>` : '' }
|
||||
${ o.features.semianonymous ? html`<li class="feature" ><span class="fa fa-user-secret"></span>${i18n_semi_anon} - <em>${i18n_semi_anon_help}</em></li>` : '' }
|
||||
${ o.features.moderated ? html`<li class="feature" ><span class="fa fa-gavel"></span>${i18n_moderated} - <em>${i18n_moderated_help}</em></li>` : '' }
|
||||
${ o.features.unmoderated ? html`<li class="feature" ><span class="fa fa-info-circle"></span>${i18n_not_moderated} - <em>${i18n_not_moderated_help}</em></li>` : '' }
|
||||
${ o.features.mam_enabled ? html`<li class="feature" ><span class="fa fa-database"></span>${i18n_archiving} - <em>${i18n_archiving_help}</em></li>` : '' }
|
||||
</ul>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">${modal_close_button}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,19 +0,0 @@
|
||||
<div class="modal" id="room-registration-modal" tabindex="-1" role="dialog" aria-labelledby="room-registration-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="room-registration-modal-label">{{{o.display_name}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="converse-form">
|
||||
{[ if (o.feedback.get('error')) { ]} <div class="alert alert-danger" role="alert">{{{o.feedback.get('error')}}}</div> {[ } ]}
|
||||
{[ if (!o.feedback.get('error')) { ]} <span class="spinner fa fa-spinner"></span> {[ } ]}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{{o.__('Close')}}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,21 +0,0 @@
|
||||
<!-- Change status Modal -->
|
||||
<div class="modal" id="modal-status-change" tabindex="-1" role="dialog" aria-labelledby="changeStatusModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="changeStatusModalLabel">{{{o.modal_title}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container brand-heading-container">
|
||||
<h6 class="brand-heading">Converse</h6>
|
||||
<p class="brand-subtitle">{{{o.version_name}}}</p>
|
||||
<p class="brand-subtitle">{{o.first_subtitle}}</p>
|
||||
<p class="brand-subtitle">{{o.second_subtitle}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
42
src/templates/client_info_modal.js
Normal file
42
src/templates/client_info_modal.js
Normal file
@ -0,0 +1,42 @@
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { html } from "lit-html";
|
||||
import { modal_header_close_button } from "./buttons"
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
import xss from "xss/dist/xss";
|
||||
|
||||
|
||||
const modal_title = __('About');
|
||||
|
||||
const first_subtitle = __(
|
||||
'%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>',
|
||||
'<a target="_blank" rel="nofollow" href="https://opkode.com">'
|
||||
);
|
||||
|
||||
const second_subtitle = __(
|
||||
'%1$s Translate %2$s it into your own language',
|
||||
'<a target="_blank" rel="nofollow" href="https://hosted.weblate.org/projects/conversejs/#languages">',
|
||||
'</a>'
|
||||
);
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="changeStatusModalLabel">${modal_title}</h5>
|
||||
${modal_header_close_button}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="modal-alert"></span>
|
||||
<div class="container brand-heading-container">
|
||||
<h6 class="brand-heading">Converse</h6>
|
||||
<p class="brand-subtitle">${o.version_name}</p>
|
||||
<p class="brand-subtitle">${unsafeHTML(xss.filterXSS(first_subtitle, {'whiteList': {'a': []}}))}</p>
|
||||
<p class="brand-subtitle">${unsafeHTML(xss.filterXSS(second_subtitle, {'whiteList': {'a': []}}))}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,25 +0,0 @@
|
||||
<div class="modal" id="list-chatrooms-modal" tabindex="-1" role="dialog" aria-labelledby="list-chatrooms-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"
|
||||
id="list-chatrooms-modal-label">{{{o.heading_list_chatrooms}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body d-flex flex-column">
|
||||
{[ if (o.show_form) { ]}
|
||||
<form class="converse-form list-chatrooms">
|
||||
<div class="form-group">
|
||||
<label for="chatroom">{{{o.label_server_address}}}:</label>
|
||||
<input type="text" value="{{{o.muc_domain}}}" required="required" name="server" class="form-control" placeholder="{{{o.server_placeholder}}}"/>
|
||||
</div>
|
||||
<input type="submit" class="btn btn-primary" name="list" value="{{{o.label_query}}}"/>
|
||||
</form>
|
||||
{[ } ]}
|
||||
<ul class="available-chatrooms list-group"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
36
src/templates/list_chatrooms_modal.js
Normal file
36
src/templates/list_chatrooms_modal.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
||||
|
||||
const i18n_list_chatrooms = __('Query for Groupchats');
|
||||
const i18n_server_address = __('Server address');
|
||||
const i18n_query = __('Show groupchats');
|
||||
|
||||
|
||||
const form = (o) => html`
|
||||
<form class="converse-form list-chatrooms">
|
||||
<div class="form-group">
|
||||
<label for="chatroom">${i18n_server_address}:</label>
|
||||
<input type="text" value="${o.muc_domain}" required="required" name="server" class="form-control" placeholder="${o.server_placeholder}"/>
|
||||
</div>
|
||||
<input type="submit" class="btn btn-primary" name="list" value="${i18n_query}"/>
|
||||
</form>
|
||||
`;
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="list-chatrooms-modal-label">${i18n_list_chatrooms}</h5>
|
||||
${modal_header_close_button}
|
||||
</div>
|
||||
<div class="modal-body d-flex flex-column">
|
||||
<span class="modal-alert"></span>
|
||||
${o.show_form ? form(o) : '' }
|
||||
<ul class="available-chatrooms list-group"></ul>
|
||||
</div>
|
||||
<div class="modal-footer">${modal_close_button}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,20 +0,0 @@
|
||||
<div class="modal" id="message-versions-modal" tabindex="-1" role="dialog" aria-labelledby="message-versions-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="message-versions-modal-label">{{{o.__('Message versions')}}}</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h4>Older versions</h4>
|
||||
{[Object.keys(o.older_versions).forEach(function (k) { ]} <p class="older-msg"><time>{{{o.dayjs(k).format('MMM D, YYYY, HH:mm:ss')}}}</time>: {{{o.older_versions[k]}}}</p> {[ }); ]}
|
||||
<hr/>
|
||||
<h4>Current version</h4>
|
||||
<p>{{{o.message}}}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{{o.__('Close')}}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
27
src/templates/message_versions_modal.js
Normal file
27
src/templates/message_versions_modal.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import dayjs from 'dayjs';
|
||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
||||
|
||||
|
||||
const i18n_message_versions = __('Message versions');
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="message-versions-modal-label">${i18n_message_versions}</h4>
|
||||
${modal_header_close_button}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h4>Older versions</h4>
|
||||
${Object.keys(o.older_versions).map(k => html`<p class="older-msg"><time>${dayjs(k).format('MMM D, YYYY, HH:mm:ss')}</time>: ${o.older_versions[k]}</p>`) }
|
||||
<hr/>
|
||||
<h4>Current version</h4>
|
||||
<p>${o.message}</p>
|
||||
</div>
|
||||
<div class="modal-footer">${modal_close_button}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,219 +0,0 @@
|
||||
<div class="modal" id="converse-modtools-modal" tabindex="-1" role="dialog" aria-labelledby="converse-modtools-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="converse-modtools-modal-label">{{{o.__('Moderator Tools')}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body d-flex flex-column">
|
||||
<ul class="nav nav-pills justify-content-center">
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link active" id="roles-tab" href="#roles-tabpanel" aria-controls="roles-tabpanel" role="tab" data-toggle="tab">Roles</a>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link" id="affiliations-tab" href="#affiliations-tabpanel" aria-controls="affiliations-tabpanel" role="tab" data-toggle="tab">Affiliations</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane tab-pane--columns active" id="roles-tabpanel" role="tabpanel" aria-labelledby="roles-tab">
|
||||
<form class="converse-form query-role">
|
||||
<p class="helptext pb-3">
|
||||
{{{o.__("Roles are assigned to users to grant or deny them certain abilities in a multi-user chat. They're assigned either explicitly or implicitly as part of an affiliation. A role that's not due to an affiliation, is only valid for the duration of the user's session.")}}}
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="role">
|
||||
<strong>{{{o.__('Role')}}}:</strong>
|
||||
</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-role" name="role">
|
||||
{[ o.roles.forEach(function (role) { ]}
|
||||
<option value="{{{role}}}" {[ if (role === o.role) { ]} selected="selected" {[ } ]}
|
||||
{[ if (role === 'moderator') { ]}
|
||||
title="{{{o.__("Moderators are privileged users who can change the roles of other users (except those with admin or owner affiliations.")}}}"
|
||||
{[ } ]}
|
||||
{[ if (role === 'participant') { ]}
|
||||
title="{{{o.__("The default role, implies that you can read and write messages.")}}}"
|
||||
{[ } ]}
|
||||
{[ if (role === 'visitor') { ]}
|
||||
title="{{{o.__("Visitors aren't allowed to write messages in a moderated multi-user chat.")}}}"
|
||||
{[ } ]}>{{{role}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="submit" class="btn btn-primary" name="users_with_role" value="{{{o.__('Show users')}}}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col pt-2">
|
||||
{[ if (o.role === 'moderator') { ]}
|
||||
<p class="helptext pb-3">{{{o.__("Moderators are privileged users who can change the roles of other users (except those with admin or owner affiliations.")}}}</p>
|
||||
{[ } ]}
|
||||
{[ if (o.role === 'participant') { ]}
|
||||
<p class="helptext pb-3">{{{o.__("The default role, implies that you can read and write messages.")}}}</p>
|
||||
{[ } ]}
|
||||
{[ if (o.role === 'visitor') { ]}
|
||||
<p class="helptext pb-3">{{{o.__("Visitors aren't allowed to write messages in a moderated multi-user chat.")}}}</p>
|
||||
{[ } ]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="scrollable-container">
|
||||
<ul class="list-group list-group--users">
|
||||
{[ if (o.loading_users_with_role) { ]}
|
||||
<li class="list-group-item"> <span class="spinner fa fa-spinner centered"/> </li>
|
||||
{[ } ]}
|
||||
{[ if (o.users_with_role && o.users_with_role.length === 0) { ]}
|
||||
<li class="list-group-item">{{{o.__('No users with that role found.')}}}</li>
|
||||
{[ } ]}
|
||||
{[ (o.users_with_role || []).forEach(function (item) { ]}
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active">
|
||||
<div><strong>JID:</strong> {{{item.jid}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Nickname:</strong> {{{item.nick}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Role:</strong> {{{item.role}}}<a href="#" data-form="role-form" class="toggle-form right fa fa-wrench"></a></div>
|
||||
<form class="role-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="{{{item.jid}}}"/>
|
||||
<input type="hidden" name="nick" value="{{{item.nick}}}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('New Role')}}}:</strong></label>
|
||||
<select class="custom-select select-role" name="role">
|
||||
{[ o.allowed_roles.forEach(function (role) { ]}
|
||||
<option value="{{{role}}}" {[ if (role === item.role) { ]} selected="selected" {[ } ]}>{{{role}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('Reason')}}}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" value="{{{o.__('Change role')}}}"/>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane tab-pane--columns" id="affiliations-tabpanel" role="tabpanel" aria-labelledby="affiliations-tab">
|
||||
<form class="converse-form query-affiliation">
|
||||
<p class="helptext pb-3">
|
||||
{{{o.__("An affiliation is a long-lived entitlement which typically implies a certain role and which grants privileges and responsibilities. For example admins and owners automatically have the moderator role.")}}}
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">
|
||||
<strong>{{{o.__('Affiliation')}}}:</strong>
|
||||
</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
{[ o.affiliations.forEach(function (aff) { ]}
|
||||
<option value="{{{aff}}}" {[ if (aff === o.affiliation) { ]} selected="selected" {[ } ]}
|
||||
{[ if (aff === 'owner') { ]}
|
||||
title="{{{o.__("Owner is the highest affiliation. Owners can modify roles and affiliations of all other users.")}}}"
|
||||
{[ } ]}
|
||||
{[ if (aff === 'admin') { ]}
|
||||
title="{{{o.__("Admin is the 2nd highest affiliation. Admins can modify roles and affiliations of all other users except owners.")}}}"
|
||||
{[ } ]}
|
||||
{[ if (aff === 'outcast') { ]}
|
||||
title="{{{o.__("To ban a user, you give them the affiliation of \"outcast\".")}}}"
|
||||
{[ } ]}>{{{aff}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="submit" class="btn btn-primary" name="users_with_affiliation" value="{{{o.__('Show users')}}}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col pt-2">
|
||||
{[ if (o.affiliation === 'owner') { ]}
|
||||
<p class="helptext pb-3">{{{o.__("Owner is the highest affiliation. Owners can modify roles and affiliations of all other users.")}}}</p>
|
||||
{[ } ]}
|
||||
{[ if (o.affiliation === 'admin') { ]}
|
||||
<p class="helptext pb-3">{{{o.__("Admin is the 2nd highest affiliation. Admins can modify roles and affiliations of all other users except owners.")}}}</p>
|
||||
{[ } ]}
|
||||
{[ if (o.affiliation === 'outcast') { ]}
|
||||
<p class="helptext pb-3">{{{o.__("To ban a user, you give them the affiliation of \"outcast\".")}}}</p>
|
||||
{[ } ]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="scrollable-container">
|
||||
<ul class="list-group list-group--users">
|
||||
{[ if (o.loading_users_with_affiliation) { ]}
|
||||
<li class="list-group-item"> <span class="spinner fa fa-spinner centered"/> </li>
|
||||
{[ } else { ]}
|
||||
{[ if (o.users_with_affiliation && o.users_with_affiliation.length === 0) { ]}
|
||||
<li class="list-group-item">{{{o.__('No users with that affiliation found.')}}}</li>
|
||||
{[ } else if (o.users_with_affiliation instanceof Error) { ]}
|
||||
<li class="list-group-item">{{{o.users_with_affiliation.message}}}</li>
|
||||
{[ } else { ]}
|
||||
{[ (o.users_with_affiliation || []).forEach(function (item) { ]}
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active">
|
||||
<div><strong>JID:</strong> {{{item.jid}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Nickname:</strong> {{{item.nick}}}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Affiliation:</strong> {{{item.affiliation}}} <a href="#" data-form="affiliation-form" class="toggle-form right fa fa-wrench"></a></div>
|
||||
<form class="affiliation-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="{{{item.jid}}}"/>
|
||||
<input type="hidden" name="nick" value="{{{item.nick}}}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('New affiliation')}}}:</strong></label>
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
{[ o.allowed_affiliations.forEach(function (aff) { ]}
|
||||
<option value="{{{aff}}}" {[ if (aff === item.affiliation) { ]} selected="selected" {[ } ]}>{{{aff}}}</option>
|
||||
{[ }); ]}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>{{{o.__('Reason')}}}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" name="change" value="{{{o.__('Change affiliation')}}}"/>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
{[ } ]}
|
||||
{[ } ]}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
229
src/templates/moderator_tools_modal.js
Normal file
229
src/templates/moderator_tools_modal.js
Normal file
@ -0,0 +1,229 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import spinner from "./spinner.js";
|
||||
import { modal_header_close_button } from "./buttons"
|
||||
|
||||
|
||||
const i18n_affiliation = __('Affiliation');
|
||||
const i18n_change_affiliation = __('Change affiliation');
|
||||
const i18n_change_role = __('Change role');
|
||||
const i18n_moderator_tools = __('Moderator Tools');
|
||||
const i18n_new_affiliation = __('New affiliation');
|
||||
const i18n_new_role = __('New Role');
|
||||
const i18n_no_users_with_aff = __('No users with that affiliation found.')
|
||||
const i18n_no_users_with_role = __('No users with that role found.');
|
||||
const i18n_reason = __('Reason');
|
||||
const i18n_role = __('Role');
|
||||
const i18n_show_users = __('Show users');
|
||||
|
||||
const i18n_helptext_role = __(
|
||||
"Roles are assigned to users to grant or deny them certain abilities in a multi-user chat. "+
|
||||
"They're assigned either explicitly or implicitly as part of an affiliation. "+
|
||||
"A role that's not due to an affiliation, is only valid for the duration of the user's session."
|
||||
);
|
||||
|
||||
const i18n_helptext_affiliation = __(
|
||||
"An affiliation is a long-lived entitlement which typically implies a certain role and which "+
|
||||
"grants privileges and responsibilities. For example admins and owners automatically have the "+
|
||||
"moderator role."
|
||||
);
|
||||
|
||||
|
||||
function getRoleHelpText (role) {
|
||||
if (role === 'moderator') {
|
||||
return __("Moderators are privileged users who can change the roles of other users (except those with admin or owner affiliations.");
|
||||
} else if (role === 'participant') {
|
||||
return __("The default role, implies that you can read and write messages.");
|
||||
} else if (role == 'visitor') {
|
||||
return __("Visitors aren't allowed to write messages in a moderated multi-user chat.");
|
||||
}
|
||||
}
|
||||
|
||||
function getAffiliationHelpText (aff) {
|
||||
if (aff === 'owner') {
|
||||
return __("Owner is the highest affiliation. Owners can modify roles and affiliations of all other users.");
|
||||
} else if (aff === 'admin') {
|
||||
return __("Admin is the 2nd highest affiliation. Admins can modify roles and affiliations of all other users except owners.");
|
||||
} else if (aff === 'outcast') {
|
||||
return __("To ban a user, you give them the affiliation of \"outcast\".");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const role_option = (o) => html`
|
||||
<option value="${o.item || ''}"
|
||||
?selected=${o.item === o.role}
|
||||
title="${getRoleHelpText(o.item)}">${o.item}</option>
|
||||
`;
|
||||
|
||||
|
||||
const affiliation_option = (o) => html`
|
||||
<option value="${o.item || ''}"
|
||||
?selected=${o.item === o.affiliation}
|
||||
title="${getAffiliationHelpText(o.item)}">${o.item}</option>
|
||||
`;
|
||||
|
||||
|
||||
const role_list_item = (o) => html`
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active">
|
||||
<div><strong>JID:</strong> ${o.item.jid}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Nickname:</strong> ${o.item.nick}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Role:</strong> ${o.item.role}<a href="#" data-form="role-form" class="toggle-form right fa fa-wrench"></a></div>
|
||||
<form class="role-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="${o.item.jid}"/>
|
||||
<input type="hidden" name="nick" value="${o.item.nick}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>${i18n_new_role}:</strong></label>
|
||||
<select class="custom-select select-role" name="role">
|
||||
${ o.allowed_roles.map(role => html`<option value="${role}" ?selected=${role === o.item.role}>${role}</option>`) }
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>${i18n_reason}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" value="${i18n_change_role}"/>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
`;
|
||||
|
||||
|
||||
const affiliation_list_item = (o) => html`
|
||||
<li class="list-group-item">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active">
|
||||
<div><strong>JID:</strong> ${o.item.jid}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Nickname:</strong> ${o.item.nick}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div><strong>Affiliation:</strong> ${o.item.affiliation} <a href="#" data-form="affiliation-form" class="toggle-form right fa fa-wrench"></a></div>
|
||||
<form class="affiliation-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="jid" value="${o.item.jid}"/>
|
||||
<input type="hidden" name="nick" value="${o.item.nick}"/>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label><strong>${i18n_new_affiliation}:</strong></label>
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
${ o.allowed_affiliations.map(aff => html`<option value="${aff}" ?selected=${aff === o.item.affiliation}>${aff}</option>`) }
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label><strong>${i18n_reason}:</strong></label>
|
||||
<input class="form-control" type="text" name="reason"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" class="btn btn-primary" name="change" value="${i18n_change_affiliation}"/>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
`;
|
||||
|
||||
|
||||
export default (o) => 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">
|
||||
<span class="modal-alert"></span>
|
||||
|
||||
<ul class="nav nav-pills justify-content-center">
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link active" id="roles-tab" href="#roles-tabpanel" aria-controls="roles-tabpanel" role="tab" data-toggle="tab">Roles</a>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link" id="affiliations-tab" href="#affiliations-tabpanel" aria-controls="affiliations-tabpanel" role="tab" data-toggle="tab">Affiliations</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane tab-pane--columns active" id="roles-tabpanel" role="tabpanel" aria-labelledby="roles-tab">
|
||||
<form class="converse-form query-role">
|
||||
<p class="helptext pb-3">${i18n_helptext_role}</p>
|
||||
<div class="form-group">
|
||||
<label for="role"><strong>${i18n_role}:</strong></label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-role" name="role">
|
||||
${o.roles.map(item => role_option(Object.assign({item}, o)))}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="submit" class="btn btn-primary" name="users_with_role" value="${i18n_show_users}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col pt-2"><p class="helptext pb-3">${getRoleHelpText(o.role)}</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="scrollable-container">
|
||||
<ul class="list-group list-group--users">
|
||||
${ o.loading_users_with_role ? html`<li class="list-group-item"> ${spinner()} </li>` : '' }
|
||||
${ (o.users_with_role && o.users_with_role.length === 0) ? html`<li class="list-group-item">${i18n_no_users_with_role}</li>` : '' }
|
||||
${ (o.users_with_role || []).map(item => role_list_item(Object.assign({item}, o))) }
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane tab-pane--columns" id="affiliations-tabpanel" role="tabpanel" aria-labelledby="affiliations-tab">
|
||||
<form class="converse-form query-affiliation">
|
||||
<p class="helptext pb-3">${i18n_helptext_affiliation}</p>
|
||||
<div class="form-group">
|
||||
<label for="affiliation">
|
||||
<strong>${i18n_affiliation}:</strong>
|
||||
</label>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<select class="custom-select select-affiliation" name="affiliation">
|
||||
${o.affiliations.map(item => affiliation_option(Object.assign({item}, o)))}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="submit" class="btn btn-primary" name="users_with_affiliation" value="${i18n_show_users}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col pt-2"><p class="helptext pb-3">${getAffiliationHelpText(o.affiliation)}</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="scrollable-container">
|
||||
<ul class="list-group list-group--users">
|
||||
${ (o.loading_users_with_affiliation) ? html`<li class="list-group-item"> ${spinner()} </li>` : '' }
|
||||
${ (Array.isArray(o.users_with_affiliation) && o.users_with_affiliation.length === 0) ? html`<li class="list-group-item">${i18n_no_users_with_aff}</li>` : '' }
|
||||
${ (o.users_with_affiliation instanceof Error) ?
|
||||
html`<li class="list-group-item">${o.users_with_affiliation.message}</li>` :
|
||||
(o.users_with_affiliation || []).map(item => affiliation_list_item(Object.assign({item}, o))) }
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
32
src/templates/profile.js
Normal file
32
src/templates/profile.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
|
||||
|
||||
const i18n_logout = __('Log out');
|
||||
const i18n_change_status = __('Click to change your chat status');
|
||||
const i18n_details = __('Show details about this chat client');
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="userinfo controlbox-padded">
|
||||
<div class="controlbox-section profile d-flex">
|
||||
<a class="show-profile" href="#">
|
||||
<canvas class="avatar align-self-center" height="40" width="40"></canvas>
|
||||
</a>
|
||||
<span class="username w-100 align-self-center">${o.fullname}</span>
|
||||
${o._converse.show_client_info && html`<a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="${i18n_details}"></a>`}
|
||||
${o._converse.allow_logout && html`<a class="controlbox-heading__btn logout fa fa-sign-out-alt align-self-center" title="${i18n_logout}"></a>`}
|
||||
</div>
|
||||
<div class="d-flex xmpp-status">
|
||||
<a class="change-status" title="${i18n_change_status}" data-toggle="modal" data-target="#changeStatusModal">
|
||||
<span class="${o.chat_status} w-100 align-self-center" data-value="${o.chat_status}">
|
||||
<span class="
|
||||
${o.chat_status === 'online' && 'fa fa-circle chat-status chat-status--online'}
|
||||
${o.chat_status === 'dnd' && 'fa fa-minus-circle chat-status chat-status--busy'}
|
||||
${o.chat_status === 'away' && 'fa fa-circle chat-status chat-status--away'}
|
||||
${o.chat_status === 'xa' && 'far fa-circle chat-status chat-status--xa '}
|
||||
${o.chat_status === 'offline' && 'fa fa-circle chat-status chat-status--offline'}"></span> ${o.status_message}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,125 +0,0 @@
|
||||
<div class="modal" id="user-profile-modal" tabindex="-1" role="dialog" aria-labelledby="user-profile-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="user-profile-modal-label">{{{o.heading_profile}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{[ if (o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse)) { ]}
|
||||
<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">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">OMEMO</a>
|
||||
</li>
|
||||
</ul>
|
||||
{[ } ]}
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="profile-tabpanel" role="tabpanel" aria-labelledby="profile-tab">
|
||||
<form class="converse-form converse-form--modal profile-form" action="#">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<a class="change-avatar" href="#">
|
||||
{[ if (o.image) { ]}
|
||||
<img alt="{{{o.alt_avatar}}}" class="img-thumbnail avatar align-self-center" height="100px" width="100px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
|
||||
{[ } ]}
|
||||
{[ if (!o.image) { ]}
|
||||
<canvas class="avatar" height="100px" width="100px"></canvas>
|
||||
{[ } ]}
|
||||
</a>
|
||||
<input class="hidden" name="image" type="file"/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label">{{{o.label_jid}}}:</label>
|
||||
<div>{{{o.jid}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-fullname" class="col-form-label">{{{o.label_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">{{{o.label_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">{{{o.label_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">{{{o.label_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">{{{o.label_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">{{{o.label_role_help}}}</small>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="save-form btn btn-primary">{{{o.__('Save and close')}}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{[ if (o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse)) { ]}
|
||||
<div class="tab-pane" id="omemo-tabpanel" role="tabpanel" aria-labelledby="omemo-tab">
|
||||
<form class="converse-form fingerprint-removal">
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item active">{{{o.__("This device's OMEMO fingerprint")}}}</li>
|
||||
<li class="list-group-item">
|
||||
{[ if (o.view.current_device && o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) { ]}
|
||||
<span class="fingerprint">{{{o.utils.formatFingerprint(o.view.current_device.get('bundle').fingerprint)}}}</span>
|
||||
{[ } else {]}
|
||||
<span class="spinner fa fa-spinner centered"/>
|
||||
{[ } ]}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-group">
|
||||
<button type="button" class="generate-bundle btn btn-danger">{{{o.__('Generate new keys and fingerprint')}}}</button>
|
||||
</div>
|
||||
|
||||
{[ if (o.view.other_devices.length) { ]}
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item nopadding active">
|
||||
<label>
|
||||
<input type="checkbox" class="select-all" title="{{{o.__('Select all')}}}"
|
||||
aria-label="{{{o.__('Checkbox to select fingerprints of all other OMEMO devices')}}}"/>
|
||||
{{{o.__('Other OMEMO-enabled devices')}}}
|
||||
</label>
|
||||
</li>
|
||||
{[ o.view.other_devices.forEach(function (device) { ]}
|
||||
{[ if (device.get('bundle') && device.get('bundle').fingerprint) { ]}
|
||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
||||
<label>
|
||||
<input type="checkbox" value="{{{device.get('id')}}}"
|
||||
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}"/>
|
||||
<span class="fingerprint">{{{o.utils.formatFingerprint(device.get('bundle').fingerprint)}}}</span>
|
||||
</label>
|
||||
</li>
|
||||
{[ } else {]}
|
||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
||||
<label>
|
||||
<input type="checkbox" value="{{{device.get('id')}}}"
|
||||
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}"/>
|
||||
<span>{{{o.__('Device without a fingerprint')}}}</span>
|
||||
</label>
|
||||
</li>
|
||||
{[ } ]}
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="save-form btn btn-primary">{{{o.__('Remove checked devices and close')}}}</button>
|
||||
</div>
|
||||
{[ } ]}
|
||||
</form>
|
||||
</div>
|
||||
{[ } ]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
165
src/templates/profile_modal.js
Normal file
165
src/templates/profile_modal.js
Normal file
@ -0,0 +1,165 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import avatar from "./avatar.js";
|
||||
import spinner from "./spinner.js";
|
||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
||||
|
||||
|
||||
const alt_avatar = __('Your avatar image');
|
||||
const heading_profile = __('Your Profile');
|
||||
const i18n_close = __('Close');
|
||||
const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following fingerprint');
|
||||
const i18n_device_without_fingerprint = __('Device without a fingerprint');
|
||||
const i18n_email = __('Email');
|
||||
const i18n_fingerprint = __("This device's OMEMO fingerprint");
|
||||
const i18n_fullname = __('Full Name');
|
||||
const i18n_generate = __('Generate new keys and fingerprint');
|
||||
const i18n_jid = __('XMPP Address (JID)');
|
||||
const i18n_nickname = __('Nickname');
|
||||
const i18n_other_devices = __('Other OMEMO-enabled devices');
|
||||
const i18n_other_devices_label = __('Checkbox to select fingerprints of all other OMEMO devices');
|
||||
const i18n_remove_devices = __('Remove checked devices and close');
|
||||
const i18n_role = __('Role');
|
||||
const i18n_save = __('Save and close');
|
||||
const i18n_select_all = __('Select all');
|
||||
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_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>`;
|
||||
|
||||
|
||||
const fingerprint = (o) => html`
|
||||
<span class="fingerprint">${o.utils.formatFingerprint(o.view.current_device.get('bundle').fingerprint)}</span>`;
|
||||
|
||||
|
||||
const device_with_fingerprint = (o) => html`
|
||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
||||
<label>
|
||||
<input type="checkbox" value="${o.device.get('id')}"
|
||||
aria-label="${i18n_fingerprint_checkbox_label}"/>
|
||||
<span class="fingerprint">${o.utils.formatFingerprint(o.device.get('bundle').fingerprint)}</span>
|
||||
</label>
|
||||
</li>
|
||||
`;
|
||||
|
||||
|
||||
const device_without_fingerprint = () => html`
|
||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
||||
<label>
|
||||
<input type="checkbox" value="${o.device.get('id')}"
|
||||
aria-label="${i18n_fingerprint_checkbox_label}"/>
|
||||
<span>${i18n_device_without_fingerprint}</span>
|
||||
</label>
|
||||
</li>
|
||||
`;
|
||||
|
||||
|
||||
const device_item = (o) => html`
|
||||
${(o.device.get('bundle') && o.device.get('bundle').fingerprint) ? device_with_fingerprint(o) : device_without_fingerprint(o) }
|
||||
`;
|
||||
|
||||
|
||||
const device_list = (o) => html`
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item nopadding active">
|
||||
<label>
|
||||
<input type="checkbox" class="select-all" title="${i18n_select_all}" aria-label="${i18n_other_devices_label}"/>
|
||||
${i18n_other_devices}
|
||||
</label>
|
||||
</li>
|
||||
${ o.view.other_devices.map(device => device_item(Object.assign({device}, o))) }
|
||||
</ul>
|
||||
<div class="form-group"><button type="submit" class="save-form btn btn-primary">${i18n_remove_devices}</button></div>
|
||||
`;
|
||||
|
||||
|
||||
const omemo_page = (o) => html`
|
||||
<div class="tab-pane" id="omemo-tabpanel" role="tabpanel" aria-labelledby="omemo-tab">
|
||||
<form class="converse-form fingerprint-removal">
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item active">${i18n_fingerprint}</li>
|
||||
<li class="list-group-item">
|
||||
${ (o.view.current_device && o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) ? fingerprint(o) : spinner() }
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-group">
|
||||
<button type="button" class="generate-bundle btn btn-danger">${i18n_generate}</button>
|
||||
</div>
|
||||
${ o.view.other_devices.length ? device_list(o) : '' }
|
||||
</form>
|
||||
</div>`;
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="user-profile-modal-label">${heading_profile}</h5>
|
||||
${modal_header_close_button}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="modal-alert"></span>
|
||||
${o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse) && navigation}
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="profile-tabpanel" role="tabpanel" aria-labelledby="profile-tab">
|
||||
<form class="converse-form converse-form--modal profile-form" action="#">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<a class="change-avatar" href="#">
|
||||
${o.image ? avatar(Object.assign({'alt_text': alt_avatar}, o)) : '<canvas class="avatar" height="100px" width="100px"></canvas>'}
|
||||
</a>
|
||||
<input class="hidden" name="image" type="file"/>
|
||||
</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>
|
||||
${ _converse.pluggable.plugins['converse-omemo'].enabled(_converse) && omemo_page(o) }
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">${modal_close_button}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,25 +0,0 @@
|
||||
<div class="userinfo controlbox-padded">
|
||||
<div class="controlbox-section profile d-flex">
|
||||
<a class="show-profile" href="#">
|
||||
<canvas class="avatar align-self-center" height="40" width="40"></canvas>
|
||||
</a>
|
||||
<span class="username w-100 align-self-center">{{{o.fullname}}}</span>
|
||||
{[ if (o._converse.show_client_info) { ]}
|
||||
<a class="controlbox-heading__btn show-client-info fa fa-info-circle align-self-center" title="{{{o.info_details}}}"></a>
|
||||
{[ } ]}
|
||||
{[ if (o._converse.allow_logout) { ]}
|
||||
<a class="controlbox-heading__btn logout fa fa-sign-out-alt align-self-center" title="{{{o.title_log_out}}}"></a>
|
||||
{[ } ]}
|
||||
</div>
|
||||
<div class="d-flex xmpp-status">
|
||||
<a class="change-status" title="{{{o.title_change_status}}}" data-toggle="modal" data-target="#changeStatusModal">
|
||||
<span class="{{{o.chat_status}}} w-100 align-self-center" data-value="{{{o.chat_status}}}">
|
||||
<span class="
|
||||
{[ if (o.chat_status === 'online') { ]} fa fa-circle chat-status chat-status--online{[ } ]}
|
||||
{[ if (o.chat_status === 'dnd') { ]} fa fa-minus-circle chat-status chat-status--busy {[ } ]}
|
||||
{[ if (o.chat_status === 'away') { ]} fa fa-circle chat-status chat-status--away{[ } ]}
|
||||
{[ if (o.chat_status === 'xa') { ]} far fa-circle chat-status chat-status--xa {[ } ]}
|
||||
{[ if (o.chat_status === 'offline') { ]} fa fa-circle chat-status chat-status--offline{[ } ]}"></span> {{{o.status_message}}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
@ -1,30 +0,0 @@
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header {{{o.level}}}">
|
||||
<h5 class="modal-title">{{{o.title}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="converse-form converse-form--modal confirm" action="#">
|
||||
<div class="form-group">
|
||||
{[o.messages.forEach(function (message) { ]}
|
||||
<p>{{{message}}}</p>
|
||||
{[ }) ]}
|
||||
</div>
|
||||
{[ if (o.type === 'prompt') { ]}
|
||||
<div class="form-group">
|
||||
<input type="text" name="reason" class="form-control" placeholder="{{{o.placeholder}}}"/>
|
||||
</div>
|
||||
{[ } ]}
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">{{{o.__('OK')}}}</button>
|
||||
<input type="button" class="btn btn-secondary" data-dismiss="modal" value="{{{o.__('Cancel')}}}"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
37
src/templates/prompt.js
Normal file
37
src/templates/prompt.js
Normal file
@ -0,0 +1,37 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
|
||||
|
||||
const i18n_cancel = __('Cancel');
|
||||
const i18n_ok = __('OK');
|
||||
|
||||
|
||||
export default (o) => html`
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header ${o.level}">
|
||||
<h5 class="modal-title">${o.title}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="modal-alert"></span>
|
||||
<form class="converse-form converse-form--modal confirm" action="#">
|
||||
<div class="form-group">
|
||||
${ o.messages.map(message => html`<p>${message}</p>`) }
|
||||
</div>
|
||||
{[ if (o.type === 'prompt') { ]}
|
||||
<div class="form-group">
|
||||
<input type="text" name="reason" class="form-control" placeholder="${o.placeholder}"/>
|
||||
</div>
|
||||
{[ } ]}
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">${i18n_ok}</button>
|
||||
<input type="button" class="btn btn-secondary" data-dismiss="modal" value="${i18n_cancel}"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
3
src/templates/spinner.js
Normal file
3
src/templates/spinner.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default () => html`<span class="spinner fa fa-spinner centered"/>`
|
@ -1,71 +0,0 @@
|
||||
<div class="modal" id="user-details-modal" tabindex="-1" role="dialog" aria-labelledby="user-details-modal-label" aria-hidden="true">
|
||||
<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>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.__('Close')}}}"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{[ if (o.image) { ]}
|
||||
<img alt="{{{o.__('The User\'s Profile Image')}}}"
|
||||
class="img-thumbnail avatar align-self-center mb-3"
|
||||
height="100" width="100" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
|
||||
{[ } ]}
|
||||
{[ if (o.fullname) { ]}
|
||||
<p><label>{{{o.__('Full Name:')}}}</label> {{{o.fullname}}}</p>
|
||||
{[ } ]}
|
||||
<p><label>{{{o.__('XMPP Address:')}}}</label> <a href="xmpp:{{{o.jid}}}">{{{o.jid}}}</a></p>
|
||||
{[ if (o.nickname) { ]}
|
||||
<p><label>{{{o.__('Nickname:')}}}</label> {{{o.nickname}}}</p>
|
||||
{[ } ]}
|
||||
{[ if (o.url) { ]}
|
||||
<p><label>{{{o.__('URL:')}}}</label> <a target="_blank" rel="noopener" href="{{{o.url}}}">{{{o.url}}}</a></p>
|
||||
{[ } ]}
|
||||
{[ if (o.email) { ]}
|
||||
<p><label>{{{o.__('Email:')}}}</label> <a href="mailto:{{{o.email}}}">{{{o.email}}}</a></p>
|
||||
{[ } ]}
|
||||
{[ if (o.role) { ]}
|
||||
<p><label>{{{o.__('Role:')}}}</label> {{{o.role}}}</p>
|
||||
{[ } ]}
|
||||
|
||||
{[ if (o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse)) { ]}
|
||||
<hr/>
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item active">{{{o.__('OMEMO Fingerprints')}}}</li>
|
||||
{[ if (!o.view.devicelist.devices) { ]}
|
||||
<li class="list-group-item"><span class="spinner fa fa-spinner centered"/></li>
|
||||
{[ } ]}
|
||||
{[ if (o.view.devicelist.devices) { ]}
|
||||
{[ o.view.devicelist.devices.each(function (device) { ]}
|
||||
{[ if (device.get('bundle') && device.get('bundle').fingerprint) { ]}
|
||||
<li class="list-group-item">
|
||||
<form class="fingerprint-trust">
|
||||
<div class="btn-group btn-group-toggle">
|
||||
<label class="btn btn--small {[ if (device.get('trusted') !== -1) { ]} btn-primary active {[ } else { ]} btn-secondary {[ } ]}">
|
||||
<input type="radio" name="{{{device.get('id')}}}" value="1"
|
||||
{[ if (device.get('trusted') !== -1) { ]} checked="checked" {[ } ]}/>{{{o.__('Trusted')}}}
|
||||
</label>
|
||||
<label class="btn btn--small {[ if (device.get('trusted') === -1) { ]} btn-primary active {[ } else { ]} btn-secondary {[ } ]}">
|
||||
<input type="radio" name="{{{device.get('id')}}}" value="-1"
|
||||
{[ if (device.get('trusted') === -1) { ]} checked="checked" {[ } ]}/>{{{o.__('Untrusted')}}}
|
||||
</label>
|
||||
</div>
|
||||
<span class="fingerprint">{{{o.utils.formatFingerprint(device.get('bundle').fingerprint)}}}</span>
|
||||
</form>
|
||||
</li>
|
||||
{[ } ]}
|
||||
{[ }); ]}
|
||||
{[ } ]}
|
||||
</ul>
|
||||
{[ } ]}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-warning" data-dismiss="modal">{{{o.__('Close')}}}</button>
|
||||
<button type="button" class="btn btn-info refresh-contact"><i class="fa fa-refresh"> </i>{{{o.__('Refresh')}}}</button>
|
||||
{[ if (o.allow_contact_removal && o.is_roster_contact) { ]}
|
||||
<button type="button" class="btn btn-danger remove-contact"><i class="far fa-trash-alt"> </i>{{{o.__('Remove as contact')}}}</button>
|
||||
{[ } ]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
93
src/templates/user_details_modal.js
Normal file
93
src/templates/user_details_modal.js
Normal file
@ -0,0 +1,93 @@
|
||||
import { __ } from '@converse/headless/i18n';
|
||||
import { html } from "lit-html";
|
||||
import avatar from "./avatar.js";
|
||||
import spinner from "./spinner.js";
|
||||
import { modal_close_button, modal_header_close_button } from "./buttons"
|
||||
|
||||
|
||||
const i18n_address = __('XMPP Address');
|
||||
const i18n_email = __('Email');
|
||||
const i18n_fingerprints = __('OMEMO Fingerprints');
|
||||
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 i18n_remove_contact = __('Remove as contact');
|
||||
const i18n_trusted = __('Trusted');
|
||||
const i18n_untrusted = __('Untrusted');
|
||||
const i18n_no_devices = __("No OMEMO-enabled devices found");
|
||||
|
||||
const avatar_data = {
|
||||
'alt_text': i18n_profile,
|
||||
'extra_classes': 'mb-3'
|
||||
}
|
||||
|
||||
|
||||
const device_fingerprint = (o) => {
|
||||
if (o.device.get('bundle') && o.device.get('bundle').fingerprint) {
|
||||
return html`
|
||||
<li class="list-group-item">
|
||||
<form class="fingerprint-trust">
|
||||
<div class="btn-group btn-group-toggle">
|
||||
<label class="btn btn--small ${(o.device.get('trusted') !== -1) ? 'btn-primary active' : 'btn-secondary'}">
|
||||
<input type="radio" name="${o.device.get('id')}" value="1" ?checked=${o.device.get('trusted') !== -1}>${i18n_trusted}
|
||||
</label>
|
||||
<label class="btn btn--small ${(o.device.get('trusted') !== -1) ? 'btn-primary active' : 'btn-secondary'}">
|
||||
<input type="radio" name="${o.device.get('id')}" value="-1" ?checked=${o.device.get('trusted') === -1}>${i18n_untrusted}
|
||||
</label>
|
||||
</div>
|
||||
<span class="fingerprint">${o.utils.formatFingerprint(o.device.get('bundle').fingerprint)}</span>
|
||||
</form>
|
||||
</li>
|
||||
`;
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const fingerprints = (o) => {
|
||||
const devices = o.view.devicelist.devices;
|
||||
return html`
|
||||
<hr/>
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item active">${i18n_fingerprints}</li>
|
||||
${ devices.length ?
|
||||
devices.map(device => device_fingerprint(Object.assign({device}, o))) :
|
||||
html`<li class="list-group-item"> ${i18n_no_devices} </li>` }
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
const remove_button = html`<button type="button" class="btn btn-danger remove-contact"><i class="far fa-trash-alt"> </i>${i18n_remove_contact}</button>`;
|
||||
|
||||
|
||||
export default (o) => 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 ? avatar(Object.assign(avatar_data, o)) : '' }
|
||||
${ 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>` : '' }
|
||||
|
||||
${ (o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse)) ? fingerprints(o) : '' }
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
${modal_close_button}
|
||||
<button type="button" class="btn btn-info refresh-contact"><i class="fa fa-refresh"> </i>${i18n_refresh}</button>
|
||||
${ (o.allow_contact_removal && o.is_roster_contact) ? remove_button : '' }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -6,6 +6,7 @@
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Converse.js: A free chat client for your website" />
|
||||
<script src="3rdparty/libsignal-protocol.js"></script>
|
||||
<link rel="manifest" href="./manifest.json">
|
||||
<link rel="shortcut icon" type="image/ico" href="favicon.ico"/>
|
||||
</head>
|
||||
|
Loading…
Reference in New Issue
Block a user