Commit 3400acbf authored by JC Brand's avatar JC Brand

Show MUC buttons in a dropdown menu

- Get rid of the ChatBoxHeading class
- Add support for showing standalone buttons in overlay viewmode
parent 2a7773dc
This diff is collapsed.
......@@ -41,7 +41,7 @@
color: #ffffff;
font-size: 100%;
margin: 0;
padding: 1rem;;
padding: 0;
position: relative;
&.chat-head-chatbox {
......@@ -54,22 +54,27 @@
.chat-head__desc {
color: var(--chat-head-color-lighten-50-percent);
font-size: 75%;
font-size: 80%;
font-size: var(--font-size-small);
margin: 0;
overflow: hidden;
padding: 0;
padding: 0.5rem 1rem 1rem 1rem;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.chatbox-title {
padding: 0.75rem 1rem 0 1rem;
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
}
.chatbox-title--no-desc {
padding: 0.75rem 1rem;
}
.chatbox-title--row {
display: flex;
flex-direction: row;
......@@ -137,7 +142,6 @@
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: var(--chat-head-color);
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
z-index: 2;
overflow: hidden;
......@@ -175,7 +179,6 @@
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
background-color: var(--chat-head-color);
border-bottom-left-radius: var(--chatbox-border-radius);
border-bottom-right-radius: var(--chatbox-border-radius);
......@@ -426,8 +429,10 @@
#conversejs.converse-embedded,
#conversejs.converse-overlayed {
.chat-head {
.controlbox-head {
padding: 0.5em;
}
.chat-head {
border-top-left-radius: var(--chatbox-border-radius);
border-top-right-radius: var(--chatbox-border-radius);
@media screen and (max-height: $mobile-landscape-height) {
......@@ -474,6 +479,22 @@
}
}
}
.chat-body {
height: calc(100% - var(--overlayed-chat-head-height));
}
.chatbox-title {
padding: 0.5rem 0.75rem 0 0.75rem;
}
.chatbox-title--no-desc {
padding: 0.5rem 0.75rem;
}
converse-dropdown {
.btn--standalone {
padding: 0 0 0 0.5em;
}
}
}
}
......@@ -497,12 +518,6 @@
bottom: 0;
}
.chat-head {
.chat-head__desc {
font-size: 70%;
}
}
.chatbox {
margin: 0;
.box-flyout {
......@@ -590,7 +605,6 @@
}
.chatbox {
.box-flyout {
background-color: var(--chat-head-color);
box-shadow: none;
height: var(--fullpage-chat-height);
min-height: calc(var(--fullpage-chat-height) / 2);
......@@ -598,6 +612,7 @@
overflow: hidden;
}
.chat-body {
height: calc(100% - var(--fullpage-chat-head-height));
background-color: var(--chat-head-color);
}
.chat-title {
......
......@@ -42,15 +42,28 @@
border-bottom: var(--chatroom-head-border-bottom);
.chat-head__desc {
color: var(--chatroom-head-description-color);
color: var(--chatroom-head-color);
display: var(--chatroom-head-description-display);
font-size: 70%;
margin-top: 3px;
border-left: var(--chatroom-head-description-border-left);
padding-left: var(--chatroom-head-description-padding-left);
a {
color: var(--chatroom-head-description-link-color);
}
&:hover {
button {
display: inline-block;
}
}
}
.chatbox-title {
.btn--transparent {
i {
color: var(--chatroom-head-color);
}
}
}
.chatbox-title__buttons {
background-color: var(--chatroom-head-bg-color);
}
a, a:visited, a:hover, a:not([href]):not([tabindex]) {
......@@ -73,6 +86,7 @@
display: var(--heading-display);
font-weight: var(--chatroom-head-title-font-weight);
padding-right: var(--chatroom-head-title-padding-right);
margin: auto 0;
.chatroom-jid {
font-size: var(--font-size-small);
}
......@@ -214,7 +228,6 @@
overflow-y: auto;
flex-basis: 0;
flex-grow: 1;
border-bottom: var(--occupants-border-bottom);
}
li {
cursor: default;
......@@ -467,10 +480,6 @@
.box-flyout {
width: 100%;
.chat-head__desc {
font-size: 70%;
}
.chatroom-body {
.chat-area {
&.full {
......
......@@ -193,6 +193,20 @@ body.converse-fullscreen {
}
}
.dropdown-item {
padding: 0.5rem 1rem;
.fa {
margin-right: 0.75rem;
}
&:active, &.selected {
color: white !important;
background-color: var(--list-item-open-color);
.fa {
color: white !important;
}
}
}
.popover {
position: fixed;
}
......@@ -352,6 +366,10 @@ body.converse-fullscreen {
.fa, .far, .fas {
color: #fff;
margin-right: 0.5em;
&.only-icon {
margin-right: 0;
}
}
}
......@@ -501,9 +519,9 @@ body.converse-fullscreen {
}
.avatar-autocomplete {
margin-right: 0.5em;
margin-right: 0.5em;
vertical-align: middle;
}
}
.activated {
display: block !important;
......@@ -541,6 +559,11 @@ body.converse-fullscreen {
}
}
.btn--transparent {
background: transparent;
border: none;
}
.btn-circle {
width: 30px;
height: 30px;
......
......@@ -117,11 +117,8 @@ $mobile_portrait_length: 480px !default;
--chatroom-head-button-color: var(--chatroom-head-bg-color);
--chatroom-head-title-font-weight: normal;
--chatroom-head-title-padding-right: 0px;
--chatroom-head-description-color: var(--chatroom-head-bg-color-lighten-25-percent);
--chatroom-head-description-link-color: white;
--chatroom-head-description-display: block;
--chatroom-head-description-border-left: 0px;
--chatroom-head-description-padding-left: 0px;
--chatroom-head-border-bottom: 0px;
--chatroom-width: 500px;
--chatroom-correcting-color: #fadfd7; // lighten($red, 30%)
......
......@@ -36,7 +36,7 @@
const view = _converse.chatboxviews.get(jid);
spyOn(view, 'renderBookmarkForm').and.callThrough();
spyOn(view, 'closeForm').and.callThrough();
await u.waitUntil(() => !_.isNull(view.el.querySelector('.toggle-bookmark')));
await u.waitUntil(() => view.el.querySelector('.toggle-bookmark') !== null);
let toggle = view.el.querySelector('.toggle-bookmark');
expect(toggle.title).toBe('Bookmark this groupchat');
toggle.click();
......@@ -216,8 +216,7 @@
);
await _converse.api.rooms.open(`lounge@montague.lit`);
const view = _converse.chatboxviews.get('lounge@montague.lit');
let bookmark_icon = await u.waitUntil(() => view.el.querySelector('.toggle-bookmark'));
expect(_.includes(bookmark_icon.classList, 'button-on')).toBeFalsy();
expect(view.el.querySelector('.chatbox-title__text .fa-bookmark')).toBe(null);
_converse.bookmarks.create({
'jid': view.model.get('jid'),
'autojoin': false,
......@@ -225,11 +224,9 @@
'nick': ' some1'
});
view.model.set('bookmarked', true);
bookmark_icon = await u.waitUntil(() => view.el.querySelector('.toggle-bookmark'));
expect(_.includes(bookmark_icon.classList, 'button-on')).toBeTruthy();
expect(view.el.querySelector('.chatbox-title__text .fa-bookmark')).not.toBe(null);
view.model.set('bookmarked', false);
bookmark_icon = await u.waitUntil(() => view.el.querySelector('.toggle-bookmark'));
expect(_.includes(bookmark_icon.classList, 'button-on')).toBeFalsy();
expect(view.el.querySelector('.chatbox-title__text .fa-bookmark')).toBe(null);
done();
}));
......@@ -256,14 +253,12 @@
expect(_converse.bookmarks.length).toBe(1);
await u.waitUntil(() => _converse.chatboxes.length >= 1);
expect(view.model.get('bookmarked')).toBeTruthy();
let bookmark_icon = await u.waitUntil(() => view.el.querySelector('.toggle-bookmark'));
expect(u.hasClass('button-on', bookmark_icon)).toBeTruthy();
expect(view.el.querySelector('.chatbox-title__text .fa-bookmark')).not.toBe(null);
spyOn(_converse.connection, 'getUniqueId').and.callThrough();
const bookmark_icon = view.el.querySelector('.toggle-bookmark');
bookmark_icon.click();
bookmark_icon = await u.waitUntil(() => view.el.querySelector('.toggle-bookmark'));
expect(view.toggleBookmark).toHaveBeenCalled();
expect(u.hasClass('button-on', bookmark_icon)).toBeFalsy();
expect(view.el.querySelector('.chatbox-title__text .fa-bookmark')).toBe(null);
expect(_converse.bookmarks.length).toBe(0);
// Check that an IQ stanza is sent out, containing no
......
......@@ -364,9 +364,6 @@
expect(trimmedview.restore).toHaveBeenCalled();
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxMaximized', jasmine.any(Object));
const toggle_el = sizzle('.toggle-chatbox-button', chatview.el).pop();
expect(u.hasClass('fa-minus', toggle_el)).toBeTruthy();
expect(u.hasClass('fa-plus', toggle_el)).toBeFalsy();
expect(chatview.model.get('minimized')).toBeFalsy();
done();
}));
......
......@@ -149,6 +149,7 @@
const cbview = _converse.chatboxviews.get('controlbox');
await u.waitUntil(() => cbview.el.querySelectorAll(".open-headline").length);
const hlview = _converse.chatboxviews.get('notify.example.com');
await u.isVisible(hlview.el);
const close_el = hlview.el.querySelector('.close-chatbox-button');
close_el.click();
await u.waitUntil(() => cbview.el.querySelectorAll(".open-headline").length === 0);
......
......@@ -1390,9 +1390,7 @@
}).up()
.c('status', {code: '110'});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(u.isVisible(view.el.querySelector('.toggle-chatbox-button'))).toBeTruthy();
await u.waitUntil(() => !_.isNull(view.el.querySelector('.configure-chatroom-button')))
expect(u.isVisible(view.el.querySelector('.configure-chatroom-button'))).toBeTruthy();
await u.waitUntil(() => view.el.querySelector('.configure-chatroom-button') !== null);
view.el.querySelector('.configure-chatroom-button').click();
/* Check that an IQ is sent out, asking for the
......@@ -1949,13 +1947,14 @@
// Members can't invite if the room isn't open
view.model.getOwnOccupant().set('affiliation', 'member');
await u.waitUntil(() => view.el.querySelector('.open-invite-modal') === null);
view.model.features.set('open', 'true');
await u.waitUntil(() => view.el.querySelector('.open-invite-modal'));
view.el.querySelector('.open-invite-modal').click();
const modal = view.sidebar_view.muc_invite_modal;
const modal = view.muc_invite_modal;
await u.waitUntil(() => u.isVisible(modal.el), 1000)
expect(modal.el.querySelectorAll('#invitee_jids').length).toBe(1);
......@@ -2174,8 +2173,8 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza));
const view = _converse.chatboxviews.get('jdev@conference.jabber.org');
await new Promise(resolve => view.model.once('change:subject', resolve));
expect(sizzle('.chat-event:last').pop().textContent.trim()).toBe('Topic set by ralphm');
expect(sizzle('.chat-topic:last').pop().textContent.trim()).toBe(text);
expect(sizzle('.chat-event:last', view.el).pop().textContent.trim()).toBe('Topic set by ralphm');
expect(view.el.querySelector('.chat-head__desc').textContent.trim()).toBe(text);
stanza = u.toStanza(
......@@ -2185,7 +2184,6 @@
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise(resolve => view.once('messageInserted', resolve));
expect(sizzle('.chat-topic', view.el).length).toBe(1);
expect(sizzle('.chat-msg__subject', view.el).length).toBe(1);
expect(sizzle('.chat-msg__subject', view.el).pop().textContent.trim()).toBe('This is a message subject');
expect(sizzle('.chat-msg__text').length).toBe(1);
......@@ -2199,7 +2197,7 @@
</message>`);
_converse.connection._dataRecv(test_utils.createRequest(stanza));
await new Promise(resolve => view.model.once('change:subject', resolve));
expect(view.el.querySelector('.chat-head__desc').textContent.trim()).toBe("");
expect(view.el.querySelector('.chat-head__desc')).toBe(null);
expect(view.el.querySelector('.chat-info:last-child').textContent.trim()).toBe("Topic cleared by ralphm");
done();
}));
......@@ -2218,7 +2216,7 @@
'author': 'ralphm'
}});
expect(sizzle('.chat-event:last').pop().textContent.trim()).toBe('Topic set by ralphm');
expect(sizzle('.chat-topic:last').pop().textContent.trim()).toBe(subject);
expect(view.el.querySelector('.chat-head__desc').textContent.trim()).toBe(subject);
done();
}));
......@@ -2875,8 +2873,9 @@
spyOn(_converse.api, "trigger").and.callThrough();
spyOn(view.model, 'leave');
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true));
view.el.querySelector('.close-chatbox-button').click();
expect(view.close).toHaveBeenCalled();
await u.waitUntil(() => view.close.calls.count());
expect(view.model.leave).toHaveBeenCalled();
await u.waitUntil(() => _converse.api.trigger.calls.count());
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
......@@ -4853,7 +4852,7 @@
await u.waitUntil(() => _converse.chatboxes.length > 1);
expect(sizzle('.chatroom', _converse.el).filter(u.isVisible).length).toBe(1); // There should now be an open chatroom
var view = _converse.chatboxviews.get('inverness@chat.shakespeare.lit');
expect(view.el.querySelector('.chat-head-chatroom').textContent.trim()).toBe("Macbeth's Castle");
expect(view.el.querySelector('.chatbox-title__text').textContent.trim()).toBe("Macbeth's Castle");
done();
}));
......
......@@ -95,6 +95,7 @@
`</presence>`)
await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
await u.waitUntil(() => !u.isVisible(modal.el));
cbview.el.querySelector('.change-status').click()
await u.waitUntil(() => modal.el.getAttribute('aria-hidden') === "false", 1000);
modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
......
......@@ -23,7 +23,6 @@
await u.waitUntil(() => _converse.chatboxes.length > 1);
const view = _converse.chatboxviews.get(contact_jid);
let show_modal_button = view.el.querySelector('.show-user-details-modal');
expect(u.isVisible(show_modal_button)).toBeTruthy();
show_modal_button.click();
const modal = view.user_details_modal;
await u.waitUntil(() => u.isVisible(modal.el), 1000);
......@@ -33,7 +32,7 @@
expect(u.isVisible(remove_contact_button)).toBeTruthy();
remove_contact_button.click();
await u.waitUntil(() => modal.el.getAttribute('aria-hidden'), 1000);
await u.waitUntil(() => !u.isVisible(modal.el));
show_modal_button = view.el.querySelector('.show-user-details-modal');
show_modal_button.click();
remove_contact_button = modal.el.querySelector('button.remove-contact');
......@@ -51,7 +50,6 @@
await test_utils.openChatBoxFor(_converse, contact_jid)
const view = _converse.chatboxviews.get(contact_jid);
let show_modal_button = view.el.querySelector('.show-user-details-modal');
expect(u.isVisible(show_modal_button)).toBeTruthy();
show_modal_button.click();
const modal = view.user_details_modal;
await u.waitUntil(() => u.isVisible(modal.el), 2000);
......
import { html } from 'lit-element';
import { CustomElement } from './element.js';
import { until } from 'lit-html/directives/until.js';
import DOMNavigator from "../dom-navigator";
import converse from "@converse/headless/converse-core";
const u = converse.env.utils;
export class Dropdown extends CustomElement {
static get properties () {
return {
'items': { type: Array }
}
}
render () {
return html`
<div class="dropleft">
<button type="button" class="btn btn--transparent btn--standalone" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-bars only-icon"></i>
</button>
<div class="dropdown-menu">
${ this.items.map(b => until(b, '')) }
</div>
</div>
`;
}
firstUpdated () {
this.menu = this.querySelector('.dropdown-menu');
this.dropdown = this.firstElementChild;
this.button = this.dropdown.querySelector('button');
this.dropdown.addEventListener('click', ev => this.toggleMenu(ev));
this.dropdown.addEventListener('keyup', ev => this.handleKeyUp(ev));
document.addEventListener('click', ev => !this.contains(ev.target) && this.hideMenu(ev));
this.initArrowNavigation();
}
initArrowNavigation () {
if (!this.navigator) {
const options = {
'selector': '.dropdown-item',
'onSelected': el => el.focus()
};
this.navigator = new DOMNavigator(this.menu, options);
}
}
enableArrowNavigation (ev) {
if (ev) {
ev.preventDefault();
ev.stopPropagation();
}
this.navigator.enable();
this.navigator.select(this.menu.firstElementChild);
}
hideMenu () {
u.removeClass('show', this.menu);
this.navigator.disable();
this.button.setAttribute('aria-expanded', false);
this.button.blur();
}
showMenu () {
u.addClass('show', this.menu);
this.button.setAttribute('aria-expanded', true);
}
toggleMenu () {
if (u.hasClass('show', this.menu)) {
this.hideMenu();
} else {
this.showMenu();
}
}
handleKeyUp (ev) {
if (ev.keyCode === converse.keycodes.ESCAPE) {
this.hideMenu();
} else if (ev.keyCode === converse.keycodes.DOWN_ARROW && !this.navigator.enabled) {
this.enableArrowNavigation(ev);
}
}
}
window.customElements.define('converse-dropdown', Dropdown);
import { LitElement } from 'lit-element';
export class CustomElement extends LitElement {
createRenderRoot () {
// Render without the shadow DOM
return this;
}
}
......@@ -7,12 +7,10 @@
import "@converse/headless/converse-muc";
import { Model } from 'skeletor.js/src/model.js';
import { View } from 'skeletor.js/src/view.js';
import { html } from "lit-html";
import { __ } from '@converse/headless/i18n';
import converse from "@converse/headless/converse-core";
import tpl_bookmarks_list from "templates/bookmarks_list.js"
import tpl_muc_bookmark_form from "templates/muc_bookmark_form.js";
import tpl_chatroom_bookmark_toggle from "templates/chatroom_bookmark_toggle.html";
const { Strophe, _ } = converse.env;
const u = converse.env.utils;
......@@ -37,21 +35,24 @@ converse.plugins.add('converse-bookmark-views', {
// plugin architecture they will replace existing methods on the
// relevant objects or classes.
ChatRoomView: {
events: {
'click .toggle-bookmark': 'toggleBookmark'
},
getHeadingButtons () {
const { _converse } = this.__super__;
const buttons = this.__super__.getHeadingButtons.call(this);
if (_converse.allow_bookmarks) {
const supported = _converse.checkBookmarksSupport();
const info_toggle_bookmark = this.model.get('bookmarked') ? __('Unbookmark this groupchat') : __('Bookmark this groupchat');
const bookmarked = this.model.get('bookmarked');
const template = html`<a class="chatbox-btn toggle-bookmark fa fa-bookmark ${bookmarked ? 'button-on' : ''}" title="${info_toggle_bookmark}"></a>`;
const data = {
'i18n_title': bookmarked ? __('Unbookmark this groupchat') : __('Bookmark this groupchat'),
'i18n_text': bookmarked ? __('Unbookmark') : __('Bookmark'),
'handler': ev => this.toggleBookmark(ev),
'a_class': 'toggle-bookmark',
'icon_class': 'fa-bookmark',
'name': 'bookmark'
}
const names = buttons.map(t => t.name);
const idx = names.indexOf('configure');
const template_promise = supported.then(s => s ? template : '');
return idx > -1 ? [...buttons.slice(0, idx), template_promise, ...buttons.slice(idx)] : [template_promise, ...buttons];
const data_promise = supported.then(s => s ? data : '');
return idx > -1 ? [...buttons.slice(0, idx), data_promise, ...buttons.slice(idx)] : [data_promise, ...buttons];
}
return buttons;
}
......@@ -100,25 +101,6 @@ converse.plugins.add('converse-bookmark-views', {
});
const bookmarkableChatRoomView = {
renderBookmarkToggle () {
const bookmark_button = tpl_chatroom_bookmark_toggle(
_.assignIn(this.model.toJSON(), {
'info_toggle_bookmark': this.model.get('bookmarked') ?
__('Unbookmark this groupchat') :
__('Bookmark this groupchat'),
'bookmarked': this.model.get('bookmarked')
}));
const buttons_row = this.el.querySelector('.chatbox-title__buttons')
const close_button = buttons_row.querySelector('.close-chatbox-button');
if (close_button) {
close_button.insertAdjacentHTML('afterend', bookmark_button);
} else {
buttons_row.insertAdjacentHTML('beforeEnd', bookmark_button);
}
},
/**
* Set whether the groupchat is bookmarked or not.
* @private
......
This diff is collapsed.
......@@ -138,7 +138,8 @@ converse.plugins.add('converse-headlines-view', {
this.listenTo(this.model, 'destroy', this.hide);
this.listenTo(this.model, 'change:minimized', this.onMinimizedChanged);
this.render().insertHeading()
this.render();
this.renderHeading();
this.updateAfterMessagesFetched();
this.insertIntoDOM().hide();
/**
......
......@@ -8,7 +8,6 @@ import { Model } from 'skeletor.js/src/model.js';
import { Overview } from "skeletor.js/src/overview";
import { View } from "skeletor.js/src/view";
import { __ } from '@converse/headless/i18n';
import { html } from "lit-html";
import converse from "@converse/headless/converse-core";
import tpl_chats_panel from "templates/chats_panel.html";
import tpl_toggle_chats from "templates/toggle_chats.html";
......@@ -74,10 +73,6 @@ converse.plugins.add('converse-minimize', {
},
ChatBoxView: {
events: {
'click .toggle-chatbox-button': 'minimize',
},
initialize () {
this.listenTo(this.model, 'change:minimized', this.onMinimizedChanged)
return this.__super__.initialize.apply(this, arguments);
......@@ -113,25 +108,27 @@ converse.plugins.add('converse-minimize', {
if (!this.model.get('minimized')) {
return this.__super__.setChatBoxWidth.call(this, width);
}
}
},
},
ChatBoxHeading: {
getHeadingButtons () {
const { _converse } = this.__super__;
const buttons = this.__super__.getHeadingButtons.call(this);
const info_minimize = __('Minimize this chat box');
const template = html`<a class="chatbox-btn toggle-chatbox-button fa fa-minus" title="${info_minimize}"></a>`;
const data = {
'a_class': 'toggle-chatbox-button',
'handler': ev => this.minimize(ev),
'i18n_text': __('Minimize'),
'i18n_title': __('Minimize this chat'),
'icon_class': "fa-minus",
'name': 'minimize',
'standalone': _converse.view_mode === 'overlayed'
}
const names = buttons.map(t => t.name);
const idx = names.indexOf('close');
return idx > -1 ? [...buttons.slice(0, idx+1), template, ...buttons.slice(idx+1)] : [template, ...buttons];
return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons];
}
},
ChatRoomView: {
events: {
'click .toggle-chatbox-button': 'minimize',
},
initialize () {
this.listenTo(this.model, 'change:minimized', this.onMinimizedChanged)
const result = this.__super__.initialize.apply(this, arguments);
......@@ -142,12 +139,20 @@ converse.plugins.add('converse-minimize', {
},
getHeadingButtons () {
const { _converse } = this.__super__;
const buttons = this.__super__.getHeadingButtons.call(this);
const info_minimize = __('Minimize this groupchat');
const template = html`<a class="chatbox-btn toggle-chatbox-button fa fa-minus" title="${info_minimize}"></a>`;
const data = {
'a_class': 'toggle-chatbox-button',
'handler': ev => this.minimize(ev),
'i18n_text': __('Minimize'),
'i18n_title': __('Minimize this groupchat'),
'icon_class': "fa-minus",
'name': 'minimize',
'standalone': _converse.view_mode === 'overlayed'
}
const names = buttons.map(t => t.name);
const idx = names.indexOf('signout');
return idx > -1 ? [...buttons.slice(0, idx+1), template, ...buttons.slice(idx+1)] : [template, ...buttons];
return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons];
}
}
},
......
This diff is collapsed.
......@@ -340,7 +340,6 @@ converse.plugins.add('converse-muc', {
// mention the user and `num_unread_general` to indicate
// generally unread messages (which *includes* mentions!).
'num_unread_general': 0,
'bookmarked': false,
'chat_state': undefined,
'hidden': ['mobile', 'fullscreen'].includes(_converse.view_mode),
......@@ -348,8 +347,8 @@ converse.plugins.add('converse-muc', {
'name': '',
'num_unread': 0,
'roomconfig': {},
'time_sent': (new Date(0)).toISOString(),
'time_opened': this.get('time_opened') || (new Date()).getTime(),
'time_sent': (new Date(0)).toISOString(),
'type': _converse.CHATROOMS_TYPE
}
},
......@@ -597,6 +596,13 @@ converse.plugins.add('converse-muc', {
return this;
},
invitesAllowed () {
return _converse.allow_muc_invitations &&
(this.features.get('open') ||
this.getOwnAffiliation() === "owner"
);
},
getDisplayName () {
const name = this.get('name');
if (name) {
......@@ -1510,7 +1516,10 @@ converse.plugins.add('converse-muc', {
// The subject is changed by sending a message of type "groupchat" to the <room@service>,
// where the <message/> MUST contain a <subject/> element that specifies the new subject but
// MUST NOT contain a <body/> element (or a <thread/> element).
u.safeSave(this, {'subject': {'author': attrs.nick, 'text': attrs.subject || ''}});
u.safeSave(this, {
'subject': {'author': attrs.nick, 'text': attrs.subject || ''},
'hide_subject': attrs.subject ? false : this.get('hide_subject')
});
return true;
}
return false;
......
......@@ -131,8 +131,7 @@ u.shouldCreateMessage = function (attrs) {
u.shouldCreateGroupchatMessage = function (attrs) {
return attrs.nick && (u.shouldCreateMessage(attrs) || attrs.is_tombstone);
},
}
u.isEmptyMessage = function (attrs) {
if (attrs instanceof Model) {
......
<div class="flyout box-flyout">
<div class="chat-head chat-head-chatbox row no-gutters"></div>
<div class="chat-body">
<div class="chat-content {[ if (o.show_send_button) { ]}chat-content-sendbutton{[ } ]}" aria-live="polite"></div>
<div class="bottom-panel">
......
import { html } from "lit-html";
import { until } from 'lit-html/directives/until.js';
import { __ } from '@converse/headless/i18n';
import { until } from 'lit-html/directives/until.js';
import avatar from "./avatar.js";
import converse from "@converse/headless/converse-core";
import xss from "xss/dist/xss";
const i18n_profile = __('The User\'s Profile Image');
......@@ -14,20 +12,24 @@ const avatar_data = {
'width': 40,
}
const tpl_standalone_btns = (o) => o.standalone_btns.reverse().map(b => until(b, ''));
export default (o) => {
return html`
<div class="chat-head chat-head-chatbox row no-gutters">
<div class="chatbox-title">
<div class="chatbox-title--row">
${ (!o._converse.singleton) ? html`<div class="chatbox-navback"><i class="fa fa-arrow-left"></i></div>` : '' }
${ (o.type !== o._converse.HEADLINES_TYPE) ? avatar(Object.assign({}, o, avatar_data)) : '' }
<div class="chatbox-title__text" title="${o.jid}">
${ o.url ? html`<a href="${o.url}" target="_blank" rel="noopener" class="user">${o.display_name}</a>` : o.display_name}
</div>
<div class="chatbox-title ${ o.status ? '' : "chatbox-title--no-desc"}">
<div class="chatbox-title--row">
${ (!o._converse.singleton) ? html`<div class="chatbox-navback"><i class="fa fa-arrow-left"></i></div>` : '' }
${ (o.type !== o._converse.HEADLINES_TYPE) ? avatar(Object.assign({}, o, avatar_data)) : '' }
<div class="chatbox-title__text" title="${o.jid}">
${ o.url ? html`<a href="${o.url}" target="_blank" rel="noopener" class="user">${o.display_name}</a>` : o.display_name}
</div>
<div class="chatbox-title__buttons row no-gutters">${ o.buttons.map(b => until(b, '')) }</div>
</div>
<p class="chat-head__desc">${ o.status }</p>
<div class="chatbox-title__buttons row no-gutters">
${ o.dropdown_btns.length ? html`<converse-dropdown .items=${o.dropdown_btns}></converse-dropdown>` : '' }
${ o.standalone_btns.length ? tpl_standalone_btns(o) : '' }
</div>
</div>
${ o.status ? html`<p class="chat-head__desc">${ o.status }</p>` : '' }
`;
}
import '../components/dropdown.js';
import { __ } from '@converse/headless/i18n';
import { html } from "lit-html";
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
import { until } from 'lit-html/directives/until.js';
......@@ -5,18 +7,26 @@ import converse from "@converse/headless/converse-core";
import xss from "xss/dist/xss";
const u = converse.env.utils;
const i18n_hide_topic = __('Hide the groupchat topic');
const tpl_standalone_btns = (o) => o.standalone_btns.reverse().map(b => until(b, ''));
export default (o) => {
const subject = o.subject ? u.addHyperlinks(xss.filterXSS(o.subject.text, {'whiteList': {}})) : '';
const show_subject = (subject && !o.hide_subject);
return html`
<div class="chatbox-title">
${ (!o._converse.singleton) ? html`<div class="chatbox-navback"><i class="fa fa-arrow-left"></i></div>` : '' }
<div class="chatbox-title__text" title="${ (o._converse.locked_muc_domain !== 'hidden') ? o.jid : '' }">${ o.title }</div>
<div class="chatbox-title ${ show_subject ? '' : "chatbox-title--no-desc"}">
${ (o._converse.standalone) ? html`<div class="chatbox-navback"><i class="fa fa-arrow-left"></i></div>` : '' }
<div class="chatbox-title__text" title="${ (o._converse.locked_muc_domain !== 'hidden') ? o.jid : '' }">${ o.title }
${ (o.bookmarked) ? html`<i class="fa fa-bookmark"></i>` : '' }
</div>
<div class="chatbox-title__buttons row no-gutters">
${ o.buttons.map(b => until(b, '')) }
${ o.standalone_btns.length ? tpl_standalone_btns(o) : '' }
${ o.dropdown_btns.length ? html`<converse-dropdown .items=${o.dropdown_btns}></converse-dropdown>` : '' }
</div>
</div>
<p class="chat-head__desc">${unsafeHTML(subject)}</p>
${ show_subject ? html`<p class="chat-head__desc" title="${i18n_hide_topic}">${unsafeHTML(subject)}</p>` : '' }
`;
}
......@@ -13,30 +13,10 @@ const PRETTY_CHAT_STATUS = {
'online': 'Online'
};
const occupant_hint = (occupant) => __('Click to mention %1$s in your message.', occupant.get('nick'))
const i18n_invite = (o) => o._converse.view_mode === 'overlayed' ? __('Invite') : __('Invite someone');
const i18n_invite_hint = __('Invite someone to join this groupchat');
const i18n_occupant_hint = (occupant) => __('Click to mention %1$s in your message.', occupant.get('nick'))
const i18n_participants = __('Participants');
const invite_widget = (o) => {
if (o.invitesAllowed()) {
return html`
<a class="open-invite-modal"
title="${i18n_invite_hint}"
data-toggle="modal"
data-target="#muc-invite-modal"
@click=${o.showInviteModal}>
<i class="btn btn-primary btn-circle fa fa-user-plus"></i>
${i18n_invite(o)}
</a>`;
} else {
return '';
}
}
export default (o) => html`
<div class="occupants-header">
<i class="hide-occupants fa fa-times"></i>
......@@ -51,10 +31,9 @@ export default (o) => html`
Object.assign({
'jid': '',
'hint_show': PRETTY_CHAT_STATUS[occupant.get('show')],
'hint_occupant': occupant_hint(occupant)
'hint_occupant': i18n_occupant_hint(occupant)
}, occupant.toJSON())
);
}) }
</ul>
${ invite_widget(o) }
`;
......@@ -60,7 +60,13 @@ const fingerprints = (o) => {
`;
}
const remove_button = html`<button type="button" class="btn btn-danger remove-contact"><i class="far fa-trash-alt"> </i>${i18n_remove_contact}</button>`;
const remove_button = (o) => {
return html`
<button type="button" @click="${o.removeContact}" class="btn btn-danger remove-contact">
<i class="far fa-trash-alt"></i>${i18n_remove_contact}
</button>
`;
}
export default (o) => html`
......@@ -84,7 +90,7 @@ export default (o) => html`
<div class="modal-footer">
${modal_close_button}
<button type="button" class="btn btn-info refresh-contact"><i class="fa fa-refresh"> </i>${i18n_refresh}</button>
${ (o.allow_contact_removal && o.is_roster_contact) ? remove_button : '' }
${ (o.allow_contact_removal && o.is_roster_contact) ? remove_button(o) : '' }
</div>
</div>
......
......@@ -275,6 +275,11 @@ u.hasClass = function (className, el) {
return (el instanceof Element) && el.classList.contains(className);
};
u.toggleClass = function (className, el) {
u.hasClass(className, el) ? u.removeClass(className, el) : u.addClass(className, el);
}
/**
* Add a class to an element.
* @method u#addClass
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment