diff --git a/src/assets/css/chat.scss b/src/assets/css/chat.scss index 0636372..423abd4 100644 --- a/src/assets/css/chat.scss +++ b/src/assets/css/chat.scss @@ -341,8 +341,12 @@ body { .scroll-to-end { position: absolute; - top: -64px; + bottom: 20px; right: 16px; + &.reversed { + top: 120px; + transform: rotate(180deg); + } } .op-button { diff --git a/src/components/Chat.vue b/src/components/Chat.vue index b165bcb..55a0db9 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -16,8 +16,8 @@ :readMarker="readMarker" :recordingMembers="typingMembers" v-on:start-recording="setShowRecorder()" - v-on:loadnext="handleScrolledToBottom(false)" - v-on:loadprevious="handleScrolledToTop()" + v-on:loadnext="handleScrolledToLatest(false)" + v-on:loadprevious="handleScrolledToOldest()" v-on:mark-read="sendRR" v-on:sendclap="sendClapReactionAtTime" /> @@ -58,7 +58,7 @@
-
{{ dayForEvent(event) }}
+
{{ dayForEvent(event) }}
@@ -67,7 +67,10 @@ touchStart(e, event); } " v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove"> - -
{{ $t('message.unread_messages') }}
+
{{ $t('message.unread_messages') }}
+ + +
{{ dayForEvent(event) }}
+ + + + + arrow_downward + + +
- - - arrow_downward - -
@@ -512,7 +521,8 @@ export default { heartPosition: { top: 0, left: 0 - } + }, + reverseOrder: false }; }, @@ -692,12 +702,12 @@ export default { (!e.getPrevContent() || e.getPrevContent().membership != "join") && e.getStateKey() == this.$matrix.currentUserId) { // Our own join event. - return this.events.slice(idx + 1); + return this.reverseOrder ? this.events.slice(idx + 1).toReversed() : this.events.slice(idx + 1); } } } } - return this.events; + return this.reverseOrder ? this.events.toReversed() : this.events; }, roomCreatedByUsRecently() { @@ -1080,7 +1090,7 @@ export default { }); } else { // Can't paginate, just scroll to bottom of window! - this.smoothScrollToEnd(); + this.smoothScrollToLatest(); } }, @@ -1150,7 +1160,7 @@ export default { this.$nextTick(() => { const container = this.chatContainer; if (container && container.scrollHeight <= container.clientHeight) { - this.handleScrolledToTop(); + this.handleScrolledToOldest(); } }); }, @@ -1162,15 +1172,23 @@ export default { const bufferHeight = container.clientHeight * WINDOW_BUFFER_SIZE; if (container.scrollTop <= bufferHeight) { // Scrolled to top - this.handleScrolledToTop(); + if (this.reverseOrder) { + this.handleScrolledToLatest(false); + } else { + this.handleScrolledToOldest(); + } } else if (container.scrollHeight - container.scrollTop.toFixed(0) - container.clientHeight <= bufferHeight) { - this.handleScrolledToBottom(false); + if (this.reverseOrder) { + this.handleScrolledToOldest(); + } else { + this.handleScrolledToLatest(false); + } } this.showScrollToEnd = container.scrollHeight === container.clientHeight ? false - : container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight || + : (this.reverseOrder ? (container.scrollTop.toFixed(0) > 0) : (container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight)) || (this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS)); this.restartRRTimer(); @@ -1265,11 +1283,14 @@ export default { var scrollToSeeNew = event.getSender() == this.$matrix.currentUserId; // When we sent, scroll const container = this.chatContainer; if (container) { - if (container.scrollHeight - container.scrollTop.toFixed(0) == container.clientHeight) { + 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.handleScrolledToBottom(scrollToSeeNew); + this.handleScrolledToLatest(scrollToSeeNew); // If kick or ban event, redirect to "goodbye"... if (event.getType() === "m.room.member" && @@ -1494,7 +1515,7 @@ export default { }); }, - handleScrolledToTop() { + handleScrolledToOldest() { if ( this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.BACKWARDS) && @@ -1505,7 +1526,7 @@ export default { .paginate(EventTimeline.BACKWARDS, 10, true) .then((success) => { if (success && this.scrollPosition) { - this.scrollPosition.prepareFor("up"); + this.scrollPosition.prepareFor(this.reverseOrder ? "down" : "up"); this.setEvents(this.timelineWindow.getEvents()); this.$nextTick(() => { // restore scroll position! @@ -1520,7 +1541,7 @@ export default { } }, - handleScrolledToBottom(scrollToEnd) { + handleScrolledToLatest(smoothScrollToLatest) { if ( this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS) && @@ -1533,13 +1554,13 @@ export default { if (success) { this.setEvents(this.timelineWindow.getEvents()); if (!this.useVoiceMode && this.scrollPosition) { - this.scrollPosition.prepareFor("down"); + this.scrollPosition.prepareFor(this.reverseOrder ? "up" : "down"); this.$nextTick(() => { // restore scroll position! console.log("Restore scroll!"); this.scrollPosition.restore(); - if (scrollToEnd) { - this.smoothScrollToEnd(); + if (smoothScrollToLatest) { + this.smoothScrollToLatest(); } }); } @@ -1555,6 +1576,7 @@ export default { * Scroll so that the given event is at the middle of the chat view (if more events) or else at the bottom. */ scrollToEvent(eventId) { + console.log("Scroll to event", eventId); const container = this.chatContainer; const ref = this.$refs[eventId]; if (container && ref) { @@ -1562,7 +1584,7 @@ export default { const item = ref[0].getBoundingClientRect(); let offsetY = (parent.bottom - parent.top) / 2; if (ref[0].clientHeight > offsetY) { - offsetY = Math.max(0, (parent.bottom - parent.top) - ref[0].clientHeight); + offsetY = this.reverseOrder ? 0 : Math.max(0, (parent.bottom - parent.top) - ref[0].clientHeight); } const targetY = parent.top + offsetY; const currentY = item.top; @@ -1573,10 +1595,21 @@ export default { } }, - smoothScrollToEnd() { + smoothScrollToLatest() { this.$nextTick(function () { 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", + }); + }); + } else { const lastChild = container.children[container.children.length - 1]; console.log("Scroll into view", lastChild); window.requestAnimationFrame(() => { @@ -1587,6 +1620,7 @@ export default { }); }); } + } }); }, @@ -1809,8 +1843,14 @@ export default { const elFirst = util.getFirstVisibleElement(container, (item) => item.hasAttribute("eventId")); const elLast = util.getLastVisibleElement(container, (item) => item.hasAttribute("eventId")); if (elFirst && elLast) { - eventIdFirst = elFirst.getAttribute("eventId"); - eventIdLast = elLast.getAttribute("eventId"); + if (this.reverseOrder) { + // For reverse order, the "first visible" is actually later in time, so swap them + eventIdFirst = elLast.getAttribute("eventId"); + eventIdLast = elFirst.getAttribute("eventId"); + } else { + eventIdFirst = elFirst.getAttribute("eventId"); + eventIdLast = elLast.getAttribute("eventId"); + } } } if (eventIdFirst && eventIdLast) {