From f0382afd837f40bb8f6e899ae5ecdead3f6efe95 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Thu, 19 Jun 2025 10:47:16 +0200 Subject: [PATCH] Fixes for "scroll to latest" --- src/assets/css/chat.scss | 6 +++ src/components/Chat.vue | 83 +++++++++++++++++++++++----------------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/assets/css/chat.scss b/src/assets/css/chat.scss index 630d959..e4537b0 100644 --- a/src/assets/css/chat.scss +++ b/src/assets/css/chat.scss @@ -369,10 +369,16 @@ body { position: absolute; bottom: 102px; right: 16px; + opacity: 1; + transition: opacity 0.3s linear; &.reversed { top: 120px; transform: rotate(180deg); } + &.hidden { + pointer-events: none; + opacity: 0; + } } .op-button { diff --git a/src/components/Chat.vue b/src/components/Chat.vue index 4f46401..fe57934 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -120,7 +120,7 @@ - @@ -410,6 +410,7 @@ export default { timelineWindowPaginating: false, scrollPosition: null, + scrollUpdateTimer: null, uploadBatch: undefined, showEmojiPicker: false, selectedEvent: null, @@ -1210,13 +1211,7 @@ export default { this.handleScrolledToLatest(false); } } - - this.showScrollToEnd = - container.scrollHeight === container.clientHeight - ? false - : (this.reverseOrder ? (container.scrollTop.toFixed(0) > 0) : (container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight)) || - (this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS)); - + this.delayedUpdateOfScrollToBottom(); this.restartRRTimer(); }, @@ -1284,6 +1279,24 @@ export default { } }, + delayedUpdateOfScrollToBottom() { + if (this.scrollUpdateTimer) { + clearTimeout(this.scrollUpdateTimer); + } + this.scrollUpdateTimer = setTimeout(() => { + this.scrollUpdateTimer = null; + const container = this.chatContainer; + if (!container) { + return; + } + this.showScrollToEnd = + container.scrollHeight === container.clientHeight + ? false + : (this.reverseOrder ? (container.scrollTop.toFixed(0) > 0) : (container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight)) || + (this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS)); + }, 1000); + }, + onEvent(event) { //console.log("OnEvent", JSON.stringify(event)); if (event.getRoomId() !== this.roomId) { @@ -1307,16 +1320,7 @@ export default { if (loadingDone && event.forwardLooking && (!event.isRelation() || event.isMxThread || event.threadRootId || event.parentThread)) { // If we are at bottom, scroll to see new events... var scrollToSeeNew = !event.isRedaction() && event.getSender() == this.$matrix.currentUserId; // When we sent, scroll - const container = this.chatContainer; - if (container) { - if (this.reverseOrder && container.scrollTop.toFixed(0) == 0) { - scrollToSeeNew = true; - } - else if (!this.reverseOrder && container.scrollHeight - container.scrollTop.toFixed(0) == container.clientHeight) { - scrollToSeeNew = true; - } - } - this.handleScrolledToLatest(scrollToSeeNew); + this.handleScrolledToLatest(scrollToSeeNew || !this.showScrollToEnd); // If kick or ban event, redirect to "goodbye"... if (event.getType() === "m.room.member" && @@ -1516,32 +1520,41 @@ export default { }, smoothScrollToLatest() { - this.$nextTick(function () { + this.$nextTick(() => { + // TODO - fix this. We need to wait until the "lastChild" below has been + // laid out correctly. If we do it "too early" it will have zero height and + // thus not correctly scrolled into the viewport. So we wait a few ticks... + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { const container = this.chatContainer; if (container && container.children.length > 0) { if (this.reverseOrder) { const firstChild = container.children[0]; - console.log("Scroll into view", firstChild); - window.requestAnimationFrame(() => { - firstChild.scrollIntoView({ - behavior: "smooth", - block: "end", - inline: "nearest", + window.requestAnimationFrame(() => { + firstChild.scrollIntoView({ + behavior: "smooth", + block: "end", + inline: "nearest", + }); }); - }); } else { - const lastChild = container.children[container.children.length - 1]; - console.log("Scroll into view", lastChild); - window.requestAnimationFrame(() => { - lastChild.scrollIntoView({ - behavior: "smooth", - block: "start", - inline: "nearest", + const lastChild = container.children[container.children.length - 1]; + window.requestAnimationFrame(() => { + lastChild.scrollIntoView({ + behavior: "smooth", + block: "start", + inline: "nearest", + }); }); - }); + } } - } }); + }) + }) + }) + }) }, showMoreMessageOperations(e) {