diff --git a/src/plugins/sib-mention-autocomplete.js b/src/plugins/sib-mention-autocomplete.js new file mode 100644 index 0000000000000000000000000000000000000000..52d46a3d303bfbf54cf5ef15836f30bdca90edf7 --- /dev/null +++ b/src/plugins/sib-mention-autocomplete.js @@ -0,0 +1,116 @@ +/** + * Custom mention autocomplete list. + */ +converse.plugins.add('sib-mention-autocomplete', { + overrides: { + ChatRoom: { + /** + * Move the `all` mention at the bottom. + * @returns {*} + */ + getAllKnownNicknames() { + const nicknames = this.__super__.getAllKnownNicknames.apply(this, arguments); + + if (nicknames.indexOf('all') >= 0) { + return nicknames.filter(n => n !== 'all').concat(['all']); + } + + return nicknames; + }, + }, + ChatRoomView: { + /** + * Disable sort so the `all` mention stays at the bottom. + */ + initMentionAutoComplete() { + this.__super__.initMentionAutoComplete.apply(this, arguments); + this.mention_auto_complete.sort = false; + }, + + /** + * Show a custom icon for `all` + * Render the full name on top of the nickname + * + * @param text + * @param input + * @returns {HTMLLIElement} + */ + getAutoCompleteListItem(text, input) { + const _converse = this.__super__._converse; + const { api } = _converse; + + input = input.trim(); + const element = document.createElement('li'); + element.setAttribute('aria-selected', 'false'); + + const is_mention_all = text.toString() === 'all'; + let vcard = null; + + // Get the vcard for the current user + if (_converse.vcards && !is_mention_all) { + vcard = _converse.vcards.findWhere({ + 'nickname': text, + }); + } + + // Update the `@all` mention with a custom label + // Update the label with the user full name if available + // TODO: Add translation support + if (is_mention_all) { + text.title = 'Notifier tous les utilisateurs du cercle'; + } else if (vcard && vcard.get('fullname')) { + text.title = vcard.get('fullname') || text.toString(); + } else { + text.title = text.toString(); + } + + // Update mention item with the avatar + if (api.settings.get('muc_mention_autocomplete_show_avatar')) { + + const ALL_IMAGE_TYPE = 'image/png'; + const ALL_IMAGE = 'iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV/Tih9UFOwg4pChdrJQVMRRq1CECqFWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIm5uToouU+L+k0CLWg+N+vLv3uHsHCPUy06xADNB020wl4mImuyp2vyKAQfQihojMLGNOkpLoOL7u4ePrXZRndT735+hXcxYDfCLxLDNMm3iDeHrTNjjvE4dYUVaJz4nHTbog8SPXFY/fOBdcFnhmyEyn5olDxGKhjZU2ZkVTI54iDquaTvlCxmOV8xZnrVxlzXvyFwZz+soy12mOIoFFLEGCCAVVlFCGjSitOikWUrQf7+Afcf0SuRRylcDIsYAKNMiuH/wPfndr5ScnvKRgHOh6cZyPMaB7F2jUHOf72HEaJ4D/GbjSW/5KHZj5JL3W0sJHwMA2cHHd0pQ94HIHGH4yZFN2JT9NIZ8H3s/om7LA0C3Qt+b11tzH6QOQpq6SN8DBIRApUPZ6h3f3tPf275lmfz+L0XKxj98XuQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+UEEAkzFM6kWwwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAACSUlEQVRYw+3Xz0sUYRjA8e/Mjs6s6+iuq64/0mzTJLQIy0DqokQRUVfp4jE6B9VBCLtF/0MEdapDknjwYJeIsLykUbaU+WNd21zW/TG6zu7M7HQQBCkQ0132MM/x4eXhw/s+7/PyCoTHbUooREosHJADckAO6EDFDAvsg81Z6TAgykqSI0+mkFdT2C4R7UwzvwZ7MKuVfdcSDvx02DbeqSVMr8LGyQYEK0/txBy+dz/5MXKVvCwVZ4dcmzn8kyHcywkMv4d4f8e2zyUSu9aFEk6izqySOt9a+B5SIilODI8jGnni/R1kG1SOPX6D9+PSzhrD60bS9OL0UNOzaSJDvaR7WgDYoJH06WbaH06QOtuC7RIRbLAFoTi3TFlJonU37coZtR5MVaZsPQNAeUwjV19ZGnNISut4vq2hH60pDshyl1H2j/6QNnNYlTJNz6dJ9bVhqnJxQFtBPxXfY7sxmg7CNta9uI4lS4i6URxQfKCDwOgsrkxuJ9fw8hPJvjYA5h9cQdRNOu+N4Z8MIVj5woIy7XUYXje+t/MAyFGN6qlF4pc6ATBVhdWhXuaHL6N+idL89EOBQLaN9/0CnffHMGo8JC4GAcgGVGI3ujk+MkH968+IORNscC8nyAT9lK9tHP7TIW4ZBB9NYlWUE73Zw1ar7++mTukEXs1QNRth4c4AntBvlHCS9LlWtFONhwuqnItSN/6VhbsDe64NjM5iSyJr17sLN6mzgSqUSJKuWy/2PllJJHz7wn/NMMH5KDogB+SAHJAD2l/8AVdEzx8qjlPAAAAAAElFTkSuQmCC'; + + const img = document.createElement('img'); + let dataUri = 'data:' + _converse.DEFAULT_IMAGE_TYPE + ';base64,' + _converse.DEFAULT_IMAGE; + + if (vcard) { + dataUri = 'data:' + vcard.get('image_type') + ';base64,' + vcard.get('image'); + } + + if (is_mention_all) { + dataUri = 'data:' + ALL_IMAGE_TYPE + ';base64,' + ALL_IMAGE; + } + + img.setAttribute('src', dataUri); + img.setAttribute('width', '32'); + img.setAttribute('class', 'avatar avatar-autocomplete'); + element.appendChild(img); + } + + const label = document.createElement('span'); + label.appendChild(document.createTextNode(text.title)); + + const val = document.createElement('small'); + val.appendChild(document.createTextNode('@')); + + const regex = new RegExp('(' + input + ')', 'ig'); + const parts = input ? text.split(regex) : [text]; + + parts.forEach(txt => { + if (input && txt.match(regex)) { + const match = document.createElement('mark'); + match.textContent = txt; + val.appendChild(match); + } else { + val.appendChild(document.createTextNode(txt)); + } + }); + + label.appendChild(val); + element.appendChild(label); + + return element; + }, + }, + }, +}); diff --git a/src/solid-xmpp-chat.js b/src/solid-xmpp-chat.js index 9cc0f43c31f6f065ffccc1f2e803704e1ea1b986..6c339da33363fbceabadb5e9ca3ffb185335902a 100644 --- a/src/solid-xmpp-chat.js +++ b/src/solid-xmpp-chat.js @@ -12,6 +12,7 @@ import './plugins/sib-custom-hats.js'; import './plugins/sib-disconnected.js'; import './plugins/sib-emoji-picker.js'; import './plugins/sib-history-improved.js'; +import './plugins/sib-mention-autocomplete.js'; import './plugins/sib-mention-mobile.js'; import './plugins/sib-reactions.js'; import './plugins/sib-remove-notifications.js'; @@ -239,6 +240,7 @@ export const SolidXMPPChat = { 'sib-disconnected', 'sib-emoji-picker', 'sib-history-improved', + 'sib-mention-autocomplete', 'sib-mention-mobile', 'sib-reactions', 'sib-remove-notifications', diff --git a/src/styles/index.scss b/src/styles/index.scss index 2d497d714ad048b3428f32f406a7b132436d941e..8161c3f777a1cc3acb4d02f0e7af2f2d58e3d748 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -589,21 +589,45 @@ .suggestion-box .suggestion-box__results, .suggestion-box > ul { - box-shadow: 0 0 5px 0 rgba(133, 140, 148, 0.09); + box-shadow: 0 0 5px 0 rgba(133, 140, 148, 0.25); } &.converse-fullscreen .chatroom .sendXMPPMessage .suggestion-box__results--above, .chatroom .sendXMPPMessage .suggestion-box__results--above { border: none; + position: absolute; + top: -3px; + transform: translateY(-100%); + bottom: unset; li { + display: flex; + align-items: center; color: var(--color-chat-suggestion); font-size: 13px; font-weight: 600; border-bottom: 1px solid #E4E8ED; + + small:before { + content: "\a"; + white-space: pre; + } + } + + @media (min-width: 768px) { + max-width: 30rem; + min-width: unset; } } + .suggestion-box { + position: unset; + } + + .sendXMPPMessage { + position: relative; + } + &.converse-fullscreen .chatroom .sendXMPPMessage .chat-toolbar, .chatroom .sendXMPPMessage .chat-toolbar { border-top: 1px solid var(--chat-head-color); @@ -846,23 +870,15 @@ } .suggestion-box mark { - background: var(--hd-hightlight-color); + background: transparent !important; + font-weight: bold; } .suggestion-box > ul > li[aria-selected=true] { - background: transparent; + background: #f6f6f6 !important; color: var(--chat-suggestion-selected-true) !important; } - .suggestion-box li:hover mark { - background: var(--hd-hightlight-color); - } - - .suggestion-box li[aria-selected=true] mark { - background: inherit; - font-weight: bold; - } - .modal-backdrop { z-index: 1030; }