Browse Source

Merge pull request #11872 from nextcloud/chore/noid/refactor-chat-scrolling

chore: refactor scroll to bottom logic
pull/11901/head
Dorra 2 years ago
committed by GitHub
parent
commit
35beec65c8
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      src/components/ChatView.vue
  2. 7
      src/components/MessagesList/MessagesGroup/Message/Message.vue
  3. 2
      src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue
  4. 103
      src/components/MessagesList/MessagesList.vue
  5. 2
      src/components/NewMessage/NewMessage.vue
  6. 2
      src/store/fileUploadStore.js

2
src/components/ChatView.vue

@ -224,7 +224,7 @@ export default {
},
smoothScrollToBottom() {
EventBus.$emit('smooth-scroll-chat-to-bottom')
EventBus.$emit('scroll-chat-to-bottom', { smooth: true, force: true })
},
},

7
src/components/MessagesList/MessagesGroup/Message/Message.vue

@ -450,13 +450,6 @@ export default {
},
},
watch: {
// Scroll list to the bottom if reaction to the message was added, as it expands the list
reactions() {
EventBus.$emit('scroll-chat-to-bottom-if-sticky')
},
},
methods: {
lastReadMessageVisibilityChanged(isVisible) {
if (isVisible) {

2
src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue

@ -365,7 +365,7 @@ export default {
watch: {
showJoinCallButton() {
EventBus.$emit('scroll-chat-to-bottom')
EventBus.$emit('scroll-chat-to-bottom', { smooth: true })
},
},

103
src/components/MessagesList/MessagesList.vue

@ -279,7 +279,6 @@ export default {
if (oldValue) {
this.$store.dispatch('cancelLookForNewMessages', { requestId: oldValue })
}
this.$emit('update:is-chat-scrolled-to-bottom', true)
this.handleStartGettingMessagesPreconditions()
// Remove expired messages when joining a room
@ -308,6 +307,9 @@ export default {
} else {
this.softUpdateByDateGroups(this.messagesGroupedByDateByAuthor, newGroups)
}
// scroll to bottom if needed
this.scrollToBottom({ smooth: true })
},
},
},
@ -316,10 +318,7 @@ export default {
this.debounceUpdateReadMarkerPosition = debounce(this.updateReadMarkerPosition, 1000)
this.debounceHandleScroll = debounce(this.handleScroll, 50)
this.scrollToBottom()
EventBus.$on('scroll-chat-to-bottom', this.handleScrollChatToBottomEvent)
EventBus.$on('smooth-scroll-chat-to-bottom', this.smoothScrollToBottom)
EventBus.$on('scroll-chat-to-bottom-if-sticky', this.scrollToBottomIfSticky)
EventBus.$on('scroll-chat-to-bottom', this.scrollToBottom)
EventBus.$on('focus-message', this.focusMessage)
EventBus.$on('route-change', this.onRouteChange)
subscribe('networkOffline', this.handleNetworkOffline)
@ -339,9 +338,7 @@ export default {
this.debounceHandleScroll.clear?.()
window.removeEventListener('focus', this.onWindowFocus)
EventBus.$off('scroll-chat-to-bottom', this.handleScrollChatToBottomEvent)
EventBus.$off('smooth-scroll-chat-to-bottom', this.smoothScrollToBottom)
EventBus.$on('scroll-chat-to-bottom-if-sticky', this.scrollToBottomIfSticky)
EventBus.$off('scroll-chat-to-bottom', this.scrollToBottom)
EventBus.$off('focus-message', this.focusMessage)
EventBus.$off('route-change', this.onRouteChange)
@ -638,7 +635,7 @@ export default {
if (!isFocused) {
// if no anchor was present or the message to focus on did not exist,
// scroll to bottom
this.scrollToBottom()
this.scrollToBottom({ force: true })
}
// if no scrollbars, clear read marker directly as scrolling is not possible for the user to clear it
@ -752,10 +749,7 @@ export default {
return
}
const followInNewMessages = this.conversation.lastMessage
&& this.conversation.lastReadMessage === this.conversation.lastMessage.id
await this.getNewMessages(followInNewMessages)
await this.getNewMessages()
},
async getMessageContext(messageId) {
@ -815,13 +809,11 @@ export default {
/**
* Creates a long polling request for a new message.
*
* @param {boolean} scrollToBottom Whether we should try to automatically scroll to the bottom
*/
async getNewMessages(scrollToBottom = true) {
async getNewMessages() {
if (this.destroying) {
return
}
// Make the request
try {
// TODO: move polling logic to the store and also cancel timers on cancel
@ -831,11 +823,6 @@ export default {
lastKnownMessageId: this.$store.getters.getLastKnownMessageId(this.token),
requestId: this.chatIdentifier,
})
// Scroll to the last message if sticky
if (scrollToBottom && this.isSticky) {
this.smoothScrollToBottom()
}
} catch (exception) {
if (Axios.isCancel(exception)) {
console.debug('The request has been canceled', exception)
@ -1106,66 +1093,42 @@ export default {
},
/**
* @param {object} options Event options
* @param {boolean} options.force Set to true, if the chat should be scrolled to the bottom even when it was not before
*/
handleScrollChatToBottomEvent(options) {
if ((options && options.force) || this.isChatScrolledToBottom) {
this.scrollToBottom()
}
},
/**
* Scrolls to the bottom of the list (to show reaction to the last message).
*/
scrollToBottomIfSticky() {
if (this.isSticky) {
this.scrollToBottom()
}
},
/**
* Scrolls to the bottom of the list smoothly.
* Scrolls to the bottom of the list.
* @param {object} options Options for scrolling
* @param {boolean} [options.smooth] 'smooth' scrolling to the bottom ('auto' by default)
* @param {boolean} [options.force] force scrolling to the bottom (otherwise check for current position)
*/
smoothScrollToBottom() {
scrollToBottom(options = {}) {
this.$nextTick(() => {
if (!this.$refs.scroller) {
return
}
if (this.isWindowVisible && (document.hasFocus() || this.isInCall)) {
// scrollTo is used when the user is watching
this.$refs.scroller.scrollTo({
top: this.$refs.scroller.scrollHeight,
behavior: 'smooth',
})
let newTop
if (options?.force) {
newTop = this.$refs.scroller.scrollHeight
this.setChatScrolledToBottom(true)
} else {
// Otherwise we jump half a message and stop autoscrolling, so the user can read up
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.$refs.scroller.scrollTop += 10
} else {
// Single new line from the new author is 75px so scroll half an avatar
this.$refs.scroller.scrollTop += 40
}
this.setChatScrolledToBottom(false)
}
})
},
/**
* Scrolls to the bottom of the list.
*/
scrollToBottom() {
this.$nextTick(() => {
if (!this.$refs.scroller) {
} else if (!this.isSticky) {
// Reading old messages
return
} else if (!this.isWindowVisible) {
const firstUnreadMessageHeight = this.$refs.scroller.scrollHeight - this.$refs.scroller.scrollTop - this.$refs.scroller.offsetHeight
const scrollBy = firstUnreadMessageHeight < 40 ? 10 : 40
// We jump half a message and stop autoscrolling, so the user can read up
// Single new line from the previous author is 35px so scroll half a line (10px)
// Single new line from the new author is 75px so scroll half an avatar (40px)
newTop = this.$refs.scroller.scrollTop + scrollBy
this.setChatScrolledToBottom(false)
} else {
newTop = this.$refs.scroller.scrollHeight
this.setChatScrolledToBottom(true)
}
this.$refs.scroller.scrollTop = this.$refs.scroller.scrollHeight
this.setChatScrolledToBottom(true)
this.$refs.scroller.scrollTo({
top: newTop,
behavior: options?.smooth ? 'smooth' : 'auto',
})
})
},
/**

2
src/components/NewMessage/NewMessage.vue

@ -695,7 +695,7 @@ export default {
this.text = ''
this.userData = {}
// Scrolls the message list to the last added message
EventBus.$emit('smooth-scroll-chat-to-bottom')
EventBus.$emit('scroll-chat-to-bottom', { smooth: true, force: true })
// Also remove the message to be replied for this conversation
this.chatExtrasStore.removeParentIdToReply(this.token)

2
src/store/fileUploadStore.js

@ -323,7 +323,7 @@ const actions = {
// Add temporary messages (files) to the messages list
dispatch('addTemporaryMessage', { token, message })
// Scroll the message list
EventBus.$emit('scroll-chat-to-bottom', { force: true })
EventBus.$emit('scroll-chat-to-bottom', { smooth: true, force: true })
}
await dispatch('prepareUploadPaths', { token, uploadId })

Loading…
Cancel
Save