+
+
+
${api.settings.get('authentication') === ANONYMOUS
? ''
diff --git a/src/plugins/roomslist/templates/roomslist.js b/src/plugins/roomslist/templates/roomslist.js
index b39fbb9054..b25f6827fb 100644
--- a/src/plugins/roomslist/templates/roomslist.js
+++ b/src/plugins/roomslist/templates/roomslist.js
@@ -3,72 +3,14 @@
* @typedef {import('@converse/headless').MUC} MUC
*/
import { html } from "lit";
-import { _converse, api, u, constants } from "@converse/headless";
+import { api, constants } from "@converse/headless";
import 'plugins/muc-views/modals/add-muc.js';
import 'plugins/muc-views/modals/muc-list.js';
import { __ } from 'i18n';
-import { getUnreadMsgsDisplay } from "shared/chat/utils";
-
import '../styles/roomsgroups.scss';
+import { tplRoomItem } from "shared/roomslist/templates/room-item";
const { CLOSED } = constants;
-const { isUniView } = u;
-
-/** @param {MUC} room */
-function isCurrentlyOpen (room) {
- return isUniView() && !room.get('hidden');
-}
-
-/** @param {MUC} room */
-function tplUnreadIndicator (room) {
- return html`
${ getUnreadMsgsDisplay(room) }`;
-}
-
-function tplActivityIndicator () {
- return html`
`;
-}
-
-/**
- * @param {RoomsList} el
- * @param {MUC} room
- */
-function tplRoomItem (el, room) {
- const i18n_leave_room = __('Leave this groupchat');
- const has_unread_msgs = room.get('num_unread_general') || room.get('has_activity');
- return html`
-
-
- el.openRoom(ev)}>
-
- ${ room.get('num_unread') ?
- tplUnreadIndicator(room) :
- (room.get('has_activity') ? tplActivityIndicator() : '') }
- ${room.getDisplayName()}
-
-
- el.closeRoom(ev)}>
-
-
- `;
-}
/**
* @param {RoomsList} el
diff --git a/src/plugins/roomslist/view.js b/src/plugins/roomslist/view.js
index 1b14e515c6..8a335ee9fb 100644
--- a/src/plugins/roomslist/view.js
+++ b/src/plugins/roomslist/view.js
@@ -19,6 +19,12 @@ export class RoomsList extends CustomElement {
initStorage(this.model, id);
this.model.fetch();
+ this.handleEvents();
+
+ this.requestUpdate();
+ }
+
+ handleEvents() {
const { chatboxes } = _converse.state;
this.listenTo(chatboxes, 'add', this.renderIfChatRoom);
this.listenTo(chatboxes, 'remove', this.renderIfChatRoom);
@@ -27,8 +33,6 @@ export class RoomsList extends CustomElement {
this.listenTo(chatboxes, 'vcard:add', () => this.requestUpdate());
this.listenTo(chatboxes, 'vcard:change', () => this.requestUpdate());
this.listenTo(this.model, 'change', () => this.requestUpdate());
-
- this.requestUpdate();
}
render() {
@@ -42,7 +46,7 @@ export class RoomsList extends CustomElement {
/** @param {import('@converse/headless').Model} model */
renderIfRelevantChange(model) {
- const attrs = ['bookmarked', 'hidden', 'name', 'num_unread', 'num_unread_general', 'has_activity'];
+ const attrs = ['bookmarked', 'hidden', 'name', 'num_unread', 'num_unread_general', 'has_activity', 'pinned'];
const changed = model.changed || {};
if (u.muc.isChatRoom(model) && Object.keys(changed).filter((m) => attrs.includes(m)).length) {
this.requestUpdate();
@@ -52,7 +56,7 @@ export class RoomsList extends CustomElement {
/** @returns {import('@converse/headless').MUC[]} */
getRoomsToShow() {
const { chatboxes } = _converse.state;
- const rooms = chatboxes.filter((m) => m.get('type') === CHATROOMS_TYPE && !m.get('closed'));
+ const rooms = chatboxes.filter((m) => m.get('type') === CHATROOMS_TYPE && !m.get('closed') && !m.get('pinned'));
rooms.sort((a, b) => (a.getDisplayName().toLowerCase() <= b.getDisplayName().toLowerCase() ? -1 : 1));
return rooms;
}
@@ -82,6 +86,29 @@ export class RoomsList extends CustomElement {
}
}
+ /** @param {Event} ev */
+ pinRoom(ev) {
+ ev.preventDefault();
+ const target = /** @type {HTMLElement} */ (ev.currentTarget);
+ const jid = target.getAttribute('data-room-jid');
+ const { bookmarks } = _converse.state;
+ bookmarks
+ .where({ jid })
+ .forEach(/** @param {import('@converse/headless').Bookmark} b */ (b) =>
+ bookmarks.pinBookmark(b));
+ }
+
+ /** @param {Event} ev */
+ unpinRoom(ev) {
+ ev.preventDefault();
+ const target = /** @type {HTMLElement} */ (ev.currentTarget);
+ const jid = target.getAttribute('data-room-jid');
+ const { bookmarks } = _converse.state;
+ bookmarks
+ .where({ jid })
+ .forEach((b) => bookmarks.unpinBookmark(b));
+ }
+
/** @param {Event} [ev] */
toggleRoomsList(ev) {
ev?.preventDefault?.();
diff --git a/src/shared/roomslist/templates/room-item.js b/src/shared/roomslist/templates/room-item.js
new file mode 100644
index 0000000000..b0d76c6cbf
--- /dev/null
+++ b/src/shared/roomslist/templates/room-item.js
@@ -0,0 +1,116 @@
+/**
+ * @typedef {import('plugins/roomslist/view').RoomsList} RoomsList
+ * @typedef {import('plugins/bookmark-views/components/bookmarks-pin-list').BookmarksPinView} BookmarksPinView
+ * @typedef {import('@converse/headless').MUC} MUC
+ */
+import { html } from "lit";
+import { api, u } from "@converse/headless";
+import 'plugins/muc-views/modals/add-muc.js';
+import 'plugins/muc-views/modals/muc-list.js';
+import { __ } from 'i18n';
+import { getUnreadMsgsDisplay } from "shared/chat/utils";
+
+const { isUniView } = u;
+
+/** @param {MUC} room */
+function isCurrentlyOpen (room) {
+ return isUniView() && !room.get('hidden');
+}
+
+/** @param {MUC} room */
+function tplUnreadIndicator (room) {
+ return html`
${ getUnreadMsgsDisplay(room) }`;
+}
+
+function tplActivityIndicator () {
+ return html`
`;
+}
+
+/**
+ * @param {RoomsList|BookmarksPinView} el
+ * @param {MUC} room
+ */
+export function tplRoomItem (el, room) {
+ const i18n_leave_room = __('Leave this groupchat');
+ const has_unread_msgs = room.get('num_unread_general') || room.get('has_activity');
+
+ const buttons = [
+ tplRoomMenuItem({
+ room,
+ alt_text: i18n_leave_room,
+ text: __('Leave'),
+ icon_class: 'fa-sign-out-alt',
+ btn_class: 'close-room',
+ handler: (ev) => el.closeRoom(ev)
+ }),
+ ];
+
+ if (api.settings.get('allow_bookmarks')) {
+ if (!room.get('pinned')) {
+ buttons.push(tplRoomMenuItem({
+ room,
+ alt_text: __('Pin this groupchat to the top of the list'),
+ text: __('Pin'),
+ icon_class: 'fa-bookmark',
+ btn_class: 'pin-room',
+ handler: (ev) => el.pinRoom(ev)
+ }))
+ } else {
+ buttons.push(tplRoomMenuItem({
+ room,
+ alt_text: __('Unpin this groupchat from the top of the list'),
+ text: __('Unpin'),
+ icon_class: 'fa-bookmark-empty',
+ btn_class: 'unpin-room',
+ handler: (ev) => el.unpinRoom(ev)
+ }))
+ }
+ }
+
+ return html`
+
+
+ el.openRoom(ev)}>
+
+ ${ room.get('num_unread') ?
+ tplUnreadIndicator(room) :
+ (room.get('has_activity') ? tplActivityIndicator() : '') }
+ ${room.getDisplayName()}
+
+
+
+ `;
+}
+
+/**
+ * @param {Object} config
+ * @param {MUC} config.room
+ * @param {string} config.alt_text
+ * @param {string} config.text
+ * @param {function} config.handler
+ * @param {string} config.icon_class
+ * @param {string} config.btn_class
+ * @returns
+ */
+function tplRoomMenuItem (config) {
+ const { room, alt_text, text, handler, icon_class, btn_class } = config;
+ return html`
+
+ ${text}
+ `;
+}
diff --git a/src/types/plugins/bookmark-views/components/bookmarks-pin-list.d.ts b/src/types/plugins/bookmark-views/components/bookmarks-pin-list.d.ts
new file mode 100644
index 0000000000..a1a54f973a
--- /dev/null
+++ b/src/types/plugins/bookmark-views/components/bookmarks-pin-list.d.ts
@@ -0,0 +1,5 @@
+export class BookmarksPinView extends RoomsList {
+ model: any;
+}
+import { RoomsList } from 'plugins/roomslist/view';
+//# sourceMappingURL=bookmarks-pin-list.d.ts.map
\ No newline at end of file
diff --git a/src/types/plugins/bookmark-views/components/model.d.ts b/src/types/plugins/bookmark-views/components/model.d.ts
new file mode 100644
index 0000000000..b406ee8223
--- /dev/null
+++ b/src/types/plugins/bookmark-views/components/model.d.ts
@@ -0,0 +1,8 @@
+export default class BookmarksPinListModel extends Model
{
+ constructor(attributes?: Partial, options?: import("@converse/skeletor").ModelOptions);
+ defaults(): {
+ toggle_state: "opened";
+ };
+}
+import { Model } from "@converse/headless";
+//# sourceMappingURL=model.d.ts.map
\ No newline at end of file
diff --git a/src/types/plugins/bookmark-views/components/templates/pin-list.d.ts b/src/types/plugins/bookmark-views/components/templates/pin-list.d.ts
new file mode 100644
index 0000000000..942587b0a3
--- /dev/null
+++ b/src/types/plugins/bookmark-views/components/templates/pin-list.d.ts
@@ -0,0 +1,5 @@
+declare function _default(el: BookmarksPinView): import("lit-html").TemplateResult<1>;
+export default _default;
+export type MUC = import("@converse/headless").MUC;
+export type BookmarksPinView = import("plugins/bookmark-views/components/bookmarks-pin-list").BookmarksPinView;
+//# sourceMappingURL=pin-list.d.ts.map
\ No newline at end of file
diff --git a/src/types/plugins/roomslist/view.d.ts b/src/types/plugins/roomslist/view.d.ts
index 0139f4dadd..7492164a5b 100644
--- a/src/types/plugins/roomslist/view.d.ts
+++ b/src/types/plugins/roomslist/view.d.ts
@@ -1,6 +1,7 @@
export class RoomsList extends CustomElement {
initialize(): void;
model: RoomsListModel;
+ handleEvents(): void;
render(): import("lit-html").TemplateResult<1>;
/** @param {import('@converse/headless').Model} model */
renderIfChatRoom(model: import("@converse/headless").Model): void;
@@ -12,6 +13,10 @@ export class RoomsList extends CustomElement {
openRoom(ev: Event): Promise;
/** @param {Event} ev */
closeRoom(ev: Event): Promise;
+ /** @param {Event} ev */
+ pinRoom(ev: Event): void;
+ /** @param {Event} ev */
+ unpinRoom(ev: Event): void;
/** @param {Event} [ev] */
toggleRoomsList(ev?: Event): void;
/**
diff --git a/src/types/shared/roomslist/templates/room-item.d.ts b/src/types/shared/roomslist/templates/room-item.d.ts
new file mode 100644
index 0000000000..703abab1d8
--- /dev/null
+++ b/src/types/shared/roomslist/templates/room-item.d.ts
@@ -0,0 +1,9 @@
+/**
+ * @param {RoomsList|BookmarksPinView} el
+ * @param {MUC} room
+ */
+export function tplRoomItem(el: RoomsList | BookmarksPinView, room: MUC): import("lit-html").TemplateResult<1>;
+export type RoomsList = import("plugins/roomslist/view").RoomsList;
+export type BookmarksPinView = import("plugins/bookmark-views/components/bookmarks-pin-list").BookmarksPinView;
+export type MUC = import("@converse/headless").MUC;
+//# sourceMappingURL=room-item.d.ts.map
\ No newline at end of file