Add api for managing modals

Set passed-in properties on modal
This commit is contained in:
JC Brand 2020-12-02 18:37:32 +01:00
parent b18cc6bcc5
commit 1a161ad2c7
24 changed files with 168 additions and 136 deletions

View File

@ -24,6 +24,7 @@ module.exports = function(config) {
{ pattern: "node_modules/sinon/pkg/sinon.js", type: 'module' },
{ pattern: "spec/mock.js", type: 'module' },
{ pattern: "spec/user-details-modal.js", type: 'module' },
{ pattern: "spec/spoilers.js", type: 'module' },
{ pattern: "spec/emojis.js", type: 'module' },
{ pattern: "spec/muclist.js", type: 'module' },
@ -44,7 +45,6 @@ module.exports = function(config) {
{ pattern: "spec/controlbox.js", type: 'module' },
{ pattern: "spec/roster.js", type: 'module' },
{ pattern: "spec/chatbox.js", type: 'module' },
{ pattern: "spec/user-details-modal.js", type: 'module' },
{ pattern: "spec/messages.js", type: 'module' },
{ pattern: "spec/corrections.js", type: 'module' },
{ pattern: "spec/styling.js", type: 'module' },

View File

@ -154,8 +154,7 @@ describe("The Controlbox", function () {
await mock.openControlBox(_converse);
var cbview = _converse.chatboxviews.get('controlbox');
cbview.el.querySelector('.change-status').click()
var modal = _converse.xmppstatusview.status_modal;
const modal = _converse.api.modal.get('modal-status-change');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
const view = _converse.xmppstatusview;
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
@ -183,7 +182,7 @@ describe("The Controlbox", function () {
await mock.openControlBox(_converse);
const cbview = _converse.chatboxviews.get('controlbox');
cbview.el.querySelector('.change-status').click()
const modal = _converse.xmppstatusview.status_modal;
const modal = _converse.api.modal.get('modal-status-change');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
const view = _converse.xmppstatusview;
@ -219,7 +218,7 @@ describe("The 'Add Contact' widget", function () {
const cbview = _converse.chatboxviews.get('controlbox');
cbview.el.querySelector('.add-contact').click()
const modal = _converse.rosterview.add_contact_modal;
const modal = _converse.api.modal.get('add-contact-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
expect(modal.el.querySelector('form.add-xmpp-contact')).not.toBe(null);
@ -252,7 +251,7 @@ describe("The 'Add Contact' widget", function () {
mock.openControlBox(_converse);
const cbview = _converse.chatboxviews.get('controlbox');
cbview.el.querySelector('.add-contact').click()
const modal = _converse.rosterview.add_contact_modal;
const modal = _converse.api.modal.get('add-contact-modal');
expect(modal.jid_auto_complete).toBe(undefined);
expect(modal.name_auto_complete).toBe(undefined);
@ -299,7 +298,7 @@ describe("The 'Add Contact' widget", function () {
const cbview = _converse.chatboxviews.get('controlbox');
cbview.el.querySelector('.add-contact').click()
const modal = _converse.rosterview.add_contact_modal;
const modal = _converse.api.modal.get('add-contact-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
// We only have autocomplete for the name input
@ -369,7 +368,7 @@ describe("The 'Add Contact' widget", function () {
const cbview = _converse.chatboxviews.get('controlbox');
cbview.el.querySelector('.add-contact').click()
modal = _converse.rosterview.add_contact_modal;
modal = _converse.api.modal.get('add-contact-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
expect(modal.jid_auto_complete).toBe(undefined);

View File

@ -152,7 +152,7 @@ window.addEventListener('converse-loaded', () => {
const roomspanel = view.roomspanel;
roomspanel.el.querySelector('.show-add-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.add_room_modal;
const modal = _converse.api.modal.get('add-chatroom-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1500)
modal.el.querySelector('input[name="chatroom"]').value = jid;
if (nick) {

View File

@ -1,4 +1,4 @@
/*global mock */
/*global mock, converse */
const _ = converse.env._;
const $iq = converse.env.$iq;
@ -8,13 +8,13 @@ const Strophe = converse.env.Strophe;
const u = converse.env.utils;
async function openModtools (view) {
async function openModtools (_converse, view) {
const textarea = view.el.querySelector('.chat-textarea');
textarea.value = '/modtools';
const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, 'keyCode': 13 };
view.onKeyDown(enter);
await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
const modal = view.modtools_modal;
const modal = _converse.api.modal.get('converse-modtools-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
return modal;
}
@ -40,7 +40,7 @@ describe("The groupchat moderator tool", function () {
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 5), 1000);
const modal = await openModtools(view);
const modal = await openModtools(_converse, view);
let tab = modal.el.querySelector('#affiliations-tab');
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
@ -163,7 +163,7 @@ describe("The groupchat moderator tool", function () {
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
const modal = await openModtools(view);
const modal = await openModtools(_converse, view);
const select = modal.el.querySelector('.select-affiliation');
expect(select.value).toBe('owner');
select.value = 'member';
@ -270,7 +270,7 @@ describe("The groupchat moderator tool", function () {
view.onKeyDown(enter);
await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
const modal = view.modtools_modal;
const modal = _converse.api.modal.get('converse-modtools-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
const tab = modal.el.querySelector('#roles-tab');
@ -326,7 +326,7 @@ describe("The groupchat moderator tool", function () {
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 5));
const modal = await openModtools(view);
const modal = await openModtools(_converse, view);
const tab = modal.el.querySelector('#affiliations-tab');
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
@ -375,7 +375,7 @@ describe("The groupchat moderator tool", function () {
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 2));
const modal = await openModtools(view);
const modal = await openModtools(_converse, view);
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
@ -443,7 +443,7 @@ describe("The groupchat moderator tool", function () {
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 3));
const modal = await openModtools(view);
const modal = await openModtools(_converse, view);
const tab = modal.el.querySelector('#affiliations-tab');
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
@ -481,7 +481,7 @@ describe("The groupchat moderator tool", function () {
view.onKeyDown(enter);
await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
const modal = view.modtools_modal;
const modal = _converse.api.modal.get('converse-modtools-modal');
const occupant = view.model.occupants.findWhere({'jid': _converse.bare_jid});
expect(modal.getAssignableAffiliations(occupant)).toEqual(['owner', 'admin', 'member', 'outcast', 'none']);

View File

@ -1973,7 +1973,7 @@ describe("Groupchats", function () {
await u.waitUntil(() => view.el.querySelector('.open-invite-modal'));
view.el.querySelector('.open-invite-modal').click();
const modal = view.muc_invite_modal;
const modal = _converse.api.modal.get('muc-invite-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000)
expect(modal.el.querySelectorAll('#invitee_jids').length).toBe(1);
@ -2456,7 +2456,7 @@ describe("Groupchats", function () {
const info_el = view.el.querySelector(".show-room-details-modal");
info_el.click();
const modal = view.model.room_details_modal;
const modal = _converse.api.modal.get('room-details-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
let features_list = modal.el.querySelector('.features-list');
@ -4629,7 +4629,7 @@ describe("Groupchats", function () {
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-add-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.add_room_modal;
const modal = _converse.api.modal.get('add-chatroom-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000)
let label_name = modal.el.querySelector('label[for="chatroom"]');
@ -4670,7 +4670,7 @@ describe("Groupchats", function () {
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-add-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.add_room_modal;
const modal = _converse.api.modal.get('add-chatroom-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000)
const name_input = modal.el.querySelector('input[name="chatroom"]');
name_input.value = 'lounge@montague.lit';
@ -4693,7 +4693,7 @@ describe("Groupchats", function () {
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-add-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.add_room_modal;
const modal = _converse.api.modal.get('add-chatroom-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000)
const label_nick = modal.el.querySelector('label[for="nickname"]');
expect(label_nick.textContent.trim()).toBe('Nickname:');
@ -4712,7 +4712,7 @@ describe("Groupchats", function () {
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-add-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.add_room_modal;
const modal = _converse.api.modal.get('add-chatroom-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000)
const label_nick = modal.el.querySelector('label[for="nickname"]');
expect(label_nick.textContent.trim()).toBe('Nickname:');
@ -4729,7 +4729,7 @@ describe("Groupchats", function () {
await mock.openControlBox(_converse);
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-add-muc-modal').click();
const modal = roomspanel.add_room_modal;
const modal = _converse.api.modal.get('add-chatroom-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000)
expect(modal.el.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
@ -4769,7 +4769,7 @@ describe("Groupchats", function () {
await mock.openControlBox(_converse);
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-add-muc-modal').click();
const modal = roomspanel.add_room_modal;
const modal = _converse.api.modal.get('add-chatroom-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000)
expect(modal.el.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
@ -4812,7 +4812,7 @@ describe("Groupchats", function () {
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-list-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.muc_list_modal;
const modal = _converse.api.modal.get('list-chatrooms-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
@ -4889,7 +4889,7 @@ describe("Groupchats", function () {
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-list-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.muc_list_modal;
const modal = _converse.api.modal.get('list-chatrooms-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
const server_input = modal.el.querySelector('input[name="server"]');
expect(server_input.value).toBe('muc.example.org');
@ -4906,7 +4906,7 @@ describe("Groupchats", function () {
const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.show-list-muc-modal').click();
mock.closeControlBox(_converse);
const modal = roomspanel.muc_list_modal;
const modal = _converse.api.modal.get('list-chatrooms-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called

View File

@ -204,7 +204,7 @@ describe("A groupchat shown in the groupchats list", function () {
const info_el = _converse.rooms_list_view.el.querySelector(".room-info");
info_el.click();
const modal = view.model.room_details_modal;
const modal = _converse.api.modal.get('room-details-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
let els = modal.el.querySelectorAll('p.room-info');
expect(els[0].textContent).toBe("Name: A Dark Cave")

View File

@ -1463,7 +1463,7 @@ describe("The OMEMO module", function() {
const view = _converse.chatboxviews.get(contact_jid);
const show_modal_button = view.el.querySelector('.show-user-details-modal');
show_modal_button.click();
const modal = view.user_details_modal;
const modal = _converse.api.modal.get('user-details-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
expect(Strophe.serialize(iq_stanza)).toBe(

View File

@ -1,4 +1,5 @@
/*global mock */
/*global mock, converse */
// See: https://xmpp.org/rfcs/rfc3921.html
const original_timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
@ -76,7 +77,7 @@ describe("A sent presence stanza", function () {
const cbview = _converse.chatboxviews.get('controlbox');
const change_status_el = await u.waitUntil(() => cbview.el.querySelector('.change-status'));
change_status_el.click()
const modal = _converse.xmppstatusview.status_modal;
let modal = _converse.api.modal.get('modal-status-change');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
const msg = 'My custom status';
modal.el.querySelector('input[name="status_message"]').value = msg;
@ -90,10 +91,11 @@ describe("A sent presence stanza", function () {
`<priority>0</priority>`+
`<c hash="sha-1" node="https://conversejs.org" ver="PxXfr6uz8ClMWIga0OB/MhKNH/M=" xmlns="http://jabber.org/protocol/caps"/>`+
`</presence>`)
await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
await u.waitUntil(() => !u.isVisible(modal.el));
cbview.el.querySelector('.change-status').click()
modal = _converse.api.modal.get('modal-status-change');
await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "false", 1000);
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
modal.el.querySelector('[type="submit"]').click();

View File

@ -66,7 +66,7 @@ describe("The Protocol", function () {
});
cbview.el.querySelector('.add-contact').click()
const modal = _converse.rosterview.add_contact_modal;
const modal = _converse.api.modal.get('add-contact-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
spyOn(modal, "addContactFromForm").and.callThrough();
modal.delegateEvents();

View File

@ -18,7 +18,7 @@ describe("The User Details Modal", function () {
const view = _converse.chatboxviews.get(contact_jid);
let show_modal_button = view.el.querySelector('.show-user-details-modal');
show_modal_button.click();
const modal = view.user_details_modal;
const modal = _converse.api.modal.get('user-details-modal');
await u.waitUntil(() => u.isVisible(modal.el), 1000);
spyOn(window, 'confirm').and.returnValue(true);
spyOn(view.model.contact, 'removeFromRoster').and.callFake(callback => callback());
@ -45,7 +45,7 @@ describe("The User Details Modal", function () {
const view = _converse.chatboxviews.get(contact_jid);
let show_modal_button = view.el.querySelector('.show-user-details-modal');
show_modal_button.click();
const modal = view.user_details_modal;
let modal = _converse.api.modal.get('user-details-modal');
await u.waitUntil(() => u.isVisible(modal.el), 2000);
spyOn(window, 'confirm').and.returnValue(true);
@ -53,7 +53,7 @@ describe("The User Details Modal", function () {
let remove_contact_button = modal.el.querySelector('button.remove-contact');
expect(u.isVisible(remove_contact_button)).toBeTruthy();
remove_contact_button.click();
await u.waitUntil(() => !u.isVisible(modal.el))
await u.waitUntil(() => u.isVisible(document.querySelector('.alert-danger')), 2000);
const header = document.querySelector('.alert-danger .modal-title');
@ -63,6 +63,7 @@ describe("The User Details Modal", function () {
document.querySelector('.alert-danger button.close').click();
show_modal_button = view.el.querySelector('.show-user-details-modal');
show_modal_button.click();
modal = _converse.api.modal.get('user-details-modal');
await u.waitUntil(() => u.isVisible(modal.el), 2000)
show_modal_button = view.el.querySelector('.show-user-details-modal');

View File

@ -15,14 +15,9 @@ export default class MessageBody extends CustomElement {
}
}
showImageModal (ev) {
showImageModal (ev) { // eslint-disable-line class-methods-use-this
ev.preventDefault();
if (this.image_modal === undefined) {
this.image_modal = new ImageModal();
}
this.image_modal.src = ev.target.src;
this.image_modal.render();
this.image_modal.show(ev);
api.modal.create(ImageModal, {'src': ev.target.src}, ev).show(ev);
}
render () {

View File

@ -238,10 +238,7 @@ export const ChatBoxView = View.extend({
showUserDetailsModal (ev) {
ev.preventDefault();
if (this.user_details_modal === undefined) {
this.user_details_modal = new UserDetailsModal({model: this.model});
}
this.user_details_modal.show(ev);
api.modal.show(UserDetailsModal, {model: this.model}, ev);
},
onDragOver (evt) {

View File

@ -7,15 +7,77 @@ import Alert from './modals/alert.js';
import BootstrapModal from './modals/base.js';
import Confirm from './modals/confirm.js';
import { Model } from '@converse/skeletor/src/model.js';
import { _converse, converse } from "@converse/headless/converse-core";
import { _converse, api, converse } from "@converse/headless/converse-core";
converse.env.BootstrapModal = BootstrapModal; // expose to plugins
let alert;
let modals = [];
const modal_api = {
/**
* API namespace for methods relating to modals
* @namespace _converse.api.modal
* @memberOf _converse.api
*/
modal: {
/**
* Shows a modal of type `ModalClass` to the user.
* Will create a new instance of that class if an existing one isn't
* found.
* @param { Class } ModalClass
* @param { [Object] } properties - Optional properties that will be
* set on a newly created modal instance (if no pre-existing modal was
* found).
* @param { [Event] } event - The DOM event that causes the modal to be shown.
*/
show (ModalClass, properties, ev) {
const modal = this.get(ModalClass.id) || this.create(ModalClass, properties);
modal.show(ev);
return modal;
},
/**
* Return a modal with the passed-in identifier, if it exists.
* @param { String } id
*/
get (id) {
return modals.filter(m => m.id == id).pop();
},
/**
* Create a modal of the passed-in type.
* @param { Class } ModalClass
* @param { [Object] } properties - Optional properties that will be
* set on the modal instance.
*/
create (ModalClass, properties) {
const modal = new ModalClass(properties);
modals.push(modal);
return modal;
},
/**
* Remove a particular modal
* @param { View } modal
*/
remove (modal) {
modals = modals.filter(m => m !== modal);
modal.remove();
},
/**
* Remove all modals
*/
removeAll () {
modals.forEach(m => m.remove());
modals = [];
}
},
/**
* Show a confirm modal to the user.
* @method _converse.api.confirm
@ -101,22 +163,13 @@ const modal_api = {
level = 'alert-warning';
}
if (alert === undefined) {
const model = new Model({
'title': title,
'messages': messages,
'level': level,
'type': 'alert'
})
alert = new Alert({model});
} else {
alert.model.set({
'title': title,
'messages': messages,
'level': level
});
}
alert.show();
const model = new Model({
'title': title,
'messages': messages,
'level': level,
'type': 'alert'
})
api.modal.show(Alert, {model});
}
}
@ -124,12 +177,15 @@ const modal_api = {
converse.plugins.add('converse-modal', {
initialize () {
_converse.api.listen.on('disconnect', () => {
api.listen.on('disconnect', () => {
const container = document.querySelector("#converse-modals");
if (container) {
container.innerHTML = '';
}
});
api.listen.on('clearSession', () => api.modal.removeAll());
Object.assign(_converse.api, modal_api);
}
});

View File

@ -501,29 +501,24 @@ export const ChatRoomView = ChatBoxView.extend({
if (!this.verifyRoles(['moderator'])) {
return;
}
if (typeof this.model.modtools_modal === 'undefined') {
const model = new Model({'affiliation': affiliation});
this.modtools_modal = new ModeratorToolsModal({model, _converse, 'chatroomview': this});
let modal = api.modal.get(ModeratorToolsModal.id);
if (modal) {
modal.model.set('affiliation', affiliation);
} else {
this.modtools_modal.set('affiliation', affiliation);
const model = new Model({'affiliation': affiliation});
modal = api.modal.create(ModeratorToolsModal, {model, _converse, 'chatroomview': this});
}
this.modtools_modal.show();
modal.show();
},
showRoomDetailsModal (ev) {
ev.preventDefault();
if (this.model.room_details_modal === undefined) {
this.model.room_details_modal = new RoomDetailsModal({'model': this.model});
}
this.model.room_details_modal.show(ev);
api.modal.show(RoomDetailsModal, {'model': this.model}, ev);
},
showOccupantDetailsModal (ev, message) {
ev.preventDefault();
if (this.model.occupant_modal === undefined) {
this.model.occupant_modal = new OccupantModal({'model': message.occupant});
}
this.model.occupant_modal.show(ev);
api.modal.show(OccupantModal, {'model': message.occupant}, ev);
},
showChatStateNotification (message) {
@ -679,12 +674,7 @@ export const ChatRoomView = ChatBoxView.extend({
showInviteModal (ev) {
ev.preventDefault();
if (this.muc_invite_modal === undefined) {
this.muc_invite_modal = new MUCInviteModal({'model': new Model()});
// TODO: remove once we have API for sending direct invite
this.muc_invite_modal.chatroomview = this;
}
this.muc_invite_modal.show(ev);
api.modal.show(MUCInviteModal, {'model': new Model(), 'chatroomview': this}, ev);
},
@ -1365,17 +1355,11 @@ export const RoomsPanel = View.extend({
},
showAddRoomModal (ev) {
if (this.add_room_modal === undefined) {
this.add_room_modal = new AddMUCModal({'model': this.model});
}
this.add_room_modal.show(ev);
api.modal.show(AddMUCModal, {'model': this.model}, ev);
},
showMUCListModal(ev) {
if (this.muc_list_modal === undefined) {
this.muc_list_modal = new MUCListModal({'model': this.model});
}
this.muc_list_modal.show(ev);
api.modal.show(MUCListModal, {'model': this.model}, ev);
}
});

View File

@ -61,26 +61,17 @@ converse.plugins.add('converse-profile', {
showProfileModal (ev) {
ev.preventDefault();
if (this.profile_modal === undefined) {
this.profile_modal = new _converse.ProfileModal({model: this.model});
}
this.profile_modal.show(ev);
api.modal.show(_converse.ProfileModal, {model: this.model}, ev);
},
showStatusChangeModal (ev) {
ev.preventDefault();
if (this.status_modal === undefined) {
this.status_modal = new _converse.ChatStatusModal({model: this.model});
}
this.status_modal.show(ev);
api.modal.show(_converse.ChatStatusModal, {model: this.model}, ev);
},
showUserSettingsModal(ev) {
ev.preventDefault();
if (this.user_settings_modal === undefined) {
this.user_settings_modal = new UserSettingsModal({model: this.model, _converse});
}
this.user_settings_modal.show(ev);
api.modal.show(UserSettingsModal, {model: this.model, _converse}, ev);
},
logOut (ev) {

View File

@ -109,10 +109,7 @@ converse.plugins.add('converse-roomslist', {
const jid = ev.target.getAttribute('data-room-jid');
const room = _converse.chatboxes.get(jid);
ev.preventDefault();
if (room.room_details_modal === undefined) {
room.room_details_modal = new RoomDetailsModal({'model': room});
}
room.room_details_modal.show(ev);
api.modal.show(RoomDetailsModal, {'model': room}, ev);
},
async openRoom (ev) {
@ -183,4 +180,3 @@ converse.plugins.add('converse-roomslist', {
api.listen.on('reconnected', initRoomsListView);
}
});

View File

@ -629,10 +629,7 @@ converse.plugins.add('converse-rosterview', {
},
showAddContactModal (ev) {
if (this.add_contact_modal === undefined) {
this.add_contact_modal = new _converse.AddContactModal({'model': new Model()});
}
this.add_contact_modal.show(ev);
api.modal.show(_converse.AddContactModal, {'model': new Model()}, ev);
},
createRosterFilter () {

View File

@ -4,6 +4,7 @@ import { __ } from '../i18n';
const Alert = BootstrapModal.extend({
id: 'alert-modal',
initialize () {
BootstrapModal.prototype.initialize.apply(this, arguments);

View File

@ -2,7 +2,7 @@ import bootstrap from "bootstrap.native";
import log from "@converse/headless/log";
import tpl_alert_component from "templates/alert.js";
import { View } from '@converse/skeletor/src/view.js';
import { _converse, converse } from "@converse/headless/converse-core";
import { _converse, api, converse } from "@converse/headless/converse-core";
import { render } from 'lit-html';
const { sizzle } = converse.env;
@ -16,9 +16,15 @@ const BaseModal = View.extend({
'click .nav-item .nav-link': 'switchTab'
},
initialize () {
initialize (options) {
if (!this.id) {
throw new Error("Each modal class must have a unique id attribute");
}
this.render()
// Allow properties to be set via passed in options
Object.assign(this, options);
this.el.setAttribute('tabindex', '-1');
this.el.setAttribute('role', 'dialog');
this.el.setAttribute('aria-hidden', 'true');
@ -36,7 +42,7 @@ const BaseModal = View.extend({
onHide () {
u.removeClass('selected', this.trigger_el);
!this.persistent && this.remove();
!this.persistent && api.modal.remove(this);
},
insertIntoDOM () {

View File

@ -6,6 +6,7 @@ const u = converse.env.utils;
const Confirm = BootstrapModal.extend({
id: 'confirm-modal',
events: {
'submit .confirm': 'onConfimation'
},

View File

@ -3,6 +3,8 @@ import tpl_image_modal from "./templates/image.js";
export default BootstrapModal.extend({
id: 'image-modal',
toHTML () {
return tpl_image_modal({
'src': this.src,

View File

@ -3,7 +3,7 @@ import tpl_message_versions_modal from "./templates/message-versions.js";
export default BootstrapModal.extend({
id: "message-versions-modal",
toHTML () {
return tpl_message_versions_modal(this.model.toJSON());
}

View File

@ -12,6 +12,7 @@ let _converse;
export default BootstrapModal.extend({
id: "converse-modtools-modal",
persistent: true,
initialize (attrs) {

View File

@ -7,7 +7,22 @@ import { _converse, api, converse } from "@converse/headless/converse-core";
const u = converse.env.utils;
function removeContact (contact) {
contact.removeFromRoster(
() => contact.destroy(),
(e) => {
e && log.error(e);
api.alert('error', __('Error'), [
__('Sorry, there was an error while trying to remove %1$s as a contact.',
contact.getDisplayName())
]);
}
);
}
const UserDetailsModal = BootstrapModal.extend({
id: 'user-details-modal',
persistent: true,
events: {
@ -74,23 +89,11 @@ const UserDetailsModal = BootstrapModal.extend({
if (!api.settings.get('allow_contact_removal')) { return; }
const result = confirm(__("Are you sure you want to remove this contact?"));
if (result === true) {
this.modal.hide();
// XXX: This is annoying but necessary to get tests to pass.
// The `dismissHandler` in bootstrap.native tries to
// XXX: The `dismissHandler` in bootstrap.native tries to
// reference the remove button after it's been cleared from
// the DOM, so we delay removing the contact to give it time.
setTimeout(() => {
this.model.contact.removeFromRoster(
() => this.model.contact.destroy(),
(err) => {
log.error(err);
api.alert('error', __('Error'), [
__('Sorry, there was an error while trying to remove %1$s as a contact.',
this.model.contact.getDisplayName())
]);
}
);
}, 1);
setTimeout(() => removeContact(this.model.contact), 1);
this.modal.hide();
}
},
});