From b94e5653a9b87043bad1e716984b811f7f298f92 Mon Sep 17 00:00:00 2001 From: ndoell Date: Wed, 25 Sep 2019 08:15:36 +0200 Subject: [PATCH] Add resize functionality to occupants-list in MUC. This adds the ability to resize the MUC-Member-List in all MUCs. The MUC-Member-List can be scaled between 20% width of the MUC and 75% of the MUC. --- CHANGES.md | 1 + sass/_chatbox.scss | 3 +- sass/_chatrooms.scss | 6 ++- src/converse-dragresize.js | 57 +--------------------- src/converse-mouse-events.js | 91 +++++++++++++++++++++++++++++++++++ src/converse-muc-views.js | 91 ++++++++++++++++++++++++++++++++++- src/converse.js | 2 + src/headless/converse-core.js | 1 + 8 files changed, 194 insertions(+), 58 deletions(-) create mode 100644 src/converse-mouse-events.js diff --git a/CHANGES.md b/CHANGES.md index 9ea7adeaf..7c087bca0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## 6.0.0 (Unreleased) +- Add: Resizing of the occupants-list in Multi-User-Chats - #129: Add support for XEP-0156: Disovering Alternative XMPP Connection Methods. Only XML is supported for now. - #1105: Preliminary support for storing persistent data in IndexedDB instead of localStorage - #1089: When filtering the roster for `online` users, show all non-offline users. diff --git a/sass/_chatbox.scss b/sass/_chatbox.scss index b9b9ee612..338328a08 100644 --- a/sass/_chatbox.scss +++ b/sass/_chatbox.scss @@ -386,7 +386,8 @@ height: 5px; width: 100%; } - &-left { + &-left, + &-occupants-left { cursor: w-resize; width: 5px; height: 100%; diff --git a/sass/_chatrooms.scss b/sass/_chatrooms.scss index 15da799ba..d3dc7ee68 100644 --- a/sass/_chatrooms.scss +++ b/sass/_chatrooms.scss @@ -155,6 +155,8 @@ display: flex; flex-direction: column; word-wrap: break-word; + flex: 0 1 100%; + min-width: 25%; .new-msgs-indicator { background-color: var(--chatroom-head-color); } @@ -173,7 +175,9 @@ border-left: var(--occupants-border-left); border-bottom-right-radius: var(--chatbox-border-radius); padding: 0.5em; - max-width: var(--occupants-max-width); + max-width: 75%; + min-width: 20%; + flex: 0 0 25%; .occupants-header { display: flex; diff --git a/src/converse-dragresize.js b/src/converse-dragresize.js index 511d5e901..7d9d1966b 100644 --- a/src/converse-dragresize.js +++ b/src/converse-dragresize.js @@ -36,7 +36,7 @@ converse.plugins.add('converse-dragresize', { * * NB: These plugins need to have already been loaded via require.js. */ - dependencies: ["converse-chatview", "converse-headline", "converse-muc-views"], + dependencies: ["converse-chatview", "converse-headline", "converse-muc-views", "converse-mouse-events"], enabled (_converse) { return _converse.view_mode == 'overlayed'; @@ -146,7 +146,6 @@ converse.plugins.add('converse-dragresize', { 'allow_dragresize': true, }); - const dragResizable = { initDragResize () { @@ -182,7 +181,7 @@ converse.plugins.add('converse-dragresize', { return this; }, - resizeChatBox (ev) { + resize (ev) { let diff; if (_converse.resizing.direction.indexOf('top') === 0) { diff = ev.pageY - this.prev_pageY; @@ -314,59 +313,7 @@ converse.plugins.add('converse-dragresize', { }; Object.assign(_converse.ChatBoxView.prototype, dragResizable); - - _converse.applyDragResistance = function (value, default_value) { - /* This method applies some resistance around the - * default_value. If value is close enough to - * default_value, then default_value is returned instead. - */ - if (value === undefined) { - return undefined; - } else if (default_value === undefined) { - return value; - } - const resistance = 10; - if ((value !== default_value) && - (Math.abs(value- default_value) < resistance)) { - return default_value; - } - return value; - }; - - - /************************ BEGIN Event Handlers ************************/ - function registerGlobalEventHandlers () { - - document.addEventListener('mousemove', function (ev) { - if (!_converse.resizing || !_converse.allow_dragresize) { return true; } - ev.preventDefault(); - _converse.resizing.chatbox.resizeChatBox(ev); - }); - - document.addEventListener('mouseup', function (ev) { - if (!_converse.resizing || !_converse.allow_dragresize) { return true; } - ev.preventDefault(); - const height = _converse.applyDragResistance( - _converse.resizing.chatbox.height, - _converse.resizing.chatbox.model.get('default_height') - ); - const width = _converse.applyDragResistance( - _converse.resizing.chatbox.width, - _converse.resizing.chatbox.model.get('default_width') - ); - if (_converse.api.connection.connected()) { - _converse.resizing.chatbox.model.save({'height': height}); - _converse.resizing.chatbox.model.save({'width': width}); - } else { - _converse.resizing.chatbox.model.set({'height': height}); - _converse.resizing.chatbox.model.set({'width': width}); - } - _converse.resizing = null; - }); - } - _converse.api.listen.on('registeredGlobalEventHandlers', registerGlobalEventHandlers); _converse.api.listen.on('beforeShowingChatView', view => view.initDragResize().setDimensions()); /************************ END Event Handlers ************************/ } }); - diff --git a/src/converse-mouse-events.js b/src/converse-mouse-events.js new file mode 100644 index 000000000..b513bea5f --- /dev/null +++ b/src/converse-mouse-events.js @@ -0,0 +1,91 @@ +// Converse.js (A browser based XMPP chat client) +// https://conversejs.org +// +// Copyright (c) 2012-2017, Jan-Carel Brand +// Licensed under the Mozilla Public License (MPLv2) +// + +import "converse-chatview"; +import "converse-controlbox"; +import converse from "@converse/headless/converse-core"; + +const { _ } = converse.env; + + +converse.plugins.add('converse-mouse-events', { + + dependencies: ["converse-chatview", "converse-headline", "converse-muc-views"], + + enabled (_converse) { + return _converse.view_mode == 'overlayed' || + _converse.allow_occupants_view_resizing; + }, + + initialize () { + const { _converse } = this; + + _converse.applyDragResistance = function (value, default_value) { + if (_.isUndefined(value)) { + return undefined; + } else if (_.isUndefined(default_value)) { + return value; + } + const resistance = 10; + if ((value !== default_value) && + (Math.abs(value - default_value) < resistance)) { + return default_value; + } + return value; + }; + + function registerGlobalEventHandlers () { + document.addEventListener('mousemove', function (ev) { + if (!_converse.resizing || + (!_converse.allow_dragresize && !_converse.allow_occupants_view_resizing)) { + return true; + } + ev.preventDefault(); + _converse.resizing.chatbox.resize(ev); + }); + + document.addEventListener('mouseup', function (ev) { + if (!_converse.resizing || + (!_converse.allow_dragresize && !_converse.allow_occupants_view_resizing)) { + return true; + } + ev.preventDefault(); + const height = _converse.applyDragResistance( + _converse.resizing.chatbox.height, + _converse.resizing.chatbox.model.get('default_height') + ); + const width = _converse.applyDragResistance( + _converse.resizing.chatbox.width, + _converse.resizing.chatbox.model.get('default_width') + ); + if (_converse.resizing.width_occupants) { + if (_converse.connection.connected) { + _converse.resizing.chatbox.chatroomview.model.save({'width_occupants': _converse.resizing.width_occupants}); + } else { + _converse.resizing.chatbox.chatroomview.model.set({'width_occupants': _converse.resizing.width_occupants}); + } + } else { + if (_converse.api.connection.connected()) { + _converse.resizing.chatbox.model.save({'height': height}); + _converse.resizing.chatbox.model.save({'width': width}); + } else { + if (_converse.connection.connected) { + _converse.resizing.chatbox.model.save({'height': height}); + _converse.resizing.chatbox.model.save({'width': width}); + } else { + _converse.resizing.chatbox.model.set({'height': height}); + _converse.resizing.chatbox.model.set({'width': width}); + } + } + } + _converse.resizing = null; + }); + } + _converse.api.listen.on('registeredGlobalEventHandlers', registerGlobalEventHandlers); + } +}); + diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index 3e4efa634..de268aee0 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -1942,6 +1942,9 @@ converse.plugins.add('converse-muc-views', { listItems: 'model', sortEvent: 'change:role', listSelector: '.occupant-list', + events: { + 'mousedown .dragresize-occupants-left': 'onStartHorizontalResizeOccupants', + }, ItemView: _converse.ChatRoomOccupantView, @@ -1971,6 +1974,12 @@ converse.plugins.add('converse-muc-views', { _converse.api.waitUntil('rosterContactsFetched').then(() => this.renderInviteWidget()); } this.setVisibility(); + + this.renderDragResizeHandles(); + + this.is_maximum = false; + this.is_minimum = false; + return this.renderRoomFeatures(); }, @@ -2097,7 +2106,78 @@ converse.plugins.add('converse-muc-views', { this.invite_auto_complete.on('suggestion-box-open', () => { this.invite_auto_complete.ul.setAttribute('style', `max-height: calc(${this.el.offsetHeight}px - 80px);`); }); - } + }, + + renderDragResizeHandles () { + const flyout = this.el; + const div = document.createElement('div'); + div.innerHTML = '
'; + flyout.insertBefore( + div, + flyout.firstChild + ); + }, + + onStartHorizontalResizeOccupants (ev) { + const flyout = this.el, + style = window.getComputedStyle(flyout); + this.width = parseInt(style.width.replace(/px$/, ''), 10); + _converse.resizing = { + 'chatbox': this, + 'direction': 'left' + }; + this.prev_pageX = ev.pageX; + }, + + resize (ev) { + if (_.includes(_converse.resizing.direction, 'left')) { + this.change_in_pixel = (this.prev_pageX - ev.pageX); + this.resizeOccupantsView(this.change_in_pixel, ev.pageX); + this.prev_pageX = ev.pageX; + } + }, + + resizeOccupantsView (change_in_pixel, current_mouse_position) { + const element_position = this.el.getBoundingClientRect(); + + // has mouse reached the minimum to the right + if (this.is_minimum) { + this.is_minimum = element_position.left < current_mouse_position; + } // has mouse reached the maximum to the left + else if (this.is_maximum) { + this.is_maximum = element_position.left > current_mouse_position; + } // Scale is within the boundaries + else { + const occupants_width_pixel = this.calculateOccupantsWithInPixel(element_position, change_in_pixel); + this.el.style.flex = "0 0 " + occupants_width_pixel + "px"; + _converse.resizing.width_occupants = occupants_width_pixel; + } + }, + + calculateOccupantsWithInPixel (element_position, change_in_pixel) { + let occupants_width_pixel = element_position.width + change_in_pixel; + const chatroom_width_pixel = this.el.parentElement.clientWidth; + + // keeping display in boundaries + if (occupants_width_pixel < (chatroom_width_pixel * 0.20)) { + // set pixel to 20% width + occupants_width_pixel = (chatroom_width_pixel * 0.20); + this.is_minimum = true; + } else if (occupants_width_pixel > (chatroom_width_pixel * 0.75)) { + // set pixel to 75% width + occupants_width_pixel = (chatroom_width_pixel * 0.75); + this.is_maximum = true; + } else if ((chatroom_width_pixel - occupants_width_pixel) < 250) { + // resize occupants if chat-area becomes smaller than 250px (min-width property set in css) + occupants_width_pixel = chatroom_width_pixel - 250; + this.is_maximum = true; + } else { + this.is_maximum = false; + this.is_minimum = false; + } + + return occupants_width_pixel; + }, }); @@ -2178,6 +2258,15 @@ converse.plugins.add('converse-muc-views', { fetchAndSetMUCDomain(view); view.model.on('change:connected', () => fetchAndSetMUCDomain(view)); }); + + + _converse.api.listen.on('beforeShowingChatView', (chatroomview) => { + const occupants_width_pixel = chatroomview.model.get('width_occupants'); + const occupants_view = chatroomview.el.querySelector('.occupants'); + if (occupants_view && occupants_width_pixel !== undefined) { + occupants_view.style.flex = "0 0 " + occupants_width_pixel + "px"; + } + }); /************************ END Event Handlers ************************/ diff --git a/src/converse.js b/src/converse.js index ce856485d..c8eda6194 100644 --- a/src/converse.js +++ b/src/converse.js @@ -20,6 +20,7 @@ import "converse-fullscreen"; import "converse-mam-views"; import "converse-minimize"; // Allows chat boxes to be minimized import "converse-muc-views"; // Views related to MUC +import "converse-mouse-events"; import "converse-headlines-view"; import "converse-notification"; // HTML5 Notifications import "converse-omemo"; @@ -51,6 +52,7 @@ const WHITELISTED_PLUGINS = [ 'converse-minimize', 'converse-modal', 'converse-muc-views', + 'converse-mouse-events', 'converse-headlines-view', 'converse-notification', 'converse-omemo', diff --git a/src/headless/converse-core.js b/src/headless/converse-core.js index 5ef917325..3f279f7c6 100644 --- a/src/headless/converse-core.js +++ b/src/headless/converse-core.js @@ -234,6 +234,7 @@ _converse.default_connection_options = {'explicitResourceBinding': true}; // ---------------------------- _converse.default_settings = { allow_non_roster_messaging: false, + allow_occupants_view_resizing: false, authentication: 'login', // Available values are "login", "prebind", "anonymous" and "external". auto_away: 0, // Seconds after which user status is set to 'away' auto_login: false, // Currently only used in connection with anonymous login