diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d94389ae83fcb6f577e91f09e5b4c9c09f9cf4d..c0fed8c2bda41f087ed55b6505a877b2dd7fc17f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This file contains the changes applied to the sources of converse<br> The current build version of converse is: `v7.0.3dev`<br> If the converse sources have to be updated, apply these changes again, or fix them using plugins +### 2021-05-19 +* Avoid clearing the textarea when pressing the ESC key and not editing any messages + ### 2021-05-18 * Move upload to fetch method from solid auth (commit 5345f05d9a4c71720e2c87a0a19c56a7b4865329) diff --git a/src/conversejs/converse.js b/src/conversejs/converse.js index 8cdce6057ac643fe3e9f8c5d0186b2aeb1d5e3e6..e86a471bf1938ebfae7fbd0c0e79f54dade00400 100644 --- a/src/conversejs/converse.js +++ b/src/conversejs/converse.js @@ -98225,9 +98225,8 @@ const ChatBoxView = View.extend({ if (message) { message.save('correcting', false); + this.insertIntoTextArea('', true, false); } - - this.insertIntoTextArea('', true, false); }, async onMessageRetractButtonClicked(message) { diff --git a/src/plugins/sib-emoji-picker.js b/src/plugins/sib-emoji-picker.js index f4448c0fd3b1604a02e33fa451f361e022c3563c..aa462a81a7f9bcc4af19bff239f96287e65efed7 100644 --- a/src/plugins/sib-emoji-picker.js +++ b/src/plugins/sib-emoji-picker.js @@ -1,4 +1,5 @@ import { Picker } from 'https://cdn.skypack.dev/emoji-picker-element'; +import { isTouchDevice } from '../utils.js'; /** * Custom emoji picker. @@ -54,7 +55,15 @@ converse.plugins.add('sib-emoji-picker', { picker = new Picker(); picker.classList.add('light'); - picker.addEventListener('emoji-click', ev => this.insertIntoTextArea(ev.detail.unicode)); + + picker.addEventListener('emoji-click', ev => { + if (isTouchDevice()) { + // Close the dropdown once we choose one on mobile devices + this.hideMenu(); + } + this.insertIntoTextArea(ev.detail.unicode); + }); + this.menu.appendChild(picker); await _showMenu.apply(this, arguments); diff --git a/src/plugins/sib-reactions.js b/src/plugins/sib-reactions.js index 19a8aeb3f263566263820a8065ae9089641fef47..61d54da65c249b7dc0cf1f206782e8dbfb903a23 100644 --- a/src/plugins/sib-reactions.js +++ b/src/plugins/sib-reactions.js @@ -273,34 +273,52 @@ converse.plugins.add('sib-reactions', { picker.style.position = 'fixed'; picker.style.zIndex = '99999'; - picker.style.margin = '10px'; - picker.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.2)'; - picker.style.borderRadius = '3px'; let pr = picker.getBoundingClientRect(); - // Stick on the left side of the target - // Stick on the right side if there is not enough space for it - // Center if there are no space anywhere - if (window.innerWidth - (rectLeft + tr.width) > pr.width) { - picker.style.left = `${rectLeft + tr.width}px`; - } else if (rectLeft > pr.width) { - picker.style.right = `${window.innerWidth - rectLeft}px`; - } else { - picker.style.left = '50%'; - picker.style.transform = 'translateX(-50%)'; - picker.style.margin = '0px'; - } + // Check if the viewport is mobile friendly + // We have to do this here since the styles is scoped to the SXC component shadow DOM + const mql = window.matchMedia('(max-width: 767.98px)'); - // Add an offset so it's a bit higher than the target - const offY = 100; + if (mql.matches) { - // Show the drawer a bit higher than the target - // Stick at the bottom if there are no space available - if (pr.height + (rectTop - offY) > window.innerHeight) { - picker.style.bottom = `0px`; + // MOBILE + picker.style.left = '0'; + picker.style.bottom = '0'; + picker.style.width = '100%'; + picker.style.height = '60vh'; + picker.style.boxShadow = '0 0 0 100vh rgba(0, 0, 0, 0.3)'; + picker.style.setProperty('--emoji-size', '1.6rem'); } else { - picker.style.top = `${rectTop - offY}px`; + + // DESKTOP + // Stick on the left side of the target + // Stick on the right side if there is not enough space for it + // Center if there are no space anywhere + picker.style.margin = '10px'; + picker.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.2)'; + picker.style.borderRadius = '3px'; + + if (window.innerWidth - (rectLeft + tr.width) > pr.width) { + picker.style.left = `${rectLeft + tr.width}px`; + } else if (rectLeft > pr.width) { + picker.style.right = `${window.innerWidth - rectLeft}px`; + } else { + picker.style.left = '50%'; + picker.style.transform = 'translateX(-50%)'; + picker.style.margin = '0px'; + } + + // Add an offset so it's a bit higher than the target + const offY = 100; + + // Show the drawer a bit higher than the target + // Stick at the bottom if there are no space available + if (pr.height + (rectTop - offY) > window.innerHeight) { + picker.style.bottom = `0px`; + } else { + picker.style.top = `${rectTop - offY}px`; + } } // Focus the search input when the picker opens diff --git a/src/plugins/sib-scroll-down-on-focus.js b/src/plugins/sib-scroll-down-on-focus.js index 11652ab4283e4a5b4d9d1723b1ed2e33b7cda9b3..196c8f6a89810172f6e8e96ebaa56b6d7fc56291 100644 --- a/src/plugins/sib-scroll-down-on-focus.js +++ b/src/plugins/sib-scroll-down-on-focus.js @@ -1,17 +1,68 @@ /** - * Forces the active chat to scroll down when the tab is focused again. + * Forces the active chat to show the new messages indicator + * when the tab is focused again and we where previously scrolled down. + * TODO: Rename this plugin */ converse.plugins.add('sib-scroll-down-on-focus', { + overrides: { + ChatBoxView: { + /** + * Force the chat view to be set as active when visible. + * Like it's set on "inactive" when the tab is focused out. + * @param {string} state + */ + onWindowStateChanged(state) { + const { _converse } = this.__super__; + + if (state === 'visible' && !this.model.isHidden()) { + this.model.setChatState(_converse.ACTIVE, { + 'silent': true, + }); + } + + this.__super__.onWindowStateChanged.apply(this, arguments); + }, + + /** + * @inheritDoc + */ + onMessageAdded(message) { + const { _converse } = this.__super__; + + // On new message, set the chat view as scrolled + // so the new messages indicator is properly displayed + // when the view is inactive (tab not focused). + if (this.model.get('chat_state') === _converse.INACTIVE) { + this.model.set('scrolled', true); + } + + this.__super__.onMessageAdded.apply(this, arguments); + }, + }, + }, initialize() { const _converse = this._converse; const { api } = _converse; - api.listen.on('windowStateChanged', async data => { - if (data.state === 'visible' && api.connection.connected()) { - const chatBox = _converse.chatboxes.findWhere({ hidden: false }); - const chatView = _converse.chatboxviews.get(chatBox?.get('jid')); - chatView?.scrollDown(); + // Return the current root component + function getRootComponent() { + return api.settings.get('root')?.getRootNode()?.host?.component; + } + + // When the chat view has been scrolled to the bottom + // send the read event to remove SIB notifications in UI + api.listen.on('chatBoxScrolledDown', (data) => { + if (data.chatbox.isHidden()) { + return; } + + window.dispatchEvent(new CustomEvent('read', { + detail: { + resource: { + '@id': getRootComponent()?.resource['@id'], + }, + }, + })); }); }, }); diff --git a/src/plugins/sib-send-button-mobile.js b/src/plugins/sib-send-button-mobile.js index fc3ef85adcaf20a14687898d832aa04e6c09cff0..19ea6fcc98aa3474b5831f61762afbbbbbb7c6e1 100644 --- a/src/plugins/sib-send-button-mobile.js +++ b/src/plugins/sib-send-button-mobile.js @@ -1,3 +1,5 @@ +import { isTouchDevice } from '../utils.js'; + /** * Prevent the "Enter" button to send the message * on touch devices. @@ -9,9 +11,7 @@ converse.plugins.add('sib-send-button-mobile', { * @param {InputEvent} ev */ onEnterPressed(ev) { - const touchDevice = ('ontouchstart' in document.documentElement); - - if (touchDevice) { + if (isTouchDevice()) { // The line break is automatically inserted return; } diff --git a/src/styles/index.scss b/src/styles/index.scss index ade77204a88b6a2066854b602fdebca802cdff69..0c33520c252d422a1327946b0681987e277c33ed 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1002,4 +1002,21 @@ background: none; color: var(--color-secondary) !important; } + + // + // Emoji picker (mobile) + // --------------------------------- + + @media (max-width: 767.98px) { + .dropdown-menu emoji-picker { + z-index: 99999; + position: fixed !important; + bottom: 0 !important; + left: 0 !important; + width: 100%; + height: 60vh; + --emoji-size: 1.6rem; + box-shadow: 0 0 0 100vh rgba(0, 0, 0, 0.3); + } + } } diff --git a/src/utils.js b/src/utils.js index 01f067ac60462f6eef371eb4607c31f374113670..a66a5de9833ccc4d47c0c7b9a47510dbb9f90eb2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -8,3 +8,9 @@ export class Deferred { this.catch = this.promise.catch.bind(this.promise); } } + +/** + * Returns TRUE if the current device has touch features (mobile). + * @return {boolean} + */ +export const isTouchDevice = () => ('ontouchstart' in document.documentElement);