diff --git a/client/css/style.css b/client/css/style.css
index 0c6f9947..c5e8baff 100644
--- a/client/css/style.css
+++ b/client/css/style.css
@@ -238,6 +238,7 @@ kbd {
#chat .nick .from::before,
#chat .action .from::before,
#chat .toggle-button::after,
+#chat .toggle-content .more-caret::before,
#version-checker::before,
.context-menu-item::before,
#help .website-link::before,
@@ -1332,75 +1333,112 @@ kbd {
color: #f00;
}
+#chat .toggle-content.opened .more-caret, /* Expand/Collapse link previews */
#chat .toggle-button.opened, /* Thumbnail toggle */
#chat .msg.condensed:not(.closed) .toggle-button { /* Expanded status message toggle */
transform: rotate(90deg);
}
#chat .toggle-content {
- background: #f5f5f5;
- border-radius: 2px;
+ background: #f6f6f6;
+ border-radius: 5px;
display: none;
- color: #222;
max-width: 100%;
margin: 0;
margin-top: 6px;
overflow: hidden;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+ contain: content; /* This fixes a display bug where border-radius stopped working on image link hover */
}
-#chat .toggle-type-error {
- background: transparent;
-}
-
+/* This applies to images of preview-type-image and thumbnails of preview-type-link */
#chat .toggle-content img {
max-width: 100%;
max-height: 128px;
display: block;
+ cursor: zoom-in;
}
-#chat .prefetch-error {
+#chat .toggle-content pre.prefetch-error {
+ padding: 0;
+ margin: 0;
+ color: inherit;
+ background-color: transparent;
+}
+
+#chat .toggle-content .prefetch-error {
display: none;
}
+#chat .toggle-content.opened .prefetch-error {
+ display: inline;
+}
+
+/* This applies to thumbnails of preview-type-link only */
#chat .toggle-content .thumb {
- max-width: 48px;
- max-height: 38px;
+ max-height: 54px;
+ max-width: 96px;
}
-#chat .toggle-thumbnail {
- padding: 6px;
+#chat .toggle-type-error,
+#chat .toggle-content .toggle-text {
+ padding: 8px 10px;
}
-#chat .toggle-text {
- padding: 6px;
- min-width: 0;
- display: flex;
- flex-direction: column;
+#chat .toggle-content .toggle-text {
white-space: nowrap;
- color: inherit;
-}
-
-#chat .toggle-text:not(:first-child) {
- padding-left: 0;
-}
-
-#chat .toggle-content .head,
-#chat .toggle-content .body {
- text-overflow: ellipsis;
overflow: hidden;
- color: inherit;
+}
+
+#chat .toggle-content.opened .toggle-text {
+ white-space: normal;
}
#chat .toggle-content .head {
+ display: flex;
+ align-items: flex-start;
font-weight: bold;
+ color: #222;
}
-#chat .toggle-content .body {
- color: #999;
+#chat .toggle-type-error,
+#chat .toggle-text .body {
+ color: #717171;
+}
+
+#chat .toggle-text a {
+ color: inherit;
+}
+
+#chat .toggle-text .overflowable {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ flex-grow: 1;
+}
+
+#chat .toggle-content .more {
+ color: #50a656;
+ font-weight: normal;
+ margin-left: 10px;
+ flex-shrink: 0;
+}
+
+#chat .toggle-content .more::after {
+ content: attr(aria-label);
+}
+
+#chat .toggle-content .more-caret {
+ display: inline-block;
+ transition: transform 0.2s;
+}
+
+#chat .toggle-content .more-caret::before {
+ content: "\f0da"; /* https://fontawesome.com/icons/caret-right?style=solid */
}
#chat .toggle-content.show {
display: inline-flex !important;
+ align-items: flex-start;
}
#chat audio {
@@ -2322,6 +2360,11 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
#chat .header .title {
padding-left: 6px;
}
+
+ #chat .toggle-content .thumb {
+ max-height: 58px;
+ max-width: 104px;
+ }
}
@media (max-width: 479px) {
diff --git a/client/js/lounge.js b/client/js/lounge.js
index 106ca1e2..9d98aef6 100644
--- a/client/js/lounge.js
+++ b/client/js/lounge.js
@@ -36,6 +36,8 @@ $(function() {
}
storage.set(name, state);
+
+ utils.togglePreviewMoreButtonsIfNeeded();
}
// If sidebar overlay is visible and it is clicked, close the sidebar
@@ -277,6 +279,8 @@ $(function() {
.addClass("active")
.trigger("show");
+ utils.togglePreviewMoreButtonsIfNeeded();
+
let title = $(document.body).data("app-name");
const chanTitle = chan.attr("aria-label");
diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js
index 99fb0f53..1f4ddf77 100644
--- a/client/js/renderPreview.js
+++ b/client/js/renderPreview.js
@@ -1,11 +1,15 @@
"use strict";
const $ = require("jquery");
+const debounce = require("lodash/debounce");
+const Mousetrap = require("mousetrap");
+
const options = require("./options");
const socket = require("./socket");
const templates = require("../views");
const chat = $("#chat");
-const Mousetrap = require("mousetrap");
+
+const {togglePreviewMoreButtonsIfNeeded} = require("./utils");
module.exports = renderPreview;
@@ -57,7 +61,8 @@ function appendPreview(preview, msg, template) {
}
const container = msg.closest(".chat");
- const channelId = container.closest(".chan").data("id") || -1;
+ const channel = container.closest(".chan");
+ const channelId = channel.data("id") || -1;
const activeChannelId = chat.find(".chan.active").data("id") || -2;
msg.find(`.text a[href="${escapedLink}"]`)
@@ -66,11 +71,46 @@ function appendPreview(preview, msg, template) {
previewContainer.append(template);
+ const moreBtn = previewContainer.find(".more");
+ const previewContent = previewContainer.find(".toggle-content");
+
+ // Depending on the size of the preview and the text within it, show or hide a
+ // "More" button that allows users to expand without having to open the link.
+ // Warning: Make sure to call this only on active channel, link previews only,
+ // expanded only.
+ const showMoreIfNeeded = () => {
+ const isVisible = moreBtn.is(":visible");
+ const shouldShow = previewContent[0].offsetWidth >= previewContainer[0].offsetWidth;
+
+ if (!isVisible && shouldShow) {
+ moreBtn.show();
+ } else if (isVisible && !shouldShow) {
+ togglePreviewMore(moreBtn, false);
+ moreBtn.hide();
+ }
+ };
+
+ // "More" button only applies on text previews
+ if (preview.type === "link") {
+ // This event is triggered when a side menu is opened/closed, or when the
+ // preview gets expanded/collapsed.
+ previewContent.on("showMoreIfNeeded",
+ () => window.requestAnimationFrame(showMoreIfNeeded)
+ );
+ }
+
if (activeChannelId === channelId) {
+ // If this preview is in active channel, hide "More" button if necessary
+ previewContent.trigger("showMoreIfNeeded");
+
container.trigger("keepToBottom");
}
}
+// On resize, previews in the current channel that are expanded need to compute
+// their "More" button. Debounced handler to avoid performance cost.
+$(window).on("resize", debounce(togglePreviewMoreButtonsIfNeeded, 150));
+
$("#chat").on("click", ".text .toggle-button", function() {
const self = $(this);
const container = self.closest(".chat");
@@ -81,6 +121,12 @@ $("#chat").on("click", ".text .toggle-button", function() {
self.toggleClass("opened");
content.toggleClass("show");
+ const isExpanded = content.hasClass("show");
+
+ if (isExpanded) {
+ content.trigger("showMoreIfNeeded");
+ }
+
// Tell the server we're toggling so it remembers at page reload
// TODO Avoid sending many single events when using `/collapse` or `/expand`
// See https://github.com/thelounge/thelounge/issues/1377
@@ -88,7 +134,7 @@ $("#chat").on("click", ".text .toggle-button", function() {
target: parseInt(self.closest(".chan").data("id"), 10),
msgId: parseInt(self.closest(".msg").prop("id").replace("msg-", ""), 10),
link: self.data("url"),
- shown: content.hasClass("show"),
+ shown: isExpanded,
});
// If scrollbar was at the bottom before toggling the preview, keep it at the bottom
@@ -97,6 +143,24 @@ $("#chat").on("click", ".text .toggle-button", function() {
}
});
+$("#chat").on("click", ".toggle-content .more", function() {
+ togglePreviewMore($(this));
+ return false;
+});
+
+function togglePreviewMore(moreBtn, state = undefined) {
+ moreBtn.closest(".toggle-content").toggleClass("opened", state);
+ const isExpanded = moreBtn.closest(".toggle-content").hasClass("opened");
+
+ moreBtn.attr("aria-expanded", isExpanded);
+
+ if (isExpanded) {
+ moreBtn.attr("aria-label", moreBtn.data("opened-text"));
+ } else {
+ moreBtn.attr("aria-label", moreBtn.data("closed-text"));
+ }
+}
+
/* Image viewer */
const imageViewer = $("#image-viewer");
diff --git a/client/js/utils.js b/client/js/utils.js
index e9f73090..5433247b 100644
--- a/client/js/utils.js
+++ b/client/js/utils.js
@@ -23,6 +23,7 @@ module.exports = {
toggleNickEditor,
toggleNotificationMarkers,
requestIdleCallback,
+ togglePreviewMoreButtonsIfNeeded,
};
function findCurrentNetworkChan(name) {
@@ -141,3 +142,9 @@ function requestIdleCallback(callback, timeout) {
callback();
}
}
+
+// Force handling preview display
+function togglePreviewMoreButtonsIfNeeded() {
+ $("#chat .chan.active .toggle-content.toggle-type-link.show")
+ .trigger("showMoreIfNeeded");
+}
diff --git a/client/views/msg_preview.tpl b/client/views/msg_preview.tpl
index 1e1cb916..5186e133 100644
--- a/client/views/msg_preview.tpl
+++ b/client/views/msg_preview.tpl
@@ -23,10 +23,31 @@
{{/if}}
-
-