Skip to content

Commit 35beec6

Browse files
authored
Merge pull request #11872 from nextcloud/chore/noid/refactor-chat-scrolling
chore: refactor scroll to bottom logic
2 parents 5a28d95 + 7cbc5d3 commit 35beec6

File tree

6 files changed

+37
-81
lines changed

6 files changed

+37
-81
lines changed

src/components/ChatView.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ export default {
224224
},
225225
226226
smoothScrollToBottom() {
227-
EventBus.$emit('smooth-scroll-chat-to-bottom')
227+
EventBus.$emit('scroll-chat-to-bottom', { smooth: true, force: true })
228228
},
229229
},
230230

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -450,13 +450,6 @@ export default {
450450
},
451451
},
452452
453-
watch: {
454-
// Scroll list to the bottom if reaction to the message was added, as it expands the list
455-
reactions() {
456-
EventBus.$emit('scroll-chat-to-bottom-if-sticky')
457-
},
458-
},
459-
460453
methods: {
461454
lastReadMessageVisibilityChanged(isVisible) {
462455
if (isVisible) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ export default {
365365
366366
watch: {
367367
showJoinCallButton() {
368-
EventBus.$emit('scroll-chat-to-bottom')
368+
EventBus.$emit('scroll-chat-to-bottom', { smooth: true })
369369
},
370370
},
371371

src/components/MessagesList/MessagesList.vue

Lines changed: 33 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,6 @@ export default {
279279
if (oldValue) {
280280
this.$store.dispatch('cancelLookForNewMessages', { requestId: oldValue })
281281
}
282-
this.$emit('update:is-chat-scrolled-to-bottom', true)
283282
this.handleStartGettingMessagesPreconditions()
284283
285284
// Remove expired messages when joining a room
@@ -308,6 +307,9 @@ export default {
308307
} else {
309308
this.softUpdateByDateGroups(this.messagesGroupedByDateByAuthor, newGroups)
310309
}
310+
311+
// scroll to bottom if needed
312+
this.scrollToBottom({ smooth: true })
311313
},
312314
},
313315
},
@@ -316,10 +318,7 @@ export default {
316318
this.debounceUpdateReadMarkerPosition = debounce(this.updateReadMarkerPosition, 1000)
317319
this.debounceHandleScroll = debounce(this.handleScroll, 50)
318320
319-
this.scrollToBottom()
320-
EventBus.$on('scroll-chat-to-bottom', this.handleScrollChatToBottomEvent)
321-
EventBus.$on('smooth-scroll-chat-to-bottom', this.smoothScrollToBottom)
322-
EventBus.$on('scroll-chat-to-bottom-if-sticky', this.scrollToBottomIfSticky)
321+
EventBus.$on('scroll-chat-to-bottom', this.scrollToBottom)
323322
EventBus.$on('focus-message', this.focusMessage)
324323
EventBus.$on('route-change', this.onRouteChange)
325324
subscribe('networkOffline', this.handleNetworkOffline)
@@ -339,9 +338,7 @@ export default {
339338
this.debounceHandleScroll.clear?.()
340339
341340
window.removeEventListener('focus', this.onWindowFocus)
342-
EventBus.$off('scroll-chat-to-bottom', this.handleScrollChatToBottomEvent)
343-
EventBus.$off('smooth-scroll-chat-to-bottom', this.smoothScrollToBottom)
344-
EventBus.$on('scroll-chat-to-bottom-if-sticky', this.scrollToBottomIfSticky)
341+
EventBus.$off('scroll-chat-to-bottom', this.scrollToBottom)
345342
EventBus.$off('focus-message', this.focusMessage)
346343
EventBus.$off('route-change', this.onRouteChange)
347344
@@ -638,7 +635,7 @@ export default {
638635
if (!isFocused) {
639636
// if no anchor was present or the message to focus on did not exist,
640637
// scroll to bottom
641-
this.scrollToBottom()
638+
this.scrollToBottom({ force: true })
642639
}
643640
644641
// if no scrollbars, clear read marker directly as scrolling is not possible for the user to clear it
@@ -752,10 +749,7 @@ export default {
752749
return
753750
}
754751
755-
const followInNewMessages = this.conversation.lastMessage
756-
&& this.conversation.lastReadMessage === this.conversation.lastMessage.id
757-
758-
await this.getNewMessages(followInNewMessages)
752+
await this.getNewMessages()
759753
},
760754
761755
async getMessageContext(messageId) {
@@ -815,13 +809,11 @@ export default {
815809
/**
816810
* Creates a long polling request for a new message.
817811
*
818-
* @param {boolean} scrollToBottom Whether we should try to automatically scroll to the bottom
819812
*/
820-
async getNewMessages(scrollToBottom = true) {
813+
async getNewMessages() {
821814
if (this.destroying) {
822815
return
823816
}
824-
825817
// Make the request
826818
try {
827819
// TODO: move polling logic to the store and also cancel timers on cancel
@@ -831,11 +823,6 @@ export default {
831823
lastKnownMessageId: this.$store.getters.getLastKnownMessageId(this.token),
832824
requestId: this.chatIdentifier,
833825
})
834-
835-
// Scroll to the last message if sticky
836-
if (scrollToBottom && this.isSticky) {
837-
this.smoothScrollToBottom()
838-
}
839826
} catch (exception) {
840827
if (Axios.isCancel(exception)) {
841828
console.debug('The request has been canceled', exception)
@@ -1106,66 +1093,42 @@ export default {
11061093
},
11071094
11081095
/**
1109-
* @param {object} options Event options
1110-
* @param {boolean} options.force Set to true, if the chat should be scrolled to the bottom even when it was not before
1111-
*/
1112-
handleScrollChatToBottomEvent(options) {
1113-
if ((options && options.force) || this.isChatScrolledToBottom) {
1114-
this.scrollToBottom()
1115-
}
1116-
},
1117-
1118-
/**
1119-
* Scrolls to the bottom of the list (to show reaction to the last message).
1120-
*/
1121-
scrollToBottomIfSticky() {
1122-
if (this.isSticky) {
1123-
this.scrollToBottom()
1124-
}
1125-
},
1126-
1127-
/**
1128-
* Scrolls to the bottom of the list smoothly.
1096+
* Scrolls to the bottom of the list.
1097+
* @param {object} options Options for scrolling
1098+
* @param {boolean} [options.smooth] 'smooth' scrolling to the bottom ('auto' by default)
1099+
* @param {boolean} [options.force] force scrolling to the bottom (otherwise check for current position)
11291100
*/
1130-
smoothScrollToBottom() {
1101+
scrollToBottom(options = {}) {
11311102
this.$nextTick(() => {
11321103
if (!this.$refs.scroller) {
11331104
return
11341105
}
11351106
1136-
if (this.isWindowVisible && (document.hasFocus() || this.isInCall)) {
1137-
// scrollTo is used when the user is watching
1138-
this.$refs.scroller.scrollTo({
1139-
top: this.$refs.scroller.scrollHeight,
1140-
behavior: 'smooth',
1141-
})
1107+
let newTop
1108+
if (options?.force) {
1109+
newTop = this.$refs.scroller.scrollHeight
11421110
this.setChatScrolledToBottom(true)
1143-
} else {
1144-
// Otherwise we jump half a message and stop autoscrolling, so the user can read up
1145-
if (this.$refs.scroller.scrollHeight - this.$refs.scroller.scrollTop - this.$refs.scroller.offsetHeight < 40) {
1146-
// Single new line from the previous author is 35px so scroll half a line
1147-
this.$refs.scroller.scrollTop += 10
1148-
} else {
1149-
// Single new line from the new author is 75px so scroll half an avatar
1150-
this.$refs.scroller.scrollTop += 40
1151-
}
1152-
this.setChatScrolledToBottom(false)
1153-
}
1154-
})
1155-
},
1156-
/**
1157-
* Scrolls to the bottom of the list.
1158-
*/
1159-
scrollToBottom() {
1160-
this.$nextTick(() => {
1161-
if (!this.$refs.scroller) {
1111+
} else if (!this.isSticky) {
1112+
// Reading old messages
11621113
return
1114+
} else if (!this.isWindowVisible) {
1115+
const firstUnreadMessageHeight = this.$refs.scroller.scrollHeight - this.$refs.scroller.scrollTop - this.$refs.scroller.offsetHeight
1116+
const scrollBy = firstUnreadMessageHeight < 40 ? 10 : 40
1117+
// We jump half a message and stop autoscrolling, so the user can read up
1118+
// Single new line from the previous author is 35px so scroll half a line (10px)
1119+
// Single new line from the new author is 75px so scroll half an avatar (40px)
1120+
newTop = this.$refs.scroller.scrollTop + scrollBy
1121+
this.setChatScrolledToBottom(false)
1122+
} else {
1123+
newTop = this.$refs.scroller.scrollHeight
1124+
this.setChatScrolledToBottom(true)
11631125
}
11641126
1165-
this.$refs.scroller.scrollTop = this.$refs.scroller.scrollHeight
1166-
this.setChatScrolledToBottom(true)
1127+
this.$refs.scroller.scrollTo({
1128+
top: newTop,
1129+
behavior: options?.smooth ? 'smooth' : 'auto',
1130+
})
11671131
})
1168-
11691132
},
11701133
11711134
/**

src/components/NewMessage/NewMessage.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,7 @@ export default {
695695
this.text = ''
696696
this.userData = {}
697697
// Scrolls the message list to the last added message
698-
EventBus.$emit('smooth-scroll-chat-to-bottom')
698+
EventBus.$emit('scroll-chat-to-bottom', { smooth: true, force: true })
699699
// Also remove the message to be replied for this conversation
700700
this.chatExtrasStore.removeParentIdToReply(this.token)
701701

src/store/fileUploadStore.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ const actions = {
323323
// Add temporary messages (files) to the messages list
324324
dispatch('addTemporaryMessage', { token, message })
325325
// Scroll the message list
326-
EventBus.$emit('scroll-chat-to-bottom', { force: true })
326+
EventBus.$emit('scroll-chat-to-bottom', { smooth: true, force: true })
327327
}
328328

329329
await dispatch('prepareUploadPaths', { token, uploadId })

0 commit comments

Comments
 (0)