Combine RoomsPanel with RoomsList element

This commit is contained in:
JC Brand 2021-01-26 09:58:16 +01:00
parent b31f334109
commit bb317d1abb
13 changed files with 107 additions and 182 deletions

View File

@ -410,7 +410,7 @@ describe("Bookmarks", function () {
it("can be retrieved from the XMPP server", mock.initConverse(
['chatBoxesFetched', 'roomsPanelRendered', 'rosterGroupsFetched'], {},
['chatBoxesFetched', 'rosterGroupsFetched'], {},
async function (done, _converse) {
const { Strophe, sizzle, u, $iq } = converse.env;
@ -420,14 +420,14 @@ describe("Bookmarks", function () {
['http://jabber.org/protocol/pubsub#publish-options']
);
/* Client requests all items
* -------------------------
*
* <iq from='juliet@capulet.lit/randomID' type='get' id='retrieve1'>
* <pubsub xmlns='http://jabber.org/protocol/pubsub'>
* <items node='storage:bookmarks'/>
* </pubsub>
* </iq>
*/
* -------------------------
*
* <iq from='juliet@capulet.lit/randomID' type='get' id='retrieve1'>
* <pubsub xmlns='http://jabber.org/protocol/pubsub'>
* <items node='storage:bookmarks'/>
* </pubsub>
* </iq>
*/
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());
@ -487,7 +487,7 @@ describe("Bookmarks", function () {
done();
}));
describe("The rooms panel", function () {
describe("The bookmarks list", function () {
it("shows a list of bookmarks", mock.initConverse(
['rosterGroupsFetched'], {},
@ -649,19 +649,21 @@ describe("Bookmarks", function () {
'name': 'The Play',
'nick': ''
});
const el = _converse.chatboxviews.el
const chats_el = document.querySelector('converse-chats');
const selector = '#chatrooms .bookmarks.rooms-list .room-item';
await u.waitUntil(() => sizzle(selector, el).filter(u.isVisible).length);
expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', el).pop())).toBeFalsy();
expect(sizzle(selector, el).filter(u.isVisible).length).toBe(1);
expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
sizzle('#chatrooms .bookmarks-toggle', el).pop().click();
expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', el).pop())).toBeTruthy();
expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.CLOSED);
sizzle('#chatrooms .bookmarks-toggle', el).pop().click();
expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', el).pop())).toBeFalsy();
expect(sizzle(selector, el).filter(u.isVisible).length).toBe(1);
expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
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();
expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', chats_el).pop())).toBeTruthy();
expect(bookmarks_el.model.get('toggle-state')).toBe(_converse.CLOSED);
sizzle('#chatrooms .bookmarks-toggle', chats_el).pop().click();
expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', chats_el).pop())).toBeFalsy();
expect(sizzle(selector, chats_el).filter(u.isVisible).length).toBe(1);
expect(bookmarks_el.model.get('toggle-state')).toBe(_converse.OPENED);
done();
}));
});
@ -688,20 +690,20 @@ describe("When hide_open_bookmarks is true and a bookmarked room is opened", fun
expect(_converse.bookmarks.length).toBe(1);
const u = converse.env.utils;
const bmarks_view = _converse.bookmarksview;
await u.waitUntil(() => bmarks_view.querySelectorAll(".open-room").length, 500);
const room_els = bmarks_view.querySelectorAll(".open-room");
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 = _converse.bookmarksview.querySelector(".open-room");
const bookmark = bookmarks_el.querySelector(".open-room");
bookmark.click();
await u.waitUntil(() => _converse.chatboxviews.get(jid));
expect(u.hasClass('hidden', _converse.bookmarksview.querySelector(".available-chatroom"))).toBeTruthy();
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', _converse.bookmarksview.querySelector(".available-chatroom")));
await u.waitUntil(() => !u.hasClass('hidden', bookmarks_el.querySelector(".available-chatroom")));
done();
}));
});

View File

@ -126,7 +126,7 @@ describe("A groupchat shown in the groupchats list", function () {
expect(room_els.length).toBe(1);
let item = room_els[0];
await u.waitUntil(() => lview.model.get(muc_jid).get('hidden') === false);
await u.waitUntil(() => _converse.chatboxes.get(muc_jid).get('hidden') === false);
await u.waitUntil(() => u.hasClass('open', item), 1000);
expect(item.textContent.trim()).toBe('coven@chat.shakespeare.lit');
await _converse.api.rooms.open('balcony@chat.shakespeare.lit', {'nick': 'some1'}, true);

View File

@ -742,7 +742,6 @@ export const api = _converse.api = {
* * [rosterContactsFetched](/docs/html/events.html#rosterContactsFetched)
* * [rosterGroupsFetched](/docs/html/events.html#rosterGroupsFetched)
* * [rosterInitialized](/docs/html/events.html#rosterInitialized)
* * [roomsPanelRendered](/docs/html/events.html#roomsPanelRendered)
*
* The various plugins might also provide promises, and they do this by using the
* `promises.add` api method.

View File

@ -16,7 +16,6 @@ import muc_api from './api.js';
import muc_utils from './utils.js';
import u from '../../utils/form';
import { Collection } from '@converse/skeletor/src/collection';
import { Model } from '@converse/skeletor/src/model.js';
import { _converse, api, converse } from '../../core.js';
import { isObject } from 'lodash-es';
@ -369,21 +368,6 @@ converse.plugins.add('converse-muc', {
});
_converse.RoomsPanelModel = Model.extend({
defaults: function () {
return {
'muc_domain': api.settings.get('muc_domain'),
'nick': _converse.getDefaultMUCNickname()
};
},
setDomain (jid) {
if (!api.settings.get('locked_muc_domain')) {
this.save('muc_domain', Strophe.getDomainFromJid(jid));
}
}
});
/**
* A direct MUC invitation to join a groupchat has been received
* See XEP-0249: Direct MUC invitations.

View File

@ -10,31 +10,30 @@ export default class BookmarksView extends ElementView {
async initialize () {
await api.waitUntil('bookmarksInitialized');
this.model = _converse.bookmarks;
this.listenTo(this.model, 'add', this.render);
this.listenTo(this.model, 'remove', this.render);
this.listenTo(_converse.bookmarks, 'add', this.render);
this.listenTo(_converse.bookmarks, 'remove', this.render);
this.listenTo(_converse.chatboxes, 'add', this.render);
this.listenTo(_converse.chatboxes, 'remove', this.render);
const id = `converse.room-bookmarks${_converse.bare_jid}-list-model`;
this.list_model = new _converse.BookmarksList({ id });
this.list_model.browserStorage = _converse.createStore(id);
this.list_model.fetch({ 'success': () => this.render(), 'error': () => this.render() });
this.model = new _converse.BookmarksList({ id });
this.model.browserStorage = _converse.createStore(id);
this.model.fetch({ 'success': () => this.render(), 'error': () => this.render() });
}
render () {
const is_hidden = b => !!(api.settings.get('hide_open_bookmarks') && _converse.chatboxes.get(b.get('jid')));
render(tpl_bookmarks_list({
'_converse': _converse,
'bookmarks': this.model,
'hidden': this.model.getUnopenedBookmarks().length && true,
'bookmarks': _converse.bookmarks,
'hidden': _converse.bookmarks.getUnopenedBookmarks().length && true,
'is_hidden': is_hidden,
'openRoom': ev => this.openRoom(ev),
'removeBookmark': ev => this.removeBookmark(ev),
'toggleBookmarksList': ev => this.toggleBookmarksList(ev),
'toggle_state': this.list_model.get('toggle-state')
'toggle_state': this.model.get('toggle-state')
}), this);
}
@ -58,15 +57,15 @@ export default class BookmarksView extends ElementView {
}
const icon_el = ev.target.matches('.fa') ? ev.target : ev.target.querySelector('.fa');
if (u.hasClass('fa-caret-down', icon_el)) {
u.slideIn(this.el.querySelector('.bookmarks'));
this.list_model.save({ 'toggle-state': _converse.CLOSED });
u.slideIn(this.querySelector('.bookmarks'));
this.model.save({ 'toggle-state': _converse.CLOSED });
icon_el.classList.remove('fa-caret-down');
icon_el.classList.add('fa-caret-right');
} else {
icon_el.classList.remove('fa-caret-right');
icon_el.classList.add('fa-caret-down');
u.slideOut(this.el.querySelector('.bookmarks'));
this.list_model.save({ 'toggle-state': _converse.OPENED });
u.slideOut(this.querySelector('.bookmarks'));
this.model.save({ 'toggle-state': _converse.OPENED });
}
}
}

View File

@ -15,9 +15,11 @@ export default o => html`
${o.connected
? html`
<converse-user-profile></converse-user-profile>
<converse-headlines-panel></converse-headlines-panel>
<converse-rooms-list></converse-rooms-list>
<converse-bookmarks></converse-bookmarks>`
<converse-headlines-panel class="controlbox-section"></converse-headlines-panel>
<div id="chatrooms" class="controlbox-section">
<converse-rooms-list></converse-rooms-list>
<converse-bookmarks></converse-bookmarks>
</div>`
: o['active-form'] === 'register'
? html`<converse-register-panel></converse-register-panel>`
: html`<converse-login-panel></converse-login-panel>`

View File

@ -94,8 +94,6 @@ converse.plugins.add('converse-muc-views', {
initialize () {
const { _converse } = this;
api.promises.add(['roomsPanelRendered']);
// Configuration values for this plugin
// ====================================
// Refer to docs/source/configuration.rst for explanations of these

View File

@ -1,74 +1,12 @@
import AddMUCModal from 'modals/add-muc.js';
import tpl_room_panel from 'templates/room_panel.js';
import { View } from '@converse/skeletor/src/view.js';
import MUCListModal from 'modals/muc-list.js';
import { _converse, api, converse } from '@converse/headless/core';
import { __ } from 'i18n';
import { converse } from '@converse/headless/core';
const u = converse.env.utils;
/**
* View which renders MUC section of the control box.
* @class
* @namespace _converse.RoomsPanel
* @memberOf _converse
*/
export const RoomsPanel = View.extend({
tagName: 'div',
className: 'controlbox-section',
id: 'chatrooms',
events: {
'click a.controlbox-heading__btn.show-add-muc-modal': 'showAddRoomModal',
'click a.controlbox-heading__btn.show-list-muc-modal': 'showMUCListModal'
},
toHTML () {
return tpl_room_panel({
'heading_chatrooms': __('Groupchats'),
'title_new_room': __('Add a new groupchat'),
'title_list_rooms': __('Query for groupchats')
});
},
showAddRoomModal (ev) {
api.modal.show(AddMUCModal, { 'model': this.model }, ev);
},
showMUCListModal (ev) {
api.modal.show(MUCListModal, { 'model': this.model }, ev);
}
});
/**
* Mixin which adds the ability to a ControlBox to render a list of open groupchats
* @mixin
*/
export const RoomsPanelViewMixin = {
renderRoomsPanel () {
if (this.roomspanel && u.isInDOM(this.roomspanel.el)) {
return this.roomspanel;
}
const id = `converse.roomspanel${_converse.bare_jid}`;
this.roomspanel = new _converse.RoomsPanel({
'model': new (_converse.RoomsPanelModel.extend({
id,
'browserStorage': _converse.createStore(id)
}))()
});
this.roomspanel.model.fetch();
this.querySelector('.controlbox-pane').insertAdjacentElement('beforeEnd', this.roomspanel.render().el);
/**
* Triggered once the section of the { @link _converse.ControlBoxView }
* which shows gropuchats has been rendered.
* @event _converse#roomsPanelRendered
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
*/
api.trigger('roomsPanelRendered');
return this.roomspanel;
},
getRoomsPanel () {
if (this.roomspanel && u.isInDOM(this.roomspanel.el)) {
return this.roomspanel;

View File

@ -7,28 +7,14 @@
*/
import "@converse/headless/plugins/muc/index.js";
import './view.js';
import RoomsList from './model.js';
import { _converse, api, converse } from "@converse/headless/core";
converse.plugins.add('converse-roomslist', {
/* Dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
* this plugin. They are called "optional" because they might not be
* available, in which case any overrides applicable to them will be
* ignored.
*
* It's possible however to make optional dependencies non-optional.
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*
* NB: These plugins need to have already been loaded via require.js.
*/
dependencies: ["converse-singleton", "converse-controlbox", "converse-muc", "converse-bookmarks"],
initialize () {
_converse.RoomsList= RoomsList;
// Event handlers
api.listen.on('connected', async () => {
if (_converse.allow_bookmarks) {
@ -36,7 +22,6 @@ converse.plugins.add('converse-roomslist', {
} else {
await Promise.all([
api.waitUntil('chatBoxesFetched'),
api.waitUntil('roomsPanelRendered')
]);
}
});

View File

@ -1,9 +0,0 @@
import { _converse } from "@converse/headless/core";
import { Model } from '@converse/skeletor/src/model.js';
export default Model.extend({
defaults: {
"toggle-state": _converse.OPENED
}
});

View File

@ -1,5 +1,8 @@
import { html } from "lit-html";
import AddMUCModal from 'modals/add-muc.js';
import MUCListModal from 'modals/muc-list.js';
import { __ } from 'i18n';
import { _converse, api } from "@converse/headless/core";
import { html } from "lit-html";
const bookmark = (o) => {
@ -58,10 +61,23 @@ const room_item = (o) => {
export default (o) => {
const i18n_desc_rooms = __('Click to toggle the list of open groupchats');
const i18n_heading_chatrooms = __('Groupchats');
const i18n_title_list_rooms = __('Query for groupchats');
const i18n_title_new_room = __('Add a new groupchat');
return html`
<div class="d-flex controlbox-padded">
<span class="w-100 controlbox-heading controlbox-heading--groupchats">${i18n_heading_chatrooms}</span>
<a class="controlbox-heading__btn show-list-muc-modal fa fa-list-ul"
@click=${(ev) => api.modal.show(MUCListModal, { 'model': o.model }, ev)}
title="${i18n_title_list_rooms}" data-toggle="modal" data-target="#muc-list-modal"></a>
<a class="controlbox-heading__btn show-add-muc-modal fa fa-plus"
@click=${(ev) => api.modal.show(AddMUCModal, { 'model': o.model }, ev)}
title="${i18n_title_new_room}" data-toggle="modal" data-target="#add-chatrooms-modal"></a>
</div>
<div class="list-container list-container--openrooms ${ o.rooms.length ? '' : 'hidden' }">
<a class="list-toggle open-rooms-toggle controlbox-padded" title="${i18n_desc_rooms}" @click=${o.toggleRoomsList}>
<span class="fa ${ (o.toggle_state === o._converse.OPENED) ? 'fa-caret-down' : 'fa-caret-right' }"></span> ${__('Open Groupchats')}</a>
<span class="fa ${ (o.toggle_state === _converse.OPENED) ? 'fa-caret-down' : 'fa-caret-right' }"></span> ${__('Open Groupchats')}</a>
<div class="items-list rooms-list open-rooms-list ${ o.collapsed && 'collapsed' }">
${ o.rooms.map(room => room_item(Object.assign({room}, o))) }
</div>

View File

@ -1,26 +1,47 @@
import RoomDetailsModal from 'modals/muc-details.js';
import tpl_rooms_list from "./templates/roomslist.js";
import { ElementView } from '@converse/skeletor/src/element.js';
import { Model } from '@converse/skeletor/src/model.js';
import { __ } from 'i18n';
import { _converse, api, converse } from "@converse/headless/core";
import { render } from 'lit-html';
const { Strophe } = converse.env;
const u = converse.env.utils;
const RoomsListModel = Model.extend({
defaults: function () {
return {
'muc_domain': api.settings.get('muc_domain'),
'nick': _converse.getDefaultMUCNickname(),
'toggle-state': _converse.OPENED,
};
},
setDomain (jid) {
if (!api.settings.get('locked_muc_domain')) {
this.save('muc_domain', Strophe.getDomainFromJid(jid));
}
}
});
export class RoomsList extends ElementView {
initialize () {
this.model = _converse.chatboxes;
this.listenTo(this.model, 'add', this.renderIfChatRoom)
this.listenTo(this.model, 'remove', this.renderIfChatRoom)
this.listenTo(this.model, 'destroy', this.renderIfChatRoom)
this.listenTo(this.model, 'change', this.renderIfRelevantChange)
const id = `converse.roomspanel${_converse.bare_jid}`;
this.model = new (RoomsListModel.extend({
id,
'browserStorage': _converse.createStore(id)
}))();
this.model.fetch();
this.listenTo(_converse.chatboxes, 'add', this.renderIfChatRoom)
this.listenTo(_converse.chatboxes, 'remove', this.renderIfChatRoom)
this.listenTo(_converse.chatboxes, 'destroy', this.renderIfChatRoom)
this.listenTo(_converse.chatboxes, 'change', this.renderIfRelevantChange)
const id = `converse.roomslist${_converse.bare_jid}`;
this.list_model = new _converse.RoomsList({id});
this.list_model.browserStorage = _converse.createStore(id);
this.list_model.fetch();
this.render();
}
@ -36,21 +57,21 @@ export class RoomsList extends ElementView {
}
}
toHTML () {
return tpl_rooms_list({
'_converse': _converse,
render () {
render(tpl_rooms_list({
'addBookmark': ev => this.addBookmark(ev),
'allow_bookmarks': _converse.allow_bookmarks && _converse.bookmarks,
'closeRoom': ev => this.closeRoom(ev),
'collapsed': this.list_model.get('toggle-state') !== _converse.OPENED,
'collapsed': this.model.get('toggle-state') !== _converse.OPENED,
'currently_open': room => _converse.isUniView() && !room.get('hidden'),
'model': this.model,
'openRoom': ev => this.openRoom(ev),
'removeBookmark': ev => this.removeBookmark(ev),
'rooms': this.model.filter(m => m.get('type') === _converse.CHATROOMS_TYPE),
'rooms': _converse.chatboxes.filter(m => m.get('type') === _converse.CHATROOMS_TYPE),
'showRoomDetailsModal': ev => this.showRoomDetailsModal(ev),
'toggleRoomsList': ev => this.toggleRoomsList(ev),
'toggle_state': this.list_model.get('toggle-state')
});
'toggle_state': this.model.get('toggle-state')
}), this);
}
showRoomDetailsModal (ev) { // eslint-disable-line class-methods-use-this
@ -94,13 +115,13 @@ export class RoomsList extends ElementView {
const icon_el = ev.target.matches('.fa') ? ev.target : ev.target.querySelector('.fa');
if (icon_el.classList.contains("fa-caret-down")) {
u.slideIn(this.el.querySelector('.open-rooms-list')).then(() => {
this.list_model.save({'toggle-state': _converse.CLOSED});
this.model.save({'toggle-state': _converse.CLOSED});
icon_el.classList.remove("fa-caret-down");
icon_el.classList.add("fa-caret-right");
});
} else {
u.slideOut(this.el.querySelector('.open-rooms-list')).then(() => {
this.list_model.save({'toggle-state': _converse.OPENED});
this.model.save({'toggle-state': _converse.OPENED});
icon_el.classList.remove("fa-caret-right");
icon_el.classList.add("fa-caret-down");
});

View File

@ -1,10 +0,0 @@
import { html } from "lit-html";
export default (o) => html`
<div class="d-flex controlbox-padded">
<span class="w-100 controlbox-heading controlbox-heading--groupchats">${o.heading_chatrooms}</span>
<a class="controlbox-heading__btn show-list-muc-modal fa fa-list-ul" title="${o.title_list_rooms}" data-toggle="modal" data-target="#muc-list-modal"></a>
<a class="controlbox-heading__btn show-add-muc-modal fa fa-plus" title="${o.title_new_room}" data-toggle="modal" data-target="#add-chatrooms-modal"></a>
</div>
<div class="list-container list-container--openrooms hidden"></div>
<div class="list-container list-container--bookmarks hidden"></div>`;