fix: show loading spinner when switching between contact threads

Clear old timeline items immediately on thread switch and display a centered
loader until the new conversation loads, instead of showing stale messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ruslan Bakiev
2026-02-24 21:45:01 +07:00
parent 3ff9120070
commit cb685446a5
2 changed files with 25 additions and 2 deletions

View File

@@ -259,6 +259,7 @@ const {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const { const {
clientTimelineItems, clientTimelineItems,
timelineLoading,
timelineContactId, timelineContactId,
timelineLimit, timelineLimit,
loadClientTimeline, loadClientTimeline,
@@ -1624,6 +1625,12 @@ onBeforeUnmount(() => {
<div v-else-if="selectedCommThread" class="relative flex h-full min-h-0 flex-col"> <div v-else-if="selectedCommThread" class="relative flex h-full min-h-0 flex-col">
<div class="comm-thread-surface min-h-0 flex-1 space-y-2 overflow-y-auto px-3 pb-2"> <div class="comm-thread-surface min-h-0 flex-1 space-y-2 overflow-y-auto px-3 pb-2">
<!-- Loading spinner while timeline is fetching -->
<div v-if="timelineLoading && clientTimelineItems.length === 0" class="flex h-full items-center justify-center">
<span class="loading loading-spinner loading-md text-primary" />
</div>
<template v-else>
<button <button
class="sticky top-0 z-10 -mx-3 mb-2 flex w-[calc(100%+1.5rem)] items-center gap-2 border-b border-base-300 bg-base-100/80 px-3 py-2 text-left backdrop-blur-sm transition hover:bg-base-100" class="sticky top-0 z-10 -mx-3 mb-2 flex w-[calc(100%+1.5rem)] items-center gap-2 border-b border-base-300 bg-base-100/80 px-3 py-2 text-left backdrop-blur-sm transition hover:bg-base-100"
@click="commPinnedOnly = !commPinnedOnly" @click="commPinnedOnly = !commPinnedOnly"
@@ -1900,6 +1907,7 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
</div> </div>
</template>
</div> </div>
<div <div

View File

@@ -30,9 +30,13 @@ export function useTimeline(opts: { apolloAuthReady: ComputedRef<boolean> }) {
); );
const clientTimelineItems = ref<ClientTimelineItem[]>([]); const clientTimelineItems = ref<ClientTimelineItem[]>([]);
const timelineLoading = ref(false);
watch(() => timelineResult.value?.getClientTimeline, (v) => { watch(() => timelineResult.value?.getClientTimeline, (v) => {
if (v) clientTimelineItems.value = v as ClientTimelineItem[]; if (v) {
clientTimelineItems.value = v as ClientTimelineItem[];
timelineLoading.value = false;
}
}, { immediate: true }); }, { immediate: true });
async function loadClientTimeline(contactId: string, limit = 500) { async function loadClientTimeline(contactId: string, limit = 500) {
@@ -40,18 +44,28 @@ export function useTimeline(opts: { apolloAuthReady: ComputedRef<boolean> }) {
if (!normalizedContactId) { if (!normalizedContactId) {
clientTimelineItems.value = []; clientTimelineItems.value = [];
timelineContactId.value = ""; timelineContactId.value = "";
timelineLoading.value = false;
return; return;
} }
// Clear old data immediately and show loader
clientTimelineItems.value = [];
timelineLoading.value = true;
timelineContactId.value = normalizedContactId; timelineContactId.value = normalizedContactId;
timelineLimit.value = limit; timelineLimit.value = limit;
await refetchTimeline(); try {
await refetchTimeline();
} finally {
timelineLoading.value = false;
}
} }
async function refreshSelectedClientTimeline(selectedCommThreadId: string) { async function refreshSelectedClientTimeline(selectedCommThreadId: string) {
const contactId = String(selectedCommThreadId ?? "").trim(); const contactId = String(selectedCommThreadId ?? "").trim();
if (!contactId) { if (!contactId) {
clientTimelineItems.value = []; clientTimelineItems.value = [];
timelineLoading.value = false;
return; return;
} }
await loadClientTimeline(contactId); await loadClientTimeline(contactId);
@@ -59,6 +73,7 @@ export function useTimeline(opts: { apolloAuthReady: ComputedRef<boolean> }) {
return { return {
clientTimelineItems, clientTimelineItems,
timelineLoading,
timelineContactId, timelineContactId,
timelineLimit, timelineLimit,
loadClientTimeline, loadClientTimeline,