From 68d461bd427a23cef3d1f897226a928e9c116e19 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Fri, 5 Mar 2021 15:00:01 +0100 Subject: [PATCH] Add the ability to show/hide unfurls --- README.md | 1 + sass/_core.scss | 2 +- spec/unfurls.js | 42 +++++++++++++++++++++++++ src/components/message-actions.js | 46 +++++++++++++++++++++++++--- src/shared/chat/message.js | 13 ++++++-- src/shared/chat/templates/message.js | 8 +++-- 6 files changed, 103 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b9d7a08ca..1757f62e1 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ In embedded mode, Converse can be embedded into an element in the DOM. - A [plugin architecture](https://conversejs.org/docs/html/plugin_development.html) based on [pluggable.js](https://conversejs.github.io/pluggable.js/) - Chat statuses (online, busy, away, offline) - Anonymous logins, see the [anonymous login demo](https://conversejs.org/demo/anonymous.html) +- URL Previews (requires server support, for example [mod_ogp](https://modules.prosody.im/mod_ogp.html) - Translated into over 30 languages ### Supported XMPP Extensions diff --git a/sass/_core.scss b/sass/_core.scss index d77e94d96..3a134ad29 100644 --- a/sass/_core.scss +++ b/sass/_core.scss @@ -2,7 +2,7 @@ opacity: 0; /* make things invisible upon start */ animation-name: fadein; animation-fill-mode: forwards; - animation-duration: 0.75s; + animation-duration: 0.5s; animation-timing-function: ease; } diff --git a/spec/unfurls.js b/spec/unfurls.js index 018e2d23a..cd88dbce5 100644 --- a/spec/unfurls.js +++ b/spec/unfurls.js @@ -261,4 +261,46 @@ describe("A Groupchat Message", function () { done(); })); + it("lets the user hide an unfurl", + mock.initConverse(['chatBoxesFetched'], + {'show_images_inline': []}, + async function (done, _converse) { + + const nick = 'romeo'; + const muc_jid = 'lounge@montague.lit'; + await mock.openAndEnterChatRoom(_converse, muc_jid, nick); + const view = _converse.api.chatviews.get(muc_jid); + + const message_stanza = u.toStanza(` + + https://www.youtube.com/watch?v=dQw4w9WgXcQ + + + + + `); + _converse.connection._dataRecv(mock.createRequest(message_stanza)); + const el = await u.waitUntil(() => view.querySelector('.chat-msg__text')); + expect(el.textContent).toBe('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); + + const metadata_stanza = u.toStanza(` + + + + + + + + + `); + _converse.connection._dataRecv(mock.createRequest(metadata_stanza)); + + await u.waitUntil(() => view.querySelector('converse-message-unfurl')); + const button = await u.waitUntil(() => view.querySelector('.chat-msg__content .chat-msg__action-hide-previews')); + button.click(); + await u.waitUntil(() => view.querySelector('converse-message-unfurl') === null, 750); + button.click(); + await u.waitUntil(() => view.querySelector('converse-message-unfurl'), 750); + done(); + })); }); diff --git a/src/components/message-actions.js b/src/components/message-actions.js index 63b5217fe..2e26f2825 100644 --- a/src/components/message-actions.js +++ b/src/components/message-actions.js @@ -12,11 +12,13 @@ class MessageActions extends CustomElement { static get properties () { return { - model: { type: Object }, - editable: { type: Boolean }, correcting: { type: Boolean }, - message_type: { type: String }, + editable: { type: Boolean }, + hide_url_previews: { type: Boolean }, is_retracted: { type: Boolean }, + message_type: { type: String }, + model: { type: Object }, + unfurls: { type: Number } } } @@ -147,7 +149,7 @@ class MessageActions extends CustomElement { } onMessageRetractButtonClicked (ev) { - ev.preventDefault(); + ev?.preventDefault?.(); const chatbox = this.model.collection.chatbox; if (chatbox.get('type') === _converse.CHATROOMS_TYPE) { this.onMUCMessageRetractButtonClicked(); @@ -156,6 +158,19 @@ class MessageActions extends CustomElement { } } + onHidePreviewsButtonClicked (ev) { + ev?.preventDefault?.(); + if (this.hide_url_previews) { + this.model.save({ + 'hide_url_previews': false, + 'url_preview_transition': 'fade-in' + }); + } else { + this.model.set('url_preview_transition', 'fade-out'); + } + + } + async getActionButtons () { const buttons = []; if (this.editable) { @@ -178,6 +193,29 @@ class MessageActions extends CustomElement { 'name': 'retract' }); } + + const ogp_metadata = this.model.get('ogp_metadata') || []; + const chatbox = this.model.collection.chatbox; + if (chatbox.get('type') === _converse.CHATROOMS_TYPE && + api.settings.get('muc_show_ogp_unfurls') && + ogp_metadata.length) { + + let title; + const hidden_preview = this.hide_url_previews; + if (ogp_metadata.length > 1) { + title = hidden_preview ? __('Show URL previews') : __('Hide URL previews'); + } else { + title = hidden_preview ? __('Show URL preview') : __('Hide URL preview'); + } + buttons.push({ + 'i18n_text': title, + 'handler': ev => this.onHidePreviewsButtonClicked(ev), + 'button_class': 'chat-msg__action-hide-previews', + 'icon_class': this.hide_url_previews ? 'fas fa-eye' : 'fas fa-eye-slash', + 'name': 'hide' + }); + } + /** * *Hook* which allows plugins to add more message action buttons * @event _converse#getMessageActionButtons diff --git a/src/shared/chat/message.js b/src/shared/chat/message.js index fffe77eee..18701c482 100644 --- a/src/shared/chat/message.js +++ b/src/shared/chat/message.js @@ -7,7 +7,7 @@ import OccupantModal from 'modals/occupant.js'; import UserDetailsModal from 'modals/user-details.js'; import dayjs from 'dayjs'; import filesize from 'filesize'; -import tpl_chat_message from './templates/message.js'; +import tpl_message from './templates/message.js'; import tpl_spinner from 'templates/spinner.js'; import { CustomElement } from 'components/element.js'; import { __ } from 'i18n'; @@ -126,7 +126,7 @@ export default class Message extends CustomElement { } renderChatMessage () { - return tpl_chat_message(this); + return tpl_message(this); } shouldShowAvatar () { @@ -145,6 +145,15 @@ export default class Message extends CustomElement { }; } + onUnfurlAnimationEnd () { + if (this.model.get('url_preview_transition') === 'fade-out') { + this.model.save({ + 'hide_url_previews': !this.model.get('hide_url_previews'), + 'url_preview_transition': 'fade-in' + }); + } + } + async onRetryClicked () { this.show_spinner = true; await api.trigger(this.retry_event_id, {'synchronous': true}); diff --git a/src/shared/chat/templates/message.js b/src/shared/chat/templates/message.js index 764d178b9..3ffb42b57 100644 --- a/src/shared/chat/templates/message.js +++ b/src/shared/chat/templates/message.js @@ -38,16 +38,20 @@ export default (o) => { ?correcting="${o.correcting}" ?editable="${o.editable}" ?is_retracted="${o.is_retracted}" + ?hide_url_previews="${o.model.get('hide_url_previews')}" + unfurls="${o.model.get('ogp_metadata')?.length}" message_type="${o.message_type}"> - ${ o.model.get('ogp_metadata')?.map(m => + ${ !o.model.get('hide_url_previews') ? o.model.get('ogp_metadata')?.map(m => html``) } + url="${m['og:url'] || ''}">`) : '' } `; }