diff --git a/sass/_chatrooms.scss b/sass/_chatrooms.scss index 3b2a3d603..d74dfa8cb 100644 --- a/sass/_chatrooms.scss +++ b/sass/_chatrooms.scss @@ -181,9 +181,10 @@ margin-bottom: 0.5em; display: flex; flex-direction: row; - .fa-user-plus { - margin-top: 0.2em; - } + } + + .fa-user-plus { + margin-right: 0.5em; } .occupants-heading { diff --git a/sass/_core.scss b/sass/_core.scss index b67acd92d..dd10de6c0 100644 --- a/sass/_core.scss +++ b/sass/_core.scss @@ -541,6 +541,22 @@ body.converse-fullscreen { } } + .btn-circle { + width: 32px; + height: 32px; + text-align: center; + padding: 0.5em 0; + font-size: var(--font-size-small); + line-height: 1.428571429; + border-radius: 50%; + } + + .btn { + &.fa { + color: white !important; + } + } + .badge-groupchat { background-color: var(--chatroom-badge-color); border-color: transparent; diff --git a/spec/muc.js b/spec/muc.js index 04afff2fa..d3c26df73 100644 --- a/spec/muc.js +++ b/spec/muc.js @@ -1944,16 +1944,16 @@ expect(view.model.getOwnAffiliation()).toBe('owner'); expect(view.model.features.get('open')).toBe(false); - expect(view.el.querySelector('.occupants-header .fa-user-plus')).not.toBe(null); + expect(view.el.querySelector('.open-invite-modal')).not.toBe(null); // Members can't invite if the room isn't open view.model.getOwnOccupant().set('affiliation', 'member'); - await u.waitUntil(() => view.el.querySelector('.occupants-header .fa-user-plus') === null); + await u.waitUntil(() => view.el.querySelector('.open-invite-modal') === null); view.model.features.set('open', 'true'); - await u.waitUntil(() => view.el.querySelector('.occupants-header .fa-user-plus')); + await u.waitUntil(() => view.el.querySelector('.open-invite-modal')); - view.el.querySelector('.occupants-header .fa-user-plus').click(); + view.el.querySelector('.open-invite-modal').click(); const modal = view.sidebar_view.muc_invite_modal; await u.waitUntil(() => u.isVisible(modal.el), 1000) @@ -2506,26 +2506,38 @@ ]; await test_utils.openAndEnterChatRoom(_converse, 'room@conference.example.org', 'romeo', features); const jid = 'room@conference.example.org'; - const chatroomview = _converse.chatboxviews.get(jid); - let features_list = chatroomview.el.querySelector('.features-list'); - let features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s); - expect(_.difference(["Password protected", "Open", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0); - expect(chatroomview.model.features.get('hidden')).toBe(false); - expect(chatroomview.model.features.get('mam_enabled')).toBe(false); - expect(chatroomview.model.features.get('membersonly')).toBe(false); - expect(chatroomview.model.features.get('moderated')).toBe(false); - expect(chatroomview.model.features.get('nonanonymous')).toBe(true); - expect(chatroomview.model.features.get('open')).toBe(true); - expect(chatroomview.model.features.get('passwordprotected')).toBe(true); - expect(chatroomview.model.features.get('persistent')).toBe(false); - expect(chatroomview.model.features.get('publicroom')).toBe(true); - expect(chatroomview.model.features.get('semianonymous')).toBe(false); - expect(chatroomview.model.features.get('temporary')).toBe(true); - expect(chatroomview.model.features.get('unmoderated')).toBe(true); - expect(chatroomview.model.features.get('unsecured')).toBe(false); - expect(chatroomview.el.querySelector('.chatbox-title__text').textContent.trim()).toBe('Room'); + const view = _converse.chatboxviews.get(jid); - chatroomview.el.querySelector('.configure-chatroom-button').click(); + const info_el = view.el.querySelector(".show-room-details-modal"); + info_el.click(); + const modal = view.model.room_details_modal; + await u.waitUntil(() => u.isVisible(modal.el), 1000); + + let features_list = modal.el.querySelector('.features-list'); + let features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s); + + expect(features_shown.join(' ')).toBe( + 'Password protected - This groupchat requires a password before entry '+ + 'Open - Anyone can join this groupchat '+ + 'Temporary - This groupchat will disappear once the last person leaves '+ + 'Not anonymous - All other groupchat participants can see your XMPP address '+ + 'Not moderated - Participants entering this groupchat can write right away'); + expect(view.model.features.get('hidden')).toBe(false); + expect(view.model.features.get('mam_enabled')).toBe(false); + expect(view.model.features.get('membersonly')).toBe(false); + expect(view.model.features.get('moderated')).toBe(false); + expect(view.model.features.get('nonanonymous')).toBe(true); + expect(view.model.features.get('open')).toBe(true); + expect(view.model.features.get('passwordprotected')).toBe(true); + expect(view.model.features.get('persistent')).toBe(false); + expect(view.model.features.get('publicroom')).toBe(true); + expect(view.model.features.get('semianonymous')).toBe(false); + expect(view.model.features.get('temporary')).toBe(true); + expect(view.model.features.get('unmoderated')).toBe(true); + expect(view.model.features.get('unsecured')).toBe(false); + expect(view.el.querySelector('.chatbox-title__text').textContent.trim()).toBe('Room'); + + view.el.querySelector('.configure-chatroom-button').click(); const IQs = _converse.connection.IQ_stanzas; let iq = await u.waitUntil(() => _.filter( @@ -2602,9 +2614,9 @@ _converse.connection._dataRecv(test_utils.createRequest(response_el)); const el = await u.waitUntil(() => document.querySelector('.chatroom-form legend')); expect(el.textContent.trim()).toBe("Configuration for room@conference.example.org"); - sizzle('[name="muc#roomconfig_membersonly"]', chatroomview.el).pop().click(); - sizzle('[name="muc#roomconfig_roomname"]', chatroomview.el).pop().value = "New room name" - chatroomview.el.querySelector('.chatroom-form input[type="submit"]').click(); + sizzle('[name="muc#roomconfig_membersonly"]', view.el).pop().click(); + sizzle('[name="muc#roomconfig_roomname"]', view.el).pop().value = "New room name" + view.el.querySelector('.chatroom-form input[type="submit"]').click(); iq = await u.waitUntil(() => _.filter(IQs, iq => u.matchesSelector(iq, `iq[to="${jid}"][type="set"]`)).pop()); const result = $iq({ @@ -2656,24 +2668,30 @@ _converse.connection._dataRecv(test_utils.createRequest(features_stanza)); - await u.waitUntil(() => new Promise(success => chatroomview.model.features.on('change', success))); - features_list = chatroomview.el.querySelector('.features-list'); + await u.waitUntil(() => new Promise(success => view.model.features.on('change', success))); + features_list = modal.el.querySelector('.features-list'); features_shown = features_list.textContent.split('\n').map(s => s.trim()).filter(s => s); - expect(_.difference(["Password protected", "Hidden", "Members only", "Temporary", "Not anonymous", "Not moderated"], features_shown).length).toBe(0); - expect(chatroomview.model.features.get('hidden')).toBe(true); - expect(chatroomview.model.features.get('mam_enabled')).toBe(false); - expect(chatroomview.model.features.get('membersonly')).toBe(true); - expect(chatroomview.model.features.get('moderated')).toBe(false); - expect(chatroomview.model.features.get('nonanonymous')).toBe(true); - expect(chatroomview.model.features.get('open')).toBe(false); - expect(chatroomview.model.features.get('passwordprotected')).toBe(true); - expect(chatroomview.model.features.get('persistent')).toBe(false); - expect(chatroomview.model.features.get('publicroom')).toBe(false); - expect(chatroomview.model.features.get('semianonymous')).toBe(false); - expect(chatroomview.model.features.get('temporary')).toBe(true); - expect(chatroomview.model.features.get('unmoderated')).toBe(true); - expect(chatroomview.model.features.get('unsecured')).toBe(false); - expect(chatroomview.el.querySelector('.chatbox-title__text').textContent.trim()).toBe('New room name'); + expect(features_shown.join(' ')).toBe( + 'Password protected - This groupchat requires a password before entry '+ + 'Hidden - This groupchat is not publicly searchable '+ + 'Members only - This groupchat is restricted to members only '+ + 'Temporary - This groupchat will disappear once the last person leaves '+ + 'Not anonymous - All other groupchat participants can see your XMPP address '+ + 'Not moderated - Participants entering this groupchat can write right away'); + expect(view.model.features.get('hidden')).toBe(true); + expect(view.model.features.get('mam_enabled')).toBe(false); + expect(view.model.features.get('membersonly')).toBe(true); + expect(view.model.features.get('moderated')).toBe(false); + expect(view.model.features.get('nonanonymous')).toBe(true); + expect(view.model.features.get('open')).toBe(false); + expect(view.model.features.get('passwordprotected')).toBe(true); + expect(view.model.features.get('persistent')).toBe(false); + expect(view.model.features.get('publicroom')).toBe(false); + expect(view.model.features.get('semianonymous')).toBe(false); + expect(view.model.features.get('temporary')).toBe(true); + expect(view.model.features.get('unmoderated')).toBe(true); + expect(view.model.features.get('unsecured')).toBe(false); + expect(view.el.querySelector('.chatbox-title__text').textContent.trim()).toBe('New room name'); done(); })); diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index 96028f709..1412801e2 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -615,6 +615,7 @@ converse.plugins.add('converse-muc-views', { initialize () { _converse.BootstrapModal.prototype.initialize.apply(this, arguments); this.listenTo(this.model, 'change', this.render); + this.listenTo(this.model.features, 'change', this.render); this.listenTo(this.model.occupants, 'add', this.render); this.listenTo(this.model.occupants, 'change', this.render); }, diff --git a/src/templates/muc_sidebar.js b/src/templates/muc_sidebar.js index 01df0c6a9..9bc3956cc 100644 --- a/src/templates/muc_sidebar.js +++ b/src/templates/muc_sidebar.js @@ -1,9 +1,8 @@ import { html } from "lit-html"; import { __ } from '@converse/headless/i18n'; -import { pick } from "lodash"; -import converse from "@converse/headless/converse-core"; import tpl_occupant from "./occupant.js"; + const PRETTY_CHAT_STATUS = { 'offline': 'Offline', 'unavailable': 'Unavailable', @@ -16,77 +15,21 @@ const PRETTY_CHAT_STATUS = { const occupant_hint = (occupant) => __('Click to mention %1$s in your message.', occupant.get('nick')) -const i18n_archived = __('Message archiving'); -const i18n_archived_hint = __('Messages are archived on the server'); -const i18n_features = __('Features'); -const i18n_hidden = __('Hidden'); -const i18n_invite_hint = __('Invite people to join this groupchat'); -const i18n_members_only = __('Members only'); -const i18n_members_only_hint = __('this groupchat is restricted to members only'); -const i18n_moderated = __('Moderated'); -const i18n_moderated_hint = __('Participants entering this groupchat need to request permission to write'); -const i18n_no_password = __('No password'); -const i18n_no_password_hint = __('This groupchat does not require a password upon entry'); -const i18n_non_anon_hint = __('All other groupchat participants can see your XMPP address'); -const i18n_not_anon = __('Not anonymous'); -const i18n_not_moderated = __('Not moderated'); -const i18n_not_searchable_hint = __('This groupchat is not publicly searchable'); -const i18n_open = __('Open'); -const i18n_open_hint = __('Anyone can join this groupchat'); +const i18n_invite_hint = __('Invite someone'); const i18n_participants = __('Participants'); -const i18n_password = __('Password protected') -const i18n_password_hint = __('This groupchat requires a password before entry'); -const i18n_persistent = __('Persistent'); -const i18n_persistent_hint = __('This groupchat persists even if it\'s unoccupied'); -const i18n_public = __('Public'); -const i18n_searchable_hint = __('This groupchat is publicly searchable'); -const i18n_semi_anon = __('Semi-anonymous'); -const i18n_semi_anon_hint = __('Only moderators can see your XMPP address'); -const i18n_temporary = __('Temporary'); -const i18n_temporary_hint = __('This groupchat will disappear once the last person leaves'); -const i18n_unmoderated_hint = __('Participants entering this groupchat can write right away'); -function renderFeatures (o) { - const picks = pick(o.features.attributes, converse.ROOM_FEATURES); - const iteratee = (a, v) => a || v; - if (Object.values(picks).reduce(iteratee)) { - return tpl_features(o.features.toJSON()); - } else { - return ''; - } -} - - -const tpl_features = (o) => html` -
-

${i18n_features}

- -
-`; - -const invite_button = (o) => { +const invite_widget = (o) => { if (o.invitesAllowed()) { return html` - `; + + + ${i18n_invite_hint} + `; } else { return ''; } @@ -98,7 +41,6 @@ export default (o) => html`
${i18n_participants} - ${ invite_button(o) }
@@ -113,5 +55,5 @@ export default (o) => html` ); }) } - ${ renderFeatures(o) } + ${ invite_widget(o) } `;