diff --git a/meshmap_frontend/src/components/ChatPanel.vue b/meshmap_frontend/src/components/ChatPanel.vue index 1169712..4db96d3 100644 --- a/meshmap_frontend/src/components/ChatPanel.vue +++ b/meshmap_frontend/src/components/ChatPanel.vue @@ -26,6 +26,7 @@ const menuX = ref(0) const menuY = ref(0) const topThreshold = 8 const bottomThreshold = 40 +const scrollOverflowAllowance = 1 const groupedMessages = computed(() => { const groups = new Map() @@ -102,25 +103,29 @@ function handleKeydown(event: KeyboardEvent) { } } -function handleScroll() { - closeMessageMenu() - const el = panelRef.value +function loadOlderFromCurrentScroll(el: HTMLElement) { if ( - !el || props.loadingOlder || !props.hasMoreMessages || - props.messages.length === 0 || + groupedMessages.value.length === 0 || restoreScrollHeight != null ) { return } - if (el.scrollTop <= topThreshold) { - restoreScrollHeight = el.scrollHeight - restoreScrollTop = el.scrollTop - restoreMessageCount = props.messages.length - emit('load-older') + restoreScrollHeight = el.scrollHeight + restoreScrollTop = el.scrollTop + restoreMessageCount = groupedMessages.value.length + emit('load-older') +} + +function handleScroll() { + closeMessageMenu() + const el = panelRef.value + if (!el || el.scrollTop > topThreshold) { + return } + loadOlderFromCurrentScroll(el) } onBeforeUpdate(() => { @@ -138,6 +143,9 @@ onMounted(async () => { if (el) { el.scrollTop = el.scrollHeight didInitialScroll = true + if (el.scrollHeight <= el.clientHeight + scrollOverflowAllowance) { + loadOlderFromCurrentScroll(el) + } } }) @@ -153,7 +161,7 @@ onUpdated(() => { } if (restoreScrollHeight != null) { - if (props.messages.length > restoreMessageCount) { + if (groupedMessages.value.length > restoreMessageCount) { el.scrollTop = el.scrollHeight - restoreScrollHeight + restoreScrollTop clearRestoreState() return @@ -167,6 +175,10 @@ onUpdated(() => { el.scrollTop = el.scrollHeight didInitialScroll = true } + + if (el.scrollHeight <= el.clientHeight + scrollOverflowAllowance) { + loadOlderFromCurrentScroll(el) + } }) diff --git a/meshmap_frontend/src/components/NodeDetailedPage.vue b/meshmap_frontend/src/components/NodeDetailedPage.vue index 3cf9bff..579afe7 100644 --- a/meshmap_frontend/src/components/NodeDetailedPage.vue +++ b/meshmap_frontend/src/components/NodeDetailedPage.vue @@ -24,6 +24,7 @@ const chatHasMore = ref(true) const error = ref('') const chatPageSize = 20 const chatHistoryRef = ref(null) +const scrollOverflowAllowance = 1 type GroupedTextMessage = TextMessage & { mergedCount: number; mergedMessages: TextMessage[] } type PendingDeleteAction = | { kind: 'delete-message'; message: GroupedTextMessage } @@ -186,24 +187,30 @@ async function loadInitialMessages() { const el = chatHistoryRef.value if (el) { el.scrollTop = el.scrollHeight + await loadMoreUntilScrollable(el) } } async function loadOlderMessages() { + const el = chatHistoryRef.value + await loadOlderMessagesFromCurrentScroll(el) +} + +async function loadOlderMessagesFromCurrentScroll(el: HTMLElement | null) { if (chatLoadingOlder.value || !chatHasMore.value) { return } - const el = chatHistoryRef.value const previousScrollHeight = el?.scrollHeight ?? 0 const previousScrollTop = el?.scrollTop ?? 0 + const previousGroupedMessageCount = groupedMessages.value.length chatLoadingOlder.value = true try { const response = await getTextMessages(chatPageSize, messages.value.length, props.nodeId) messages.value = mergeMessages(messages.value, toChronological(response.items)) chatHasMore.value = response.items.length === chatPageSize await nextTick() - if (el) { + if (el && groupedMessages.value.length > previousGroupedMessageCount) { el.scrollTop = el.scrollHeight - previousScrollHeight + previousScrollTop } } catch (err) { @@ -213,6 +220,16 @@ async function loadOlderMessages() { } } +async function loadMoreUntilScrollable(el: HTMLElement) { + while (chatHasMore.value && el.scrollHeight <= el.clientHeight + scrollOverflowAllowance) { + const previousGroupedMessageCount = groupedMessages.value.length + await loadOlderMessagesFromCurrentScroll(el) + if (groupedMessages.value.length <= previousGroupedMessageCount) { + break + } + } +} + function closeMessageMenu() { menuMessage.value = null }