${bm.getDisplayName()}
diff --git a/src/plugins/bookmark-views/components/templates/list.js b/src/plugins/bookmark-views/components/templates/list.js
new file mode 100644
index 000000000..94e42dbb7
--- /dev/null
+++ b/src/plugins/bookmark-views/components/templates/list.js
@@ -0,0 +1,35 @@
+import bookmark_item from './item.js';
+import { __ } from 'i18n';
+import { _converse } from '@converse/headless/core.js';
+import { html } from "lit";
+
+const filterBookmark = (b, text) => b.get('name')?.includes(text) || b.get('jid')?.includes(text);
+
+export default (el) => {
+ const i18n_placeholder = __('Filter');
+ const filter_text = el.model.get('filter_text');
+ const { bookmarks } = _converse;
+ const shown_bookmarks = filter_text ? bookmarks.filter(b => filterBookmark(b, filter_text)) : bookmarks;
+
+ return html`
+
+
+
+
+ ${ shown_bookmarks.map(bm => bookmark_item(bm)) }
+
+
+ `;
+}
diff --git a/src/plugins/bookmark-views/index.js b/src/plugins/bookmark-views/index.js
index 4a6291233..b731c9f24 100644
--- a/src/plugins/bookmark-views/index.js
+++ b/src/plugins/bookmark-views/index.js
@@ -3,9 +3,11 @@
* @copyright 2022, the Converse.js contributors
* @license Mozilla Public License (MPLv2)
*/
+import './modals/bookmark-list.js';
+import './modals/bookmark-form.js';
import '@converse/headless/plugins/muc/index.js';
-import BookmarkForm from './form.js';
-import BookmarksView from './bookmarks-list.js';
+import BookmarkForm from './components/bookmark-form.js';
+import BookmarksView from './components/bookmarks-list.js';
import { _converse, api, converse } from '@converse/headless/core';
import { bookmarkableChatRoomView } from './mixins.js';
import { getHeadingButtons, removeBookmarkViaEvent, addBookmarkViaEvent } from './utils.js';
diff --git a/src/plugins/bookmark-views/mixins.js b/src/plugins/bookmark-views/mixins.js
index e95640057..693b98b0c 100644
--- a/src/plugins/bookmark-views/mixins.js
+++ b/src/plugins/bookmark-views/mixins.js
@@ -1,4 +1,3 @@
-import './modal.js';
import { _converse, api, converse } from '@converse/headless/core';
const { u } = converse.env;
diff --git a/src/plugins/bookmark-views/modal.js b/src/plugins/bookmark-views/modals/bookmark-form.js
similarity index 93%
rename from src/plugins/bookmark-views/modal.js
rename to src/plugins/bookmark-views/modals/bookmark-form.js
index 29008b835..85052284d 100644
--- a/src/plugins/bookmark-views/modal.js
+++ b/src/plugins/bookmark-views/modals/bookmark-form.js
@@ -1,4 +1,4 @@
-import './form.js';
+import '../components/bookmark-form.js';
import BaseModal from "plugins/modal/modal.js";
import { html } from "lit";
import { __ } from 'i18n';
diff --git a/src/plugins/bookmark-views/modals/bookmark-list.js b/src/plugins/bookmark-views/modals/bookmark-list.js
new file mode 100644
index 000000000..ad72a63c7
--- /dev/null
+++ b/src/plugins/bookmark-views/modals/bookmark-list.js
@@ -0,0 +1,18 @@
+import '../components/bookmarks-list.js';
+import BaseModal from "plugins/modal/modal.js";
+import { html } from "lit";
+import { __ } from 'i18n';
+import { api } from "@converse/headless/core";
+
+export default class BookmarkListModal extends BaseModal {
+
+ renderModal () { // eslint-disable-line class-methods-use-this
+ return html`
`;
+ }
+
+ getModalTitle () { // eslint-disable-line class-methods-use-this
+ return __('Bookmarks');
+ }
+}
+
+api.elements.define('converse-bookmark-list-modal', BookmarkListModal);
diff --git a/src/plugins/bookmark-views/styles/bookmarks.scss b/src/plugins/bookmark-views/styles/bookmarks.scss
index 5842969f9..bf701f3cd 100644
--- a/src/plugins/bookmark-views/styles/bookmarks.scss
+++ b/src/plugins/bookmark-views/styles/bookmarks.scss
@@ -24,3 +24,9 @@
}
}
}
+
+converse-bookmarks {
+ .list-item-link {
+ padding: 0 1em;
+ }
+}
diff --git a/src/plugins/bookmark-views/templates/list.js b/src/plugins/bookmark-views/templates/list.js
deleted file mode 100644
index f8b37afd8..000000000
--- a/src/plugins/bookmark-views/templates/list.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import bookmark_item from './item.js';
-import { __ } from 'i18n';
-import { _converse } from '@converse/headless/core.js';
-import { html } from "lit";
-import { until } from 'lit/directives/until.js';
-
-const list = (el, bookmarks) => {
- const desc_bookmarks = __('Click to toggle the bookmarks list');
- const label_bookmarks = __('Bookmarks');
- const toggle_state = el.model.get('toggle-state');
- return html`
-
- `;
-}
-
-export default (el) => {
- const bookmarks = _converse.bookmarks.getUnopenedBookmarks();
- return until(bookmarks.then((bookmarks) => list(el, bookmarks)), '');
-}
diff --git a/src/plugins/bookmark-views/tests/bookmarks-list.js b/src/plugins/bookmark-views/tests/bookmarks-list.js
index 480f3a26d..afa3a5f31 100644
--- a/src/plugins/bookmark-views/tests/bookmarks-list.js
+++ b/src/plugins/bookmark-views/tests/bookmarks-list.js
@@ -2,7 +2,7 @@
const { Strophe, u, sizzle, $iq } = converse.env;
-describe("The bookmarks list", function () {
+describe("The bookmarks list modal", function () {
it("shows a list of bookmarks", mock.initConverse(
['chatBoxesFetched'], {},
@@ -16,6 +16,9 @@ describe("The bookmarks list", function () {
);
mock.openControlBox(_converse);
+ const controlbox = _converse.chatboxviews.get('controlbox');
+ controlbox.querySelector('.show-bookmark-list-modal').click();
+
const IQ_stanzas = _converse.connection.IQ_stanzas;
const sent_stanza = await u.waitUntil(
() => IQ_stanzas.filter(s => sizzle('items[node="storage:bookmarks"]', s).length).pop());
@@ -59,9 +62,10 @@ describe("The bookmarks list", function () {
}).c('nick').t('JC').up().up();
_converse.connection._dataRecv(mock.createRequest(stanza));
- await u.waitUntil(() => document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length);
- expect(document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(5);
- let els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
+ const modal = _converse.api.modal.get('converse-bookmark-list-modal');
+ await u.waitUntil(() => modal.querySelectorAll('.bookmarks.rooms-list .room-item').length);
+ expect(modal.querySelectorAll('.bookmarks.rooms-list .room-item').length).toBe(5);
+ let els = modal.querySelectorAll('.bookmarks.rooms-list .room-item a.list-item-link');
expect(els[0].textContent).toBe("1st Bookmark");
expect(els[1].textContent).toBe("Another room");
expect(els[2].textContent).toBe("Bookmark with a very very long name that will be shortened");
@@ -69,10 +73,10 @@ describe("The bookmarks list", function () {
expect(els[4].textContent).toBe("The Play's the Thing");
spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true));
- document.querySelector('#chatrooms .bookmarks.rooms-list .room-item:nth-child(2) a:nth-child(2)').click();
+ modal.querySelector('.bookmarks.rooms-list .room-item:nth-child(2) a:nth-child(2)').click();
expect(_converse.api.confirm).toHaveBeenCalled();
- await u.waitUntil(() => document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length === 4)
- els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
+ await u.waitUntil(() => modal.querySelectorAll('.bookmarks.rooms-list .room-item').length === 4)
+ els = modal.querySelectorAll('.bookmarks.rooms-list .room-item a.list-item-link');
expect(els[0].textContent).toBe("1st Bookmark");
expect(els[1].textContent).toBe("Bookmark with a very very long name that will be shortened");
expect(els[2].textContent).toBe("noname@conference.shakespeare.lit");
@@ -80,17 +84,22 @@ describe("The bookmarks list", function () {
}));
it("can be used to open a MUC from a bookmark", mock.initConverse(
- [], {'view_mode': 'fullscreen'}, async function (_converse) {
+ ['chatBoxesFetched'], {'view_mode': 'fullscreen'},
+ async function (_converse) {
const api = _converse.api;
+
+ await mock.waitForRoster(_converse, 'current', 0);
await mock.waitUntilDiscoConfirmed(
_converse, _converse.bare_jid,
[{'category': 'pubsub', 'type': 'pep'}],
['http://jabber.org/protocol/pubsub#publish-options']
);
- await mock.waitForRoster(_converse, 'current', 0);
- await mock.openControlBox(_converse);
- const view = await _converse.chatboxviews.get('controlbox');
+ mock.openControlBox(_converse);
+
+ const controlbox = await _converse.chatboxviews.get('controlbox');
+ controlbox.querySelector('.show-bookmark-list-modal').click();
+
const IQ_stanzas = _converse.connection.IQ_stanzas;
const sent_stanza = await u.waitUntil(
() => IQ_stanzas.filter(s => sizzle('items[node="storage:bookmarks"]', s).length).pop());
@@ -110,79 +119,26 @@ describe("The bookmarks list", function () {
'jid': 'first@conference.shakespeare.lit'
}).c('nick').t('JC');
_converse.connection._dataRecv(mock.createRequest(stanza));
- await u.waitUntil(() => view.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length);
- expect(view.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(2);
- view.querySelector('.bookmarks.rooms-list .open-room').click();
+
+ const modal = api.modal.get('converse-bookmark-list-modal');
+ await u.waitUntil(() => u.isVisible(modal), 1000);
+
+ await u.waitUntil(() => modal.querySelectorAll('.bookmarks.rooms-list .room-item').length);
+ expect(modal.querySelectorAll('.bookmarks.rooms-list .room-item').length).toBe(2);
+ modal.querySelector('.bookmarks.rooms-list .open-room').click();
await u.waitUntil(() => _converse.chatboxes.length === 2);
expect((await api.rooms.get('first@conference.shakespeare.lit')).get('hidden')).toBe(false);
- await u.waitUntil(() => view.querySelectorAll('.list-container--bookmarks .available-chatroom:not(.hidden)').length === 1);
- view.querySelector('.list-container--bookmarks .available-chatroom:not(.hidden) .open-room').click();
+ await u.waitUntil(() => modal.querySelectorAll('.list-container--bookmarks .available-chatroom').length);
+ modal.querySelector('.list-container--bookmarks .available-chatroom:last-child .open-room').click();
await u.waitUntil(() => _converse.chatboxes.length === 3);
+
expect((await api.rooms.get('first@conference.shakespeare.lit')).get('hidden')).toBe(true);
expect((await api.rooms.get('theplay@conference.shakespeare.lit')).get('hidden')).toBe(false);
- view.querySelector('.list-container--openrooms .open-room:first-child').click();
- await u.waitUntil(() => view.querySelector('.list-item.open').getAttribute('data-room-jid') === 'first@conference.shakespeare.lit');
+ controlbox.querySelector('.list-container--openrooms .open-room:first-child').click();
+ await u.waitUntil(() => controlbox.querySelector('.list-item.open').getAttribute('data-room-jid') === 'first@conference.shakespeare.lit');
expect((await api.rooms.get('first@conference.shakespeare.lit')).get('hidden')).toBe(false);
expect((await api.rooms.get('theplay@conference.shakespeare.lit')).get('hidden')).toBe(true);
}));
-
- it("remembers the toggle state of the bookmarks list", mock.initConverse(
- [], {}, async function (_converse) {
-
- await mock.waitForRoster(_converse, 'current', 0);
- await mock.openControlBox(_converse);
- await mock.waitUntilDiscoConfirmed(
- _converse, _converse.bare_jid,
- [{'category': 'pubsub', 'type': 'pep'}],
- ['http://jabber.org/protocol/pubsub#publish-options']
- );
-
- const { Strophe, u, sizzle, $iq } = converse.env;
- const IQ_stanzas = _converse.connection.IQ_stanzas;
- const sent_stanza = await u.waitUntil(
- () => IQ_stanzas.filter(s => sizzle('iq items[node="storage:bookmarks"]', s).length).pop());
-
- expect(Strophe.serialize(sent_stanza)).toBe(
- `
`+
- ''+
- ''+
- ''+
- ''
- );
- const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id': sent_stanza.getAttribute('id')})
- .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
- .c('items', {'node': 'storage:bookmarks'})
- .c('item', {'id': 'current'})
- .c('storage', {'xmlns': 'storage:bookmarks'});
- _converse.connection._dataRecv(mock.createRequest(stanza));
- await _converse.api.waitUntil('bookmarksInitialized');
-
- _converse.bookmarks.create({
- 'jid': 'theplay@conference.shakespeare.lit',
- 'autojoin': false,
- 'name': 'The Play',
- 'nick': ''
- });
- const chats_el = document.querySelector('converse-chats');
- const selector = '#chatrooms .bookmarks.rooms-list .room-item';
- await u.waitUntil(() => sizzle(selector, chats_el).filter(u.isVisible).length);
- expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', chats_el).pop())).toBeFalsy();
- expect(sizzle(selector, chats_el).filter(u.isVisible).length).toBe(1);
-
- const bookmarks_el = chats_el.querySelector('converse-bookmarks');
- expect(bookmarks_el.model.get('toggle-state')).toBe(_converse.OPENED);
-
- sizzle('#chatrooms .bookmarks-toggle', chats_el).pop().click();
-
- await u.waitUntil(() => u.hasClass('hidden', sizzle('#chatrooms .bookmarks.rooms-list', chats_el).pop()));
- expect(bookmarks_el.model.get('toggle-state')).toBe(_converse.CLOSED);
-
- sizzle('#chatrooms .bookmarks-toggle', chats_el).pop().click();
-
- await u.waitUntil(() => !u.hasClass('hidden', sizzle('#chatrooms .bookmarks.rooms-list', chats_el).pop()));
- expect(sizzle(selector, chats_el).filter(u.isVisible).length).toBe(1);
- expect(bookmarks_el.model.get('toggle-state')).toBe(_converse.OPENED);
- }));
});
diff --git a/src/plugins/bookmark-views/tests/bookmarks.js b/src/plugins/bookmark-views/tests/bookmarks.js
index 04eef062c..86ea34c76 100644
--- a/src/plugins/bookmark-views/tests/bookmarks.js
+++ b/src/plugins/bookmark-views/tests/bookmarks.js
@@ -492,40 +492,3 @@ describe("Bookmarks", function () {
expect(_converse.bookmarks.get('another@conference.shakespeare.lit').get('autojoin')).toBe(false);
}));
});
-
-describe("When hide_open_bookmarks is true and a bookmarked room is opened", function () {
-
- it("can be closed", mock.initConverse(
- [], { hide_open_bookmarks: true }, async function (_converse) {
-
- await mock.waitForRoster(_converse, 'current', 0);
- await mock.openControlBox(_converse);
- await mock.waitUntilBookmarksReturned(_converse);
-
- // Check that it's there
- const jid = 'room@conference.example.org';
- _converse.bookmarks.create({
- 'jid': jid,
- 'autojoin': false,
- 'name': 'The Play',
- 'nick': ' Othello'
- });
- expect(_converse.bookmarks.length).toBe(1);
-
- const u = converse.env.utils;
- const bookmarks_el = document.querySelector('converse-bookmarks');
- await u.waitUntil(() => bookmarks_el.querySelectorAll(".open-room").length, 500);
- const room_els = bookmarks_el.querySelectorAll(".open-room");
- expect(room_els.length).toBe(1);
-
- const bookmark = bookmarks_el.querySelector(".open-room");
- bookmark.click();
- await u.waitUntil(() => _converse.chatboxviews.get(jid));
-
- expect(u.hasClass('hidden', bookmarks_el.querySelector(".available-chatroom"))).toBeTruthy();
- // Check that it reappears once the room is closed
- const view = _converse.chatboxviews.get(jid);
- view.close();
- await u.waitUntil(() => !u.hasClass('hidden', bookmarks_el.querySelector(".available-chatroom")));
- }));
-});
diff --git a/src/plugins/bookmark-views/utils.js b/src/plugins/bookmark-views/utils.js
index eebae4197..cf4e20301 100644
--- a/src/plugins/bookmark-views/utils.js
+++ b/src/plugins/bookmark-views/utils.js
@@ -1,4 +1,3 @@
-import './modal.js';
import invokeMap from 'lodash-es/invokeMap';
import { Model } from '@converse/skeletor/src/model.js';
import { __ } from 'i18n';
diff --git a/src/plugins/controlbox/templates/controlbox.js b/src/plugins/controlbox/templates/controlbox.js
index 9f6a1007d..7ecfcc66c 100644
--- a/src/plugins/controlbox/templates/controlbox.js
+++ b/src/plugins/controlbox/templates/controlbox.js
@@ -39,10 +39,7 @@ export default (el) => {
? html`
-
-
-
-
+
${ api.settings.get("authentication") === _converse.ANONYMOUS ? '' :
html`
`
}`
diff --git a/src/plugins/roomslist/templates/roomslist.js b/src/plugins/roomslist/templates/roomslist.js
index 5b41da2b6..c481fe18d 100644
--- a/src/plugins/roomslist/templates/roomslist.js
+++ b/src/plugins/roomslist/templates/roomslist.js
@@ -76,9 +76,26 @@ export default (o) => {
const i18n_heading_chatrooms = __('Groupchats');
const i18n_title_list_rooms = __('Query for groupchats');
const i18n_title_new_room = __('Add a new groupchat');
+ const i18n_show_bookmarks = __('Show bookmarked groupchats');
return html`
-
-
- ${__('Open Groupchats')}
${ o.rooms.map(room => room_item(Object.assign({room}, o))) }
diff --git a/src/plugins/roomslist/view.js b/src/plugins/roomslist/view.js
index 9e040bdee..934cf35ee 100644
--- a/src/plugins/roomslist/view.js
+++ b/src/plugins/roomslist/view.js
@@ -92,7 +92,8 @@ export class RoomsList extends CustomElement {
toggleRoomsList (ev) {
ev?.preventDefault?.();
- const icon_el = ev.target.matches('.fa') ? ev.target : ev.target.querySelector('.fa');
+ const target = ev.currentTarget;
+ const icon_el = target.matches('.fa') ? target : target.querySelector('.fa');
if (icon_el.classList.contains("fa-caret-down")) {
u.slideIn(this.querySelector('.open-rooms-list')).then(() => {
this.model.save({'toggle-state': _converse.CLOSED});
diff --git a/src/shared/styles/forms.scss b/src/shared/styles/forms.scss
index 1a1857649..bdafd05cc 100644
--- a/src/shared/styles/forms.scss
+++ b/src/shared/styles/forms.scss
@@ -50,13 +50,15 @@
margin-top: 0.5em;
}
- .clear-input {
- margin-top: 0.5em;
- margin-bottom : 0.5em;
- position: absolute;
- right: 0.2em;
- cursor: pointer;
- font-size: var(--font-size);
+ .btn-group {
+ .clear-input {
+ margin-top: 0.5em;
+ margin-bottom : 0.5em;
+ position: absolute;
+ right: 0.2em;
+ cursor: pointer;
+ font-size: var(--font-size);
+ }
}
converse-register,