From b1c9af3ed0015e9fea6cc60b6c2c6a796a154332 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Wed, 4 Dec 2019 16:01:34 +0100 Subject: [PATCH] Various improvements to resizing of occupants view. - Remove need for the `converse-mouse-events` plugin. - Register `mousemove` and `mouseup` handlers only when necessary and only inside the MUC DOM element. - Restore converse-dragresize to roughly it's original state before work started on this. - Move `applyDragResistance` to utils. updates #1640 --- CHANGES.md | 16 +-- sass/_chatrooms.scss | 2 +- sass/_variables.scss | 3 - src/converse-dragresize.js | 81 +++++++++++--- src/converse-mouse-events.js | 91 --------------- src/converse-muc-views.js | 168 +++++++++++++--------------- src/converse.js | 2 - src/headless/converse-core.js | 1 - src/templates/chatroom_sidebar.html | 1 + src/utils/html.js | 29 ++++- 10 files changed, 181 insertions(+), 213 deletions(-) delete mode 100644 src/converse-mouse-events.js diff --git a/CHANGES.md b/CHANGES.md index 5ac93a650..f948ab03a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,18 +13,18 @@ - New config option [muc_mention_autocomplete_show_avatar](https://conversejs.org/docs/html/configuration.html#muc-mention-autocomplete-show_avatar) - New config option [persistent_store](https://conversejs.org/docs/html/configuration.html#persistent-store) -- 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 -- #1666: Allow scrolling of the OMEMO fingerprints list -- #1691: Fix `collection.chatbox is undefined` errors -- #1772 `_converse.api.contact.add(jid, nick)` fails, says not a function -- #1792: Fix: modals don't have scrollbars -- #1796: Don't show "back" arrow navigation (on mobile) in the chat header when in `singleton` mode - Initial support for sending custom emojis. Currently only between Converse instances. Still working out a wire protocol for compatibility with other clients. To add custom emojis, edit the `emojis.json` file. +- #129: Add support for [XEP-0156: Disovering Alternative XMPP Connection Methods](https://xmpp.org/extensions/xep-0156.html). Only XML is supported for now. +- #1105: Support for storing persistent data in IndexedDB +- #1640: Add the ability to resize the occupants sidebar in MUCs +- #1666: Allow scrolling of the OMEMO fingerprints list +- #1691: Fix `collection.chatbox is undefined` errors +- #1772: `_converse.api.contact.add(jid, nick)` fails, says not a function +- #1792: Fix: modals don't have scrollbars +- #1796: Don't show "back" arrow navigation (on mobile) in the chat header when in `singleton` mode ### Breaking changes diff --git a/sass/_chatrooms.scss b/sass/_chatrooms.scss index ed4676d2c..3a7076a60 100644 --- a/sass/_chatrooms.scss +++ b/sass/_chatrooms.scss @@ -146,7 +146,7 @@ display: flex; flex-direction: column; word-wrap: break-word; - flex: 0 1 100%; + flex: 0 1 100%; min-width: 25%; .new-msgs-indicator { background-color: var(--chatroom-head-color); diff --git a/sass/_variables.scss b/sass/_variables.scss index ba0236995..fec17a9d2 100644 --- a/sass/_variables.scss +++ b/sass/_variables.scss @@ -150,7 +150,6 @@ $mobile_portrait_length: 480px !default; --occupants-padding: 1em; --occupants-background-color: white; - --occupants-max-width: inherit; --occupants-border-left: 1px solid var(--text-color); --occupants-border-bottom: 1px solid lightgrey; --occupants-features-display: block; @@ -222,8 +221,6 @@ $mobile_portrait_length: 480px !default; --chatroom-badge-hover-color: #D24E2B; // $red --occupants-background-color: #F3F3F3; - /* TODO: find a way to allow that and reflow the chat-area properly. - * --occupants-max-width: 240px; */ --occupants-border-left: 0px; --occupants-border-bottom: 0px; --occupants-features-display: none; diff --git a/src/converse-dragresize.js b/src/converse-dragresize.js index 4d7d12157..5187c7ac9 100644 --- a/src/converse-dragresize.js +++ b/src/converse-dragresize.js @@ -1,7 +1,7 @@ // Converse.js (A browser based XMPP chat client) // https://conversejs.org // -// Copyright (c) 2012-2017, Jan-Carel Brand +// Copyright (c) 2012-2019, Jan-Carel Brand // Licensed under the Mozilla Public License (MPLv2) // /** @@ -9,10 +9,11 @@ */ import "converse-chatview"; import "converse-controlbox"; +import { debounce, get } from "lodash"; import converse from "@converse/headless/converse-core"; import tpl_dragresize from "templates/dragresize.html"; -const { _ } = converse.env; +const u = converse.env.utils; function renderDragResizeHandles (_converse, view) { const flyout = view.el.querySelector('.box-flyout'); @@ -36,7 +37,7 @@ converse.plugins.add('converse-dragresize', { * * NB: These plugins need to have already been loaded via require.js. */ - dependencies: ["converse-chatview", "converse-headlines-view", "converse-muc-views", "converse-mouse-events"], + dependencies: ["converse-chatview", "converse-headlines-view", "converse-muc-views"], enabled (_converse) { return _converse.view_mode == 'overlayed'; @@ -48,16 +49,12 @@ converse.plugins.add('converse-dragresize', { // relevant objects or classes. ChatBox: { initialize () { - const { _converse } = this.__super__; const result = this.__super__.initialize.apply(this, arguments); const height = this.get('height'), width = this.get('width'); - const save = this.get('id') === 'controlbox' ? - (attrs) => this.set(attrs) : - (attrs) => this.save(attrs); - + const save = this.get('id') === 'controlbox' ? a => this.set(a) : a => this.save(a); save({ - 'height': _converse.applyDragResistance(height, this.get('default_height')), - 'width': _converse.applyDragResistance(width, this.get('default_width')), + 'height': u.applyDragResistance(height, this.get('default_height')), + 'width': u.applyDragResistance(width, this.get('default_width')), }); return result; } @@ -146,11 +143,12 @@ converse.plugins.add('converse-dragresize', { 'allow_dragresize': true, }); + const dragResizable = { initDragResize () { const view = this; - const debouncedSetDimensions = _.debounce(() => view.setDimensions()); + const debouncedSetDimensions = debounce(() => view.setDimensions()); window.addEventListener('resize', view.debouncedSetDimensions) this.listenTo(this.model, 'destroy', () => window.removeEventListener('resize', debouncedSetDimensions)); @@ -174,14 +172,14 @@ converse.plugins.add('converse-dragresize', { // Initialize last known mouse position this.prev_pageY = 0; this.prev_pageX = 0; - if (_.get(_converse.connection, 'connected')) { + if (get(_converse.connection, 'connected')) { this.height = this.model.get('height'); this.width = this.model.get('width'); } return this; }, - resize (ev) { + resizeChatBox (ev) { let diff; if (_converse.resizing.direction.indexOf('top') === 0) { diff = ev.pageY - this.prev_pageY; @@ -191,7 +189,7 @@ converse.plugins.add('converse-dragresize', { this.setChatBoxHeight(this.height); } } - if (_.includes(_converse.resizing.direction, 'left')) { + if (_converse.resizing.direction.includes('left')) { diff = this.prev_pageX - ev.pageX; if (diff) { this.width = ((this.width+diff) > (this.model.get('min_width') || 0)) ? (this.width+diff) : this.model.get('min_width'); @@ -218,7 +216,7 @@ converse.plugins.add('converse-dragresize', { setChatBoxHeight (height) { if (height) { - height = _converse.applyDragResistance(height, this.model.get('default_height'))+'px'; + height = u.applyDragResistance(height, this.model.get('default_height'))+'px'; } else { height = ""; } @@ -230,7 +228,7 @@ converse.plugins.add('converse-dragresize', { setChatBoxWidth (width) { if (width) { - width = _converse.applyDragResistance(width, this.model.get('default_width'))+'px'; + width = u.applyDragResistance(width, this.model.get('default_width'))+'px'; } else { width = ""; } @@ -313,6 +311,57 @@ converse.plugins.add('converse-dragresize', { }; Object.assign(_converse.ChatBoxView.prototype, dragResizable); + + u.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 = u.applyDragResistance( + _converse.resizing.chatbox.height, + _converse.resizing.chatbox.model.get('default_height') + ); + const width = u.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 deleted file mode 100644 index b513bea5f..000000000 --- a/src/converse-mouse-events.js +++ /dev/null @@ -1,91 +0,0 @@ -// 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 1886ae68c..2e55a8f38 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -648,6 +648,7 @@ converse.plugins.add('converse-muc-views', { 'input .chat-textarea': 'inputChanged', 'keydown .chat-textarea': 'onKeyDown', 'keyup .chat-textarea': 'onKeyUp', + 'mousedown .dragresize-occupants-left': 'onStartResizeOccupants', 'paste .chat-textarea': 'onPaste', 'submit .muc-nickname-form': 'submitNickname' }, @@ -678,6 +679,10 @@ converse.plugins.add('converse-muc-views', { this.listenTo(this.model.occupants, 'change:role', this.onOccupantRoleChanged); this.listenTo(this.model.occupants, 'change:affiliation', this.onOccupantAffiliationChanged); + // Bind so that we can pass it to addEventListener and removeEventListener + this.onMouseMove = this.onMouseMove.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + this.render(); this.createOccupantsView(); await this.updateAfterMessagesFetched(); @@ -746,9 +751,80 @@ converse.plugins.add('converse-muc-views', { createOccupantsView () { this.model.occupants.chatroomview = this; - const view = new _converse.ChatRoomOccupantsView({'model': this.model.occupants}); + this.occupants_view = new _converse.ChatRoomOccupantsView({'model': this.model.occupants}); const container_el = this.el.querySelector('.chatroom-body'); - container_el.insertAdjacentElement('beforeend', view.el); + const occupants_width = this.model.get('occupants_width'); + if (this.occupants_view && occupants_width !== undefined) { + this.occupants_view.el.style.flex = "0 0 " + occupants_width + "px"; + } + container_el.insertAdjacentElement('beforeend', this.occupants_view.el); + }, + + onStartResizeOccupants (ev) { + this.resizing = true; + this.el.addEventListener('mousemove', this.onMouseMove); + this.el.addEventListener('mouseup', this.onMouseUp); + + const style = window.getComputedStyle(this.occupants_view.el); + this.width = parseInt(style.width.replace(/px$/, ''), 10); + this.prev_pageX = ev.pageX; + }, + + onMouseMove (ev) { + if (this.resizing) { + ev.preventDefault(); + const delta = this.prev_pageX - ev.pageX; + this.resizeOccupantsView(delta, ev.pageX); + this.prev_pageX = ev.pageX; + } + }, + + onMouseUp (ev) { + if (this.resizing) { + ev.preventDefault(); + this.resizing = false; + this.el.removeEventListener('mousemove', this.onMouseMove); + this.el.removeEventListener('mouseup', this.onMouseUp); + const element_position = this.occupants_view.el.getBoundingClientRect(); + const occupants_width = this.calculateOccupantsWidth(element_position, 0); + const attrs = {occupants_width}; + _converse.connection.connected ? this.model.save(attrs) : this.model.set(attrs); + } + }, + + resizeOccupantsView (delta, current_mouse_position) { + const element_position = this.occupants_view.el.getBoundingClientRect(); + if (this.is_minimum) { + this.is_minimum = element_position.left < current_mouse_position; + } else if (this.is_maximum) { + this.is_maximum = element_position.left > current_mouse_position; + } else { + const occupants_width = this.calculateOccupantsWidth(element_position, delta); + this.occupants_view.el.style.flex = "0 0 " + occupants_width + "px"; + } + }, + + calculateOccupantsWidth(element_position, delta) { + let occupants_width = element_position.width + delta; + const room_width = this.el.clientWidth; + // keeping display in boundaries + if (occupants_width < (room_width * 0.20)) { + // set pixel to 20% width + occupants_width = (room_width * 0.20); + this.is_minimum = true; + } else if (occupants_width > (room_width * 0.75)) { + // set pixel to 75% width + occupants_width = (room_width * 0.75); + this.is_maximum = true; + } else if ((room_width - occupants_width) < 250) { + // resize occupants if chat-area becomes smaller than 250px (min-width property set in css) + occupants_width = room_width - 250; + this.is_maximum = true; + } else { + this.is_maximum = false; + this.is_minimum = false; + } + return occupants_width; }, getAutoCompleteList () { @@ -2052,9 +2128,6 @@ converse.plugins.add('converse-muc-views', { listItems: 'model', sortEvent: 'change:role', listSelector: '.occupant-list', - events: { - 'mousedown .dragresize-occupants-left': 'onStartHorizontalResizeOccupants', - }, ItemView: _converse.ChatRoomOccupantView, @@ -2084,12 +2157,6 @@ 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(); }, @@ -2218,76 +2285,6 @@ converse.plugins.add('converse-muc-views', { }); }, - 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; - }, }); @@ -2369,15 +2366,6 @@ 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 e82509d5e..4adbbc10e 100644 --- a/src/converse.js +++ b/src/converse.js @@ -20,7 +20,6 @@ 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,7 +50,6 @@ 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 c51929d2f..1b353e040 100644 --- a/src/headless/converse-core.js +++ b/src/headless/converse-core.js @@ -243,7 +243,6 @@ _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 diff --git a/src/templates/chatroom_sidebar.html b/src/templates/chatroom_sidebar.html index 06acbf930..0e34901e9 100644 --- a/src/templates/chatroom_sidebar.html +++ b/src/templates/chatroom_sidebar.html @@ -3,6 +3,7 @@

{{{o.label_occupants}}}

+
    diff --git a/src/utils/html.js b/src/utils/html.js index 3176177df..5cb3cdba6 100644 --- a/src/utils/html.js +++ b/src/utils/html.js @@ -160,9 +160,36 @@ u.renderImageURL = function (_converse, url) { }; +/** + * Applies some resistance to `value` around the `default_value`. + * If value is close enough to `default_value`, then it is returned, otherwise + * `value` is returned. + * @method u#applyDragResistance + * @param { Integer } value + * @param { Integer } default_value + * @returns { Integer } + */ +u.applyDragResistance = function (value, default_value) { + 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; +}; + + /** * Returns a Promise which resolves once all images have been loaded. - * @returns {Promise} + * @method u#renderImageURLs + * @param { _converse } + * @param { HTMLElement } + * @returns { Promise } */ u.renderImageURLs = function (_converse, el) { if (!_converse.show_images_inline) {