From c9084e4ed5118abf8fa55bb543c16b039dcc4820 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Wed, 15 Apr 2020 09:13:50 +0200 Subject: [PATCH] muc-views: Present challenge to `destroy` confirmation And also allow the user to specify the JID to where the conversation has moved to. --- spec/muc.js | 71 ++++++++++++++++++++++++----- src/converse-muc-views.js | 94 ++++++++++++++++++++++----------------- 2 files changed, 115 insertions(+), 50 deletions(-) diff --git a/spec/muc.js b/spec/muc.js index 61155396b..fefa3f7ab 100644 --- a/spec/muc.js +++ b/spec/muc.js @@ -3836,27 +3836,43 @@ async function (done, _converse) { const muc_jid = 'lounge@montague.lit'; + const new_muc_jid = 'foyer@montague.lit'; await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo'); - const view = _converse.api.chatviews.get(muc_jid); - spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); - const textarea = view.el.querySelector('.chat-textarea'); - textarea.value = '/destroy bored'; + let view = _converse.api.chatviews.get(muc_jid); + spyOn(_converse.api, 'confirm').and.callThrough(); + let textarea = view.el.querySelector('.chat-textarea'); + textarea.value = '/destroy'; view.onFormSubmitted(new Event('submit')); + let modal = await u.waitUntil(() => document.querySelector('.modal-dialog')); + await u.waitUntil(() => u.isVisible(modal)); - const sent_IQs = _converse.connection.IQ_stanzas; - const sent_IQ = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector('destroy')).pop()); + let challenge_el = modal.querySelector('[name="challenge"]'); + challenge_el.value = muc_jid+'e'; + const reason_el = modal.querySelector('[name="reason"]'); + reason_el.value = 'Moved to a new location'; + const newjid_el = modal.querySelector('[name="newjid"]'); + newjid_el.value = new_muc_jid; + let submit = modal.querySelector('[type="submit"]'); + submit.click(); + expect(u.isVisible(modal)).toBeTruthy(); + expect(u.hasClass('error', challenge_el)).toBeTruthy(); + challenge_el.value = muc_jid; + submit.click(); + + let sent_IQs = _converse.connection.IQ_stanzas; + let sent_IQ = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector('destroy')).pop()); expect(Strophe.serialize(sent_IQ)).toBe( - ``+ + ``+ ``+ - ``+ + ``+ ``+ - `bored`+ + `Moved to a new location`+ ``+ ``+ ``+ ``); - const result_stanza = $iq({ + let result_stanza = $iq({ 'type': 'result', 'id': sent_IQ.getAttribute('id'), 'from': view.model.get('jid'), @@ -3868,6 +3884,41 @@ await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED)); await u.waitUntil(() => _converse.chatboxes.length === 1); expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object)); + + // Try again without reason or new JID + _converse.connection.IQ_stanzas = []; + sent_IQs = _converse.connection.IQ_stanzas; + await test_utils.openAndEnterChatRoom(_converse, new_muc_jid, 'romeo'); + view = _converse.api.chatviews.get(new_muc_jid); + textarea = view.el.querySelector('.chat-textarea'); + textarea.value = '/destroy'; + view.onFormSubmitted(new Event('submit')); + modal = await u.waitUntil(() => document.querySelector('.modal-dialog')); + await u.waitUntil(() => u.isVisible(modal)); + + challenge_el = modal.querySelector('[name="challenge"]'); + challenge_el.value = new_muc_jid; + submit = modal.querySelector('[type="submit"]'); + submit.click(); + + sent_IQ = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector('destroy')).pop()); + expect(Strophe.serialize(sent_IQ)).toBe( + ``+ + ``+ + ``+ + ``+ + ``); + + result_stanza = $iq({ + 'type': 'result', + 'id': sent_IQ.getAttribute('id'), + 'from': view.model.get('jid'), + 'to': _converse.connection.jid + }); + expect(_converse.chatboxes.length).toBe(2); + _converse.connection._dataRecv(test_utils.createRequest(result_stanza)); + await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED)); + await u.waitUntil(() => _converse.chatboxes.length === 1); done(); })); }); diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index 2074582fc..42c30d03a 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -1014,19 +1014,14 @@ converse.plugins.add('converse-muc-views', { if (_converse.show_retraction_warning) { messages[1] = retraction_warning; } - const result = await api.confirm(__('Confirm'), messages); - if (result) { - this.retractOwnMessage(message); - } + !!(await api.confirm(__('Confirm'), messages)) && this.retractOwnMessage(message); } else if (await message.mayBeModerated()) { if (message.get('sender') === 'me') { let messages = [__('Are you sure you want to retract this message?')]; if (_converse.show_retraction_warning) { messages = [messages[0], retraction_warning, messages[1]] } - if (await api.confirm(__('Confirm'), messages)) { - this.retractOtherMessage(message); - } + !!(await api.confirm(__('Confirm'), messages)) && this.retractOtherMessage(message); } else { let messages = [ __('You are about to retract this message.'), @@ -1040,9 +1035,7 @@ converse.plugins.add('converse-muc-views', { messages, __('Optional reason') ); - if (reason !== false) { - this.retractOtherMessage(message, reason); - } + (reason !== false) && this.retractOtherMessage(message, reason); } } else { const err_msg = __(`Sorry, you're not allowed to retract this message`); @@ -1158,31 +1151,6 @@ converse.plugins.add('converse-muc-views', { }); } - const conn_status = this.model.session.get('connection_status'); - if (conn_status === converse.ROOMSTATUS.ENTERED) { - const allowed_commands = this.getAllowedCommands(); - if (allowed_commands.includes('modtools')) { - buttons.push({ - 'i18n_text': __('Moderate'), - 'i18n_title': __('Moderate this groupchat'), - 'handler': () => this.showModeratorToolsModal(), - 'a_class': 'moderate-chatroom-button', - 'icon_class': 'fa-user-cog', - 'name': 'moderate' - }); - } - if (allowed_commands.includes('destroy')) { - buttons.push({ - 'i18n_text': __('Destroy'), - 'i18n_title': __('Remove this groupchat'), - 'handler': ev => this.destroy(ev), - 'a_class': 'destroy-chatroom-button', - 'icon_class': 'fa-trash', - 'name': 'destroy' - }); - } - } - if (this.model.invitesAllowed()) { buttons.push({ 'i18n_text': __('Invite'), @@ -1208,6 +1176,32 @@ converse.plugins.add('converse-muc-views', { }); } + + const conn_status = this.model.session.get('connection_status'); + if (conn_status === converse.ROOMSTATUS.ENTERED) { + const allowed_commands = this.getAllowedCommands(); + if (allowed_commands.includes('modtools')) { + buttons.push({ + 'i18n_text': __('Moderate'), + 'i18n_title': __('Moderate this groupchat'), + 'handler': () => this.showModeratorToolsModal(), + 'a_class': 'moderate-chatroom-button', + 'icon_class': 'fa-user-cog', + 'name': 'moderate' + }); + } + if (allowed_commands.includes('destroy')) { + buttons.push({ + 'i18n_text': __('Destroy'), + 'i18n_title': __('Remove this groupchat'), + 'handler': ev => this.destroy(ev), + 'a_class': 'destroy-chatroom-button', + 'icon_class': 'fa-trash', + 'name': 'destroy' + }); + } + } + if (!api.settings.get("singleton")) { buttons.push({ 'i18n_text': __('Leave'), @@ -1532,10 +1526,30 @@ converse.plugins.add('converse-muc-views', { } }, - async destroy (reason, new_jid) { - const message = [__('Are you sure you want to destroy this groupchat?')]; - if (await api.confirm(__('Confirm'), message)) { - return this.model.sendDestroyIQ(reason, new_jid).then(() => this.close()) + async destroy () { + const messages = [__('Are you sure you want to destroy this groupchat?')]; + let fields = [{ + 'name': 'challenge', + 'label': __('Please enter the XMPP address of this groupchat to confirm'), + 'challenge': this.model.get('jid'), + 'placeholder': __('name@example.org'), + 'required': true + }, { + 'name': 'reason', + 'label': __('Optional reason for destroying this groupchat'), + 'placeholder': __('Reason') + }, { + 'name': 'newjid', + 'label': __('Optional XMPP address for a new groupchat that replaces this one'), + 'placeholder': __('replacement@example.org') + }]; + try { + fields = await api.confirm(__('Confirm'), messages, fields); + const reason = fields.filter(f => f.name === 'reason').pop()?.value; + const newjid = fields.filter(f => f.name === 'newjid').pop()?.value; + return this.model.sendDestroyIQ(reason, newjid).then(() => this.close()) + } catch (e) { + log.error(e); } }, @@ -1583,7 +1597,7 @@ converse.plugins.add('converse-muc-views', { if (!this.verifyAffiliations(['owner'])) { break; } - this.destroy(args).catch(e => this.onCommandError(e)); + this.destroy().catch(e => this.onCommandError(e)); break; } case 'help': {