Skip to content

Commit dc03e1c

Browse files
committed
SMS pagination
1 parent c79000f commit dc03e1c

1 file changed

Lines changed: 59 additions & 11 deletions

File tree

src/views/messages/MessagesView.vue

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
<div v-if="detailLoading" class="chat-loading">
2323
<v-circular-progress indeterminate class="sm" />
2424
</div>
25-
<template v-else>
25+
<div v-if="loadingMore" class="chat-loading-more">
26+
<v-circular-progress indeterminate class="sm" />
27+
</div>
28+
<template v-if="!detailLoading">
2629
<div v-for="(item, index) in sortedItems" :key="item.id" class="chat-message-wrapper">
2730
<div v-if="showDateSeparator(index)" class="chat-date-separator">
2831
<span>{{ formatDateLabel(item.date) }}</span>
@@ -191,6 +194,11 @@ let lastMmsSendBody = ''
191194
let lastMmsSendAddress = ''
192195
const { tags, fetch: fetchTags } = useTags(dataType)
193196
197+
const PAGE_SIZE = 100
198+
const offset = ref(0)
199+
const noMoreOlder = ref(false)
200+
const loadingMore = ref(false)
201+
194202
// MMS size constants — images are auto-compressed on the server, but
195203
// video/audio cannot be compressed and will fail if too large.
196204
const MMS_WARN_SIZE = 300 * 1024 // 300 KB — conservative carrier minimum
@@ -286,31 +294,65 @@ function scrollToBottom() {
286294
}
287295
288296
function onScroll() {
289-
// placeholder for future infinite scroll
297+
if (!chatScrollRef.value || loadingMore.value || noMoreOlder.value || loading.value) return
298+
if (chatScrollRef.value.scrollTop < 200) {
299+
fetchMore()
300+
}
290301
}
291302
292-
const { loading, fetch } = initLazyQuery({
303+
const { loading, fetch: rawFetch } = initLazyQuery({
293304
handle: (data: { sms: IMessage[]; smsCount: number }, error: string) => {
294-
detailLoading.value = false
295305
if (error) {
306+
detailLoading.value = false
307+
loadingMore.value = false
296308
toast(t(error), 'error')
297309
} else if (data) {
298-
items.value = data.sms
299-
// Clear the in-memory SMS pending item once the backend confirms the new message
300-
if (pendingSmsItem.value && data.sms.length > pendingSmsPreCount) {
301-
pendingSmsItem.value = null
310+
if (loadingMore.value) {
311+
const el = chatScrollRef.value
312+
const prevScrollHeight = el?.scrollHeight ?? 0
313+
const existingIds = new Set(items.value.map((i) => i.id))
314+
const newItems = data.sms.filter((i) => !existingIds.has(i.id))
315+
items.value = [...newItems, ...items.value]
316+
if (data.sms.length < PAGE_SIZE) noMoreOlder.value = true
317+
loadingMore.value = false
318+
nextTick(() => {
319+
if (el) {
320+
el.scrollTop = el.scrollHeight - prevScrollHeight
321+
}
322+
})
323+
} else {
324+
detailLoading.value = false
325+
items.value = data.sms
326+
if (data.sms.length < PAGE_SIZE) noMoreOlder.value = true
327+
if (pendingSmsItem.value && data.sms.length > pendingSmsPreCount) {
328+
pendingSmsItem.value = null
329+
}
330+
scrollToBottom()
302331
}
303-
scrollToBottom()
304332
}
305333
},
306334
document: smsGQL,
307335
variables: () => ({
308-
offset: 0,
309-
limit: 5000,
336+
offset: offset.value,
337+
limit: PAGE_SIZE,
310338
query: buildQuery([{ name: 'thread_id', op: '', value: threadId.value }]),
311339
}),
312340
})
313341
342+
function fetch() {
343+
offset.value = 0
344+
noMoreOlder.value = false
345+
loadingMore.value = false
346+
rawFetch()
347+
}
348+
349+
function fetchMore() {
350+
if (loadingMore.value || noMoreOlder.value || loading.value) return
351+
loadingMore.value = true
352+
offset.value = items.value.length
353+
rawFetch()
354+
}
355+
314356
const { mutate: mutateCall } = initMutation({
315357
document: callGQL,
316358
})
@@ -646,6 +688,12 @@ onUnmounted(() => {
646688
flex: 1;
647689
}
648690
691+
.chat-loading-more {
692+
display: flex;
693+
justify-content: center;
694+
padding: 12px 0;
695+
}
696+
649697
.chat-date-separator {
650698
display: flex;
651699
justify-content: center;

0 commit comments

Comments
 (0)