From 7355db94d62e84706bd5ae5ff21c007865fc5381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Thu, 22 Mar 2018 01:17:44 -0400 Subject: [PATCH 01/10] Improve UI of previews - Remove margins around thumbnails and images, increase radius, etc. for a fresher look overall - Increase preview body contrast just enough to pass AA standards - Add a `More`/`Less` button to expand previews that take more than one line. Button is added only when necessary - Use a zoom-in cursor on hover for thumbnails - Improve preview message errors --- client/css/style.css | 103 +++++++++++++++++++++++++---------- client/js/lounge.js | 4 ++ client/js/renderPreview.js | 38 +++++++++++++ client/js/utils.js | 6 ++ client/views/msg_preview.tpl | 53 ++++++++++++++---- 5 files changed, 164 insertions(+), 40 deletions(-) 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..be9d6fc9 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -49,6 +49,7 @@ $(function() { slideoutMenu.toggle(isOpen); storeSidebarVisibility("thelounge.state.sidebar", isOpen); + utils.togglePreviewMoreButtonsIfNeeded(); }); viewport.on("click", ".rt", function() { @@ -57,6 +58,7 @@ $(function() { viewport.toggleClass("rt", isOpen); chat.find(".chan.active .chat").trigger("keepToBottom"); storeSidebarVisibility("thelounge.state.userlist", isOpen); + utils.togglePreviewMoreButtonsIfNeeded(); return false; }); @@ -252,6 +254,8 @@ $(function() { if (!keepSidebarOpen && $(window).outerWidth() < utils.mobileViewportPixels) { slideoutMenu.toggle(false); } + + utils.togglePreviewMoreButtonsIfNeeded(); } const lastActive = $("#windows > .active"); diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 99fb0f53..8530d053 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -66,6 +66,26 @@ function appendPreview(preview, msg, template) { previewContainer.append(template); + const moreBtn = previewContainer.find(".more"); + const previewContent = previewContainer.find(".toggle-content")[0]; + + const showMoreIfNeeded = () => { + if (preview.type === "link") { + const isVisible = moreBtn.is(":visible"); + const shouldShow = previewContent.offsetWidth >= previewContainer[0].offsetWidth; + + if (!isVisible && shouldShow) { + moreBtn.show(); + } else if (isVisible && !shouldShow) { + togglePreviewMore(moreBtn, false); + moreBtn.hide(); + } + } + }; + + $(window).on("resize", showMoreIfNeeded); + window.requestAnimationFrame(showMoreIfNeeded); + if (activeChannelId === channelId) { container.trigger("keepToBottom"); } @@ -97,6 +117,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..a7660d8f 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,8 @@ function requestIdleCallback(callback, timeout) { callback(); } } + +// Force handling preview display +function togglePreviewMoreButtonsIfNeeded() { + window.requestAnimationFrame(() => $(window).trigger("resize")); +} 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}} - -
{{head}}
-
{{body}}
-
+
+
+ + + +
+ + + +
{{/equal}} {{#equal type "error"}} {{#equal error "image-too-big"}} @@ -38,12 +59,24 @@ {{/equal}} {{#equal error "message"}} - - There was an error loading preview for this link. - Click here - to open it in a new window. - ({{message}}) - +
+ + A preview could not be loaded. + Click here + to open it in a new window. + +
+
{{message}}
+
+ + {{/equal}} {{/equal}} From 527cac4c759f186ae2bb921e75069d00263f1918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Fri, 6 Apr 2018 00:40:27 -0400 Subject: [PATCH 02/10] Debounce resize event to avoid performance bottleneck --- client/js/renderPreview.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 8530d053..5b4567f3 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -1,11 +1,13 @@ "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"); module.exports = renderPreview; @@ -83,7 +85,7 @@ function appendPreview(preview, msg, template) { } }; - $(window).on("resize", showMoreIfNeeded); + $(window).on("resize", debounce(showMoreIfNeeded, 150)); window.requestAnimationFrame(showMoreIfNeeded); if (activeChannelId === channelId) { From 5bff07f6f67cdc6424f4f7de53a7892b9a4de82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sat, 7 Apr 2018 02:47:49 -0400 Subject: [PATCH 03/10] Fix handling preview More buttons on inactive channels --- client/js/renderPreview.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 5b4567f3..0887ac02 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -59,7 +59,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}"]`) @@ -72,7 +73,7 @@ function appendPreview(preview, msg, template) { const previewContent = previewContainer.find(".toggle-content")[0]; const showMoreIfNeeded = () => { - if (preview.type === "link") { + if (preview.type === "link" && channel.hasClass("active")) { const isVisible = moreBtn.is(":visible"); const shouldShow = previewContent.offsetWidth >= previewContainer[0].offsetWidth; From 53a2101de367c486047bc449a5010f7c6f8ce629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sat, 7 Apr 2018 22:48:04 -0400 Subject: [PATCH 04/10] Make sure preview "More" button computation does nothing when preview is collapsed --- client/js/renderPreview.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 0887ac02..de1b1301 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -70,12 +70,16 @@ function appendPreview(preview, msg, template) { previewContainer.append(template); const moreBtn = previewContainer.find(".more"); - const previewContent = previewContainer.find(".toggle-content")[0]; + const previewContent = previewContainer.find(".toggle-content"); const showMoreIfNeeded = () => { - if (preview.type === "link" && channel.hasClass("active")) { + // Only applies on: + if (preview.type === "link" && // link previews + channel.hasClass("active") && // in the current channels + previewContent.hasClass("show") // that are expanded + ) { const isVisible = moreBtn.is(":visible"); - const shouldShow = previewContent.offsetWidth >= previewContainer[0].offsetWidth; + const shouldShow = previewContent[0].offsetWidth >= previewContainer[0].offsetWidth; if (!isVisible && shouldShow) { moreBtn.show(); @@ -88,6 +92,7 @@ function appendPreview(preview, msg, template) { $(window).on("resize", debounce(showMoreIfNeeded, 150)); window.requestAnimationFrame(showMoreIfNeeded); + previewContent.on("showMoreIfNeeded", showMoreIfNeeded); if (activeChannelId === channelId) { container.trigger("keepToBottom"); @@ -104,6 +109,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 @@ -111,7 +122,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 From 1ccdeb015cf6eac8d82ccf70c7387f16f30b6805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sat, 7 Apr 2018 23:20:14 -0400 Subject: [PATCH 05/10] Trigger the correct event directly instead of resize when opening/closing a menu --- client/js/renderPreview.js | 5 ++++- client/js/utils.js | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index de1b1301..083122e2 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -92,7 +92,10 @@ function appendPreview(preview, msg, template) { $(window).on("resize", debounce(showMoreIfNeeded, 150)); window.requestAnimationFrame(showMoreIfNeeded); - previewContent.on("showMoreIfNeeded", showMoreIfNeeded); + previewContent.on( + "showMoreIfNeeded", + () => window.requestAnimationFrame(showMoreIfNeeded) + ); if (activeChannelId === channelId) { container.trigger("keepToBottom"); diff --git a/client/js/utils.js b/client/js/utils.js index a7660d8f..5433247b 100644 --- a/client/js/utils.js +++ b/client/js/utils.js @@ -145,5 +145,6 @@ function requestIdleCallback(callback, timeout) { // Force handling preview display function togglePreviewMoreButtonsIfNeeded() { - window.requestAnimationFrame(() => $(window).trigger("resize")); + $("#chat .chan.active .toggle-content.toggle-type-link.show") + .trigger("showMoreIfNeeded"); } From 8d255fc33154ba3e2489fdcbff72d10207b40fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sat, 7 Apr 2018 23:22:55 -0400 Subject: [PATCH 06/10] Only bind preview "More" buttons on link previews --- client/js/renderPreview.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 083122e2..9598bd58 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -73,9 +73,8 @@ function appendPreview(preview, msg, template) { const previewContent = previewContainer.find(".toggle-content"); const showMoreIfNeeded = () => { - // Only applies on: - if (preview.type === "link" && // link previews - channel.hasClass("active") && // in the current channels + // Only applies on previews: + if (channel.hasClass("active") && // in the current channel previewContent.hasClass("show") // that are expanded ) { const isVisible = moreBtn.is(":visible"); @@ -90,12 +89,13 @@ function appendPreview(preview, msg, template) { } }; - $(window).on("resize", debounce(showMoreIfNeeded, 150)); - window.requestAnimationFrame(showMoreIfNeeded); - previewContent.on( - "showMoreIfNeeded", - () => window.requestAnimationFrame(showMoreIfNeeded) - ); + if (preview.type === "link") { + $(window).on("resize", debounce(showMoreIfNeeded, 150)); + window.requestAnimationFrame(showMoreIfNeeded); + previewContent.on("showMoreIfNeeded", + () => window.requestAnimationFrame(showMoreIfNeeded) + ); + } if (activeChannelId === channelId) { container.trigger("keepToBottom"); From dbabdd83dcadc2e481ea47ff874b69a033f31fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sun, 8 Apr 2018 01:04:25 -0400 Subject: [PATCH 07/10] Lighten the "More" button handler when checks are not necessary --- client/js/lounge.js | 4 ++-- client/js/renderPreview.js | 40 +++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/client/js/lounge.js b/client/js/lounge.js index be9d6fc9..bf35b900 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -254,8 +254,6 @@ $(function() { if (!keepSidebarOpen && $(window).outerWidth() < utils.mobileViewportPixels) { slideoutMenu.toggle(false); } - - utils.togglePreviewMoreButtonsIfNeeded(); } const lastActive = $("#windows > .active"); @@ -281,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 9598bd58..47364883 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -72,32 +72,44 @@ function appendPreview(preview, msg, 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 = () => { - // Only applies on previews: - if (channel.hasClass("active") && // in the current channel - previewContent.hasClass("show") // that are expanded - ) { - const isVisible = moreBtn.is(":visible"); - const shouldShow = previewContent[0].offsetWidth >= previewContainer[0].offsetWidth; + 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(); - } + 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") { - $(window).on("resize", debounce(showMoreIfNeeded, 150)); - window.requestAnimationFrame(showMoreIfNeeded); + // On resize, only touch previews in the current channel that are expanded + $(window).on("resize", debounce(() => { + if (channel.hasClass("active") && previewContent.hasClass("show")) { + showMoreIfNeeded(); + } + }, 150)); + + // 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 + if (preview.type === "link") { + window.requestAnimationFrame(showMoreIfNeeded); + } + container.trigger("keepToBottom"); } } From ac02bd370bcb05eac84d52e35086928b35db3ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sun, 8 Apr 2018 17:37:33 -0400 Subject: [PATCH 08/10] Do not compute preview "More" buttons on mobile --- client/js/lounge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/js/lounge.js b/client/js/lounge.js index bf35b900..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 @@ -49,7 +51,6 @@ $(function() { slideoutMenu.toggle(isOpen); storeSidebarVisibility("thelounge.state.sidebar", isOpen); - utils.togglePreviewMoreButtonsIfNeeded(); }); viewport.on("click", ".rt", function() { @@ -58,7 +59,6 @@ $(function() { viewport.toggleClass("rt", isOpen); chat.find(".chan.active .chat").trigger("keepToBottom"); storeSidebarVisibility("thelounge.state.userlist", isOpen); - utils.togglePreviewMoreButtonsIfNeeded(); return false; }); From d55420faaec4446d58b882de811b688847739da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sun, 8 Apr 2018 17:49:00 -0400 Subject: [PATCH 09/10] Bind a single resize event, debounced, instead of one handler per preview --- client/js/renderPreview.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 47364883..423d6398 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -9,6 +9,8 @@ const socket = require("./socket"); const templates = require("../views"); const chat = $("#chat"); +const {togglePreviewMoreButtonsIfNeeded} = require("./utils"); + module.exports = renderPreview; function renderPreview(preview, msg) { @@ -90,13 +92,6 @@ function appendPreview(preview, msg, template) { // "More" button only applies on text previews if (preview.type === "link") { - // On resize, only touch previews in the current channel that are expanded - $(window).on("resize", debounce(() => { - if (channel.hasClass("active") && previewContent.hasClass("show")) { - showMoreIfNeeded(); - } - }, 150)); - // This event is triggered when a side menu is opened/closed, or when the // preview gets expanded/collapsed. previewContent.on("showMoreIfNeeded", @@ -114,6 +109,10 @@ function appendPreview(preview, msg, template) { } } +// 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"); From bb16b3e30bdc2afda7bd29c11281005101aabb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Sun, 8 Apr 2018 17:50:27 -0400 Subject: [PATCH 10/10] Simplify showing/hiding "More" button of a new preview on active channel --- client/js/renderPreview.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 423d6398..1f4ddf77 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -101,9 +101,7 @@ function appendPreview(preview, msg, template) { if (activeChannelId === channelId) { // If this preview is in active channel, hide "More" button if necessary - if (preview.type === "link") { - window.requestAnimationFrame(showMoreIfNeeded); - } + previewContent.trigger("showMoreIfNeeded"); container.trigger("keepToBottom"); }