diff --git a/src/assets/variables.scss b/src/assets/variables.scss index 968d6b9c1e4..b2b269af473 100644 --- a/src/assets/variables.scss +++ b/src/assets/variables.scss @@ -82,6 +82,8 @@ $chat-font-size: 15px; $chat-line-height: 1.6em; // fade transition +$fade-transition: all var(--animation-quick, 100ms) ease-in-out; + .fade { &-enter { opacity: 0; @@ -97,7 +99,7 @@ $chat-line-height: 1.6em; } &-enter-active, &-leave-active { - transition: all 150ms ease-in-out; + transition: $fade-transition; } } @@ -120,7 +122,7 @@ $chat-line-height: 1.6em; } &-enter-active, &-leave-active { - transition: all 150ms ease-in-out; + transition: $fade-transition; /* force top container to resize during animation */ position: absolute !important; } diff --git a/src/components/ChatView.vue b/src/components/ChatView.vue index efbe1c640d1..1ff11e34839 100644 --- a/src/components/ChatView.vue +++ b/src/components/ChatView.vue @@ -40,20 +40,15 @@ + :is-visible="isVisible" /> diff --git a/src/components/MessagesList/MessagesList.vue b/src/components/MessagesList/MessagesList.vue index b8a7b691abd..719e8155971 100644 --- a/src/components/MessagesList/MessagesList.vue +++ b/src/components/MessagesList/MessagesList.vue @@ -30,6 +30,7 @@ get the messagesList array and loop through the list to generate the messages. are outside of the viewport -->
{ - this.scrollToFocussedMessage() + this.scrollToFocusedMessage() }) } } + this.isInitialisingMessages = false + // get new messages await this.lookForNewMessages() @@ -581,7 +573,7 @@ export default { // don't scroll if lookForNewMessages was polling as we don't want // to scroll back to the read marker after receiving new messages later if (!hasScrolled) { - this.scrollToFocussedMessage() + this.scrollToFocusedMessage() } } } else { @@ -669,8 +661,6 @@ export default { requestId: this.chatIdentifier, }) - this.isInitialisingMessages = false - // Scroll to the last message if sticky if (scrollToBottom && this.isSticky) { this.smoothScrollToBottom() @@ -709,15 +699,7 @@ export default { }, 500) }, - debounceHandleScroll() { - if (this.loadChatInLegacyMode) { - this.debounceHandleScrollWithoutPreconditions() - } else if (!this.isInitialisingMessages && !this.isFocusingMessage) { - this.debounceHandleScrollWithoutPreconditions() - } - }, - - debounceHandleScrollWithoutPreconditions: debounce(function() { + debounceHandleScroll: debounce(function() { this.handleScroll() }, 50), @@ -745,16 +727,22 @@ export default { } } - const scrollHeight = this.scroller.scrollHeight - const scrollTop = this.scroller.scrollTop + const { scrollHeight, scrollTop, clientHeight } = this.$refs.scroller const scrollOffset = scrollHeight - scrollTop - const elementHeight = this.scroller.clientHeight const tolerance = 10 - if (scrollOffset < elementHeight + tolerance && scrollOffset > elementHeight - tolerance) { + + // For chats, scrolled to bottom or / and fitted in one screen + if (scrollOffset < clientHeight + tolerance && scrollOffset > clientHeight - tolerance) { this.setChatScrolledToBottom(true) this.displayMessagesLoader = false this.previousScrollTopValue = scrollTop - } else if (scrollHeight > elementHeight && scrollTop < 800 && scrollTop <= this.previousScrollTopValue) { + this.debounceUpdateReadMarkerPosition() + return + } + + this.setChatScrolledToBottom(false) + + if (scrollHeight > clientHeight && scrollTop < 800 && scrollTop <= this.previousScrollTopValue) { if (this.loadingOldMessages) { // already loading, don't do it twice return @@ -764,13 +752,9 @@ export default { } await this.getOldMessages(false) this.displayMessagesLoader = false - this.previousScrollTopValue = scrollTop - } else { - this.setChatScrolledToBottom(false) - this.displayMessagesLoader = false - this.previousScrollTopValue = scrollTop } + this.previousScrollTopValue = scrollTop this.debounceUpdateReadMarkerPosition() }, @@ -794,7 +778,7 @@ export default { } let previousEl = el - const scrollTop = this.scroller.scrollTop + const { scrollTop } = this.$refs.scroller while (el) { // is the message element fully visible with no intersection with the bottom border ? if (el.offsetTop - scrollTop >= 0) { @@ -850,7 +834,7 @@ export default { * but only do so if the previous marker was already seen. * * The new marker position will be sent to the backend but not applied visually. - * Visually, the marker will only move the next time the user is focussing back to this + * Visually, the marker will only move the next time the user is focusing back to this * conversation in refreshReadMarkerPosition() */ updateReadMarkerPosition() { @@ -884,7 +868,7 @@ export default { return } - if (lastReadMessageElement && (lastReadMessageElement.offsetTop - this.scroller.scrollTop > 0)) { + if (lastReadMessageElement && (lastReadMessageElement.offsetTop - this.$refs.scroller.scrollTop > 0)) { // still visible, hasn't disappeared at the top yet return } @@ -914,7 +898,6 @@ export default { handleScrollChatToBottomEvent(options) { if ((options && options.force) || this.isChatScrolledToBottom) { this.scrollToBottom() - this.setChatScrolledToBottom(true) } }, @@ -925,19 +908,19 @@ export default { this.$nextTick(function() { if (this.isWindowVisible && (document.hasFocus() || this.isInCall)) { // scrollTo is used when the user is watching - this.scroller.scrollTo({ - top: this.scroller.scrollHeight, + this.$refs.scroller.scrollTo({ + top: this.$refs.scroller.scrollHeight, behavior: 'smooth', }) this.setChatScrolledToBottom(true) } else { // Otherwise we jump half a message and stop autoscrolling, so the user can read up - if (this.scroller.scrollHeight - this.scroller.scrollTop - this.scroller.offsetHeight < 40) { + if (this.$refs.scroller.scrollHeight - this.$refs.scroller.scrollTop - this.$refs.scroller.offsetHeight < 40) { // Single new line from the previous author is 35px so scroll half a line - this.scroller.scrollTop += 10 + this.$refs.scroller.scrollTop += 10 } else { // Single new line from the new author is 75px so scroll half an avatar - this.scroller.scrollTop += 40 + this.$refs.scroller.scrollTop += 40 } this.setChatScrolledToBottom(false) } @@ -948,7 +931,7 @@ export default { */ scrollToBottom() { this.$nextTick(function() { - this.scroller.scrollTop = this.scroller.scrollHeight + this.$refs.scroller.scrollTop = this.$refs.scroller.scrollHeight this.setChatScrolledToBottom(true) }) @@ -982,13 +965,14 @@ export default { }) if (!smooth) { // scroll the viewport slightly further to make sure the element is about 1/3 from the top - this.scroller.scrollTop += this.scroller.offsetHeight / 4 + this.$refs.scroller.scrollTop += this.$refs.scroller.offsetHeight / 4 } if (highlightAnimation) { element.focus() element.highlightAnimation() } this.isFocusingMessage = false + await this.handleScroll() }) return true @@ -1051,7 +1035,7 @@ export default { }, setChatScrolledToBottom(boolean) { - this.$emit('set-chat-scrolled-to-bottom', boolean) + this.isChatScrolledToBottom = boolean if (boolean) { // mark as read if marker was seen // we have to do this early because unfocusing the window will remove the stickiness @@ -1073,6 +1057,13 @@ export default { flex: 1 0; overflow-y: auto; overflow-x: hidden; + border-bottom: 1px solid var(--color-border); + transition: $fade-transition; + + &--chatScrolledToBottom { + border-bottom-color: transparent; + } + &__loading { height: 50px; display: flex; diff --git a/src/components/NewMessageForm/NewMessageForm.vue b/src/components/NewMessageForm/NewMessageForm.vue index d1f9ff4ab16..fcb179ebc01 100644 --- a/src/components/NewMessageForm/NewMessageForm.vue +++ b/src/components/NewMessageForm/NewMessageForm.vue @@ -20,8 +20,7 @@ -->