From 505416a59eba1eab9cdfdc5d4277f589dbe244c3 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Sat, 5 Feb 2022 22:47:05 +0100 Subject: [PATCH] Let bookmarks be created/removed via a modal --- src/plugins/bookmark-views/form.js | 13 ++++++- src/plugins/bookmark-views/mixins.js | 13 +++---- src/plugins/bookmark-views/modal.js | 19 +++++++++ src/plugins/bookmark-views/templates/form.js | 14 ++++--- src/plugins/bookmark-views/templates/modal.js | 19 +++++++++ src/plugins/bookmark-views/tests/bookmarks.js | 39 ++++++++++--------- src/plugins/bookmark-views/utils.js | 12 +++--- src/plugins/muc-views/index.js | 1 - .../muc-views/modals/moderator-tools.js | 6 +-- src/plugins/muc-views/utils.js | 2 - webpack.html | 3 +- webpack/webpack.serve.js | 2 +- 12 files changed, 95 insertions(+), 48 deletions(-) create mode 100644 src/plugins/bookmark-views/modal.js create mode 100644 src/plugins/bookmark-views/templates/modal.js diff --git a/src/plugins/bookmark-views/form.js b/src/plugins/bookmark-views/form.js index 02b87ab51..e418e0aa6 100644 --- a/src/plugins/bookmark-views/form.js +++ b/src/plugins/bookmark-views/form.js @@ -14,12 +14,14 @@ class MUCBookmarkForm extends CustomElement { connectedCallback () { super.connectedCallback(); this.model = _converse.chatboxes.get(this.jid); + this.bookmark = _converse.bookmarks.findWhere({ 'jid': this.model.get('jid') }); } render () { return tpl_muc_bookmark_form( Object.assign(this.model.toJSON(), { - 'onCancel': ev => this.closeBookmarkForm(ev), + 'bookmark': this.bookmark, + 'onCancel': ev => this.removeBookmark(ev), 'onSubmit': ev => this.onBookmarkFormSubmitted(ev) }) ); @@ -36,9 +38,16 @@ class MUCBookmarkForm extends CustomElement { this.closeBookmarkForm(ev); } + removeBookmark (ev) { + this.bookmark?.destroy(); + this.closeBookmarkForm(ev); + } + closeBookmarkForm (ev) { ev.preventDefault(); - this.model.session.save('view', null); + const evt = document.createEvent('Event'); + evt.initEvent('hide.bs.modal', true, true); + this.dispatchEvent(evt); } } diff --git a/src/plugins/bookmark-views/mixins.js b/src/plugins/bookmark-views/mixins.js index 63d2f006e..362c1434f 100644 --- a/src/plugins/bookmark-views/mixins.js +++ b/src/plugins/bookmark-views/mixins.js @@ -1,4 +1,5 @@ -import { _converse, converse } from '@converse/headless/core'; +import MUCBookmarkFormModal from './modal.js'; +import { _converse, api, converse } from '@converse/headless/core'; const { u } = converse.env; @@ -30,13 +31,9 @@ export const bookmarkableChatRoomView = { u.showElement(this.bookmark_form.el); }, - toggleBookmark (ev) { + showBookmarkModal(ev) { ev?.preventDefault(); - const models = _converse.bookmarks.where({ 'jid': this.model.get('jid') }); - if (!models.length) { - this.model.session.set('view', converse.MUC.VIEWS.BOOKMARK); - } else { - models.forEach(model => model.destroy()); - } + const jid = this.model.get('jid'); + api.modal.show(MUCBookmarkFormModal, { jid }, ev); } }; diff --git a/src/plugins/bookmark-views/modal.js b/src/plugins/bookmark-views/modal.js new file mode 100644 index 000000000..c9b6c71bd --- /dev/null +++ b/src/plugins/bookmark-views/modal.js @@ -0,0 +1,19 @@ +import './form.js'; +import BaseModal from "plugins/modal/base.js"; +import tpl_modal from './templates/modal.js'; + +const MUCBookmarkFormModal = BaseModal.extend({ + id: "converse-bookmark-modal", + + initialize (attrs) { + this.jid = attrs.jid; + this.affiliation = attrs.affiliation; + BaseModal.prototype.initialize.apply(this, arguments); + }, + + toHTML () { + return tpl_modal(this); + } +}); + +export default MUCBookmarkFormModal; diff --git a/src/plugins/bookmark-views/templates/form.js b/src/plugins/bookmark-views/templates/form.js index 9d96721b9..e19311dae 100644 --- a/src/plugins/bookmark-views/templates/form.js +++ b/src/plugins/bookmark-views/templates/form.js @@ -3,22 +3,24 @@ import { __ } from 'i18n'; export default (o) => { - const i18n_heading = __('Bookmark this groupchat'); + const name = o.bookmark?.get('name') ?? o.name; + const nick = o.bookmark?.get('nick') ?? o.nick; + const i18n_heading = __('Bookmark for "%1$s"', name); const i18n_autojoin = __('Would you like this groupchat to be automatically joined upon startup?'); - const i18n_cancel = __('Cancel'); + const i18n_remove = __('Remove'); const i18n_name = __('The name for this bookmark:'); const i18n_nick = __('What should your nickname for this groupchat be?'); - const i18n_submit = __('Save'); + const i18n_submit = o.bookmark ? __('Update') : __('Save'); return html`
${i18n_heading}
- +
- +
@@ -26,7 +28,7 @@ export default (o) => {
- + ${o.bookmark ? html`` : '' }
`; diff --git a/src/plugins/bookmark-views/templates/modal.js b/src/plugins/bookmark-views/templates/modal.js new file mode 100644 index 000000000..46f8cd6e1 --- /dev/null +++ b/src/plugins/bookmark-views/templates/modal.js @@ -0,0 +1,19 @@ +import { __ } from 'i18n'; +import { html } from "lit"; +import { modal_header_close_button } from "plugins/modal/templates/buttons.js" + +export default (o) => { + const i18n_moderator_tools = __('Bookmark'); + return html` + `; +} diff --git a/src/plugins/bookmark-views/tests/bookmarks.js b/src/plugins/bookmark-views/tests/bookmarks.js index 5bc0177d0..c394e7cec 100644 --- a/src/plugins/bookmark-views/tests/bookmarks.js +++ b/src/plugins/bookmark-views/tests/bookmarks.js @@ -5,8 +5,7 @@ const { Strophe, u, sizzle, $iq } = converse.env; describe("A chat room", function () { - it("can be bookmarked", mock.initConverse( - ['chatBoxesFetched'], {}, async function (_converse) { + it("can be bookmarked", mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => { await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilDiscoConfirmed( @@ -27,20 +26,13 @@ describe("A chat room", function () { await mock.returnMemberLists(_converse, muc_jid, [], ['member', 'admin', 'owner']); await u.waitUntil(() => view.querySelector('.toggle-bookmark') !== null); + const toggle = view.querySelector('.toggle-bookmark'); expect(toggle.title).toBe('Bookmark this groupchat'); toggle.click(); - const cancel_button = await u.waitUntil(() => view.querySelector('.button-cancel')); - expect(view.model.session.get('view')).toBe('bookmark-form'); - cancel_button.click(); - - await u.waitUntil(() => view.model.session.get('view') === null); - - expect(u.hasClass('on-button', toggle), false); - expect(toggle.title).toBe('Bookmark this groupchat'); - - toggle.click(); + const modal = _converse.api.modal.get('converse-bookmark-modal'); + await u.waitUntil(() => u.isVisible(modal.el), 1000); /* Client uploads data: * -------------------- @@ -74,13 +66,13 @@ describe("A chat room", function () { * */ expect(view.model.get('bookmarked')).toBeFalsy(); - const form = await u.waitUntil(() => view.querySelector('.chatroom-form')); + const form = await u.waitUntil(() => modal.el.querySelector('.chatroom-form')); form.querySelector('input[name="name"]').value = 'Play's the Thing'; form.querySelector('input[name="autojoin"]').checked = 'checked'; form.querySelector('input[name="nick"]').value = 'JC'; const IQ_stanzas = _converse.connection.IQ_stanzas; - view.querySelector('converse-muc-bookmark-form .btn-primary').click(); + modal.el.querySelector('converse-muc-bookmark-form .btn-primary').click(); const sent_stanza = await u.waitUntil( () => IQ_stanzas.filter(s => sizzle('iq publish[node="storage:bookmarks"]', s).length).pop()); @@ -227,7 +219,6 @@ describe("A chat room", function () { })); it("can be unbookmarked", mock.initConverse([], {}, async function (_converse) { - const { u, Strophe } = converse.env; await mock.waitForRoster(_converse, 'current', 0); await mock.waitUntilBookmarksReturned(_converse); @@ -240,14 +231,14 @@ describe("A chat room", function () { const view = _converse.chatboxviews.get(muc_jid); await u.waitUntil(() => view.querySelector('.toggle-bookmark')); - spyOn(view, 'toggleBookmark').and.callThrough(); + spyOn(view, 'showBookmarkModal').and.callThrough(); spyOn(_converse.bookmarks, 'sendBookmarkStanza').and.callThrough(); _converse.bookmarks.create({ 'jid': view.model.get('jid'), 'autojoin': false, 'name': 'The Play', - 'nick': ' Othello' + 'nick': 'Othello' }); expect(_converse.bookmarks.length).toBe(1); @@ -257,7 +248,19 @@ describe("A chat room", function () { spyOn(_converse.connection, 'getUniqueId').and.callThrough(); const bookmark_icon = view.querySelector('.toggle-bookmark'); bookmark_icon.click(); - expect(view.toggleBookmark).toHaveBeenCalled(); + expect(view.showBookmarkModal).toHaveBeenCalled(); + + const modal = _converse.api.modal.get('converse-bookmark-modal'); + await u.waitUntil(() => u.isVisible(modal.el), 1000); + const form = await u.waitUntil(() => modal.el.querySelector('.chatroom-form')); + + expect(form.querySelector('input[name="name"]').value).toBe('The Play'); + expect(form.querySelector('input[name="autojoin"]').checked).toBeFalsy(); + expect(form.querySelector('input[name="nick"]').value).toBe('Othello'); + + // Remove the bookmark + modal.el.querySelector('.button-remove').click(); + await u.waitUntil(() => view.querySelector('.chatbox-title__text .fa-bookmark') === null); expect(_converse.bookmarks.length).toBe(0); diff --git a/src/plugins/bookmark-views/utils.js b/src/plugins/bookmark-views/utils.js index d14d0e63a..7fcbe587a 100644 --- a/src/plugins/bookmark-views/utils.js +++ b/src/plugins/bookmark-views/utils.js @@ -1,8 +1,9 @@ -import { checkBookmarksSupport } from '@converse/headless/plugins/bookmarks/utils'; +import MUCBookmarkFormModal from './modal.js'; import invokeMap from 'lodash-es/invokeMap'; import { Model } from '@converse/skeletor/src/model.js'; -import { _converse, api, converse } from '@converse/headless/core'; import { __ } from 'i18n'; +import { _converse, api, converse } from '@converse/headless/core'; +import { checkBookmarksSupport } from '@converse/headless/plugins/bookmarks/utils'; export function getHeadingButtons (view, buttons) { @@ -11,7 +12,7 @@ export function getHeadingButtons (view, buttons) { const data = { 'i18n_title': bookmarked ? __('Unbookmark this groupchat') : __('Bookmark this groupchat'), 'i18n_text': bookmarked ? __('Unbookmark') : __('Bookmark'), - 'handler': ev => view.toggleBookmark(ev), + 'handler': ev => view.showBookmarkModal(ev), 'a_class': 'toggle-bookmark', 'icon_class': 'fa-bookmark', 'name': 'bookmark' @@ -33,11 +34,10 @@ export function removeBookmarkViaEvent (ev) { } } -export async function addBookmarkViaEvent (ev) { +export function addBookmarkViaEvent (ev) { ev.preventDefault(); const jid = ev.target.getAttribute('data-room-jid'); - const room = await api.rooms.open(jid, { 'bring_to_foreground': true }); - room.session.save('view', converse.MUC.VIEWS.BOOKMARK); + api.modal.show(MUCBookmarkFormModal, { jid }, ev); } diff --git a/src/plugins/muc-views/index.js b/src/plugins/muc-views/index.js index 5c1e233a8..c39aea5f6 100644 --- a/src/plugins/muc-views/index.js +++ b/src/plugins/muc-views/index.js @@ -14,7 +14,6 @@ import './styles/index.scss'; converse.MUC.VIEWS = { CONFIG: 'config-form', - BOOKMARK: 'bookmark-form' } converse.plugins.add('converse-muc-views', { diff --git a/src/plugins/muc-views/modals/moderator-tools.js b/src/plugins/muc-views/modals/moderator-tools.js index fff053cfd..5a18fe9e3 100644 --- a/src/plugins/muc-views/modals/moderator-tools.js +++ b/src/plugins/muc-views/modals/moderator-tools.js @@ -1,15 +1,15 @@ import '../modtools.js'; -import BootstrapModal from "plugins/modal/base.js"; +import BaseModal from "plugins/modal/base.js"; import tpl_moderator_tools from './templates/moderator-tools.js'; -const ModeratorToolsModal = BootstrapModal.extend({ +const ModeratorToolsModal = BaseModal.extend({ id: "converse-modtools-modal", persistent: true, initialize (attrs) { this.jid = attrs.jid; this.affiliation = attrs.affiliation; - BootstrapModal.prototype.initialize.apply(this, arguments); + BaseModal.prototype.initialize.apply(this, arguments); }, toHTML () { diff --git a/src/plugins/muc-views/utils.js b/src/plugins/muc-views/utils.js index 86369e58d..90fe10e59 100644 --- a/src/plugins/muc-views/utils.js +++ b/src/plugins/muc-views/utils.js @@ -124,8 +124,6 @@ export function getChatRoomBodyTemplate (o) { if (view === converse.MUC.VIEWS.CONFIG) { return html``; - } else if (view === converse.MUC.VIEWS.BOOKMARK) { - return html``; } else { return html` ${ conn_status == RS.PASSWORD_REQUIRED ? html`` : '' } diff --git a/webpack.html b/webpack.html index 6ec9e3925..4309bc7b3 100644 --- a/webpack.html +++ b/webpack.html @@ -38,7 +38,8 @@ muc_domain: 'conference.chat.example.org', muc_respect_autojoin: true, view_mode: 'fullscreen', - websocket_url: 'ws://chat.example.org:5380/xmpp-websocket', + // websocket_url: 'ws://chat.example.org:5380/xmpp-websocket', + websocket_url: 'wss://conversejs.org/xmpp-websocket', // bosh_service_url: 'http://chat.example.org:5280/http-bind', allow_user_defined_connection_url: true, muc_show_logs_before_join: true, diff --git a/webpack/webpack.serve.js b/webpack/webpack.serve.js index b29fff513..988b981fd 100644 --- a/webpack/webpack.serve.js +++ b/webpack/webpack.serve.js @@ -9,7 +9,7 @@ module.exports = merge(common, { devtool: "inline-source-map", devServer: { static: [ path.resolve(__dirname, '../') ], - port: 3003 + port: 3004 }, plugins: [ new HTMLWebpackPlugin({