feat: remove CommunicationsQuery, load messages on-demand only

- Remove bulk CommunicationsQuery from useContacts (was loading ALL
  messages for ALL contacts on init)
- Rebuild commThreads from contacts + contactInboxes using the new
  lastMessageText field from Phase 1
- Per-contact messages now load on-demand via getClientTimeline
- Remove commItems from useWorkspaceRouting, use clientTimelineItems

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ruslan Bakiev
2026-02-24 20:02:58 +07:00
parent 601de37ab0
commit ac9c50b47d
3 changed files with 28 additions and 75 deletions

View File

@@ -95,7 +95,6 @@ const {
// ---------------------------------------------------------------------------
const {
contacts,
commItems,
contactSearch,
selectedChannel,
sortMode,
@@ -110,7 +109,6 @@ const {
markAvatarBroken,
contactInitials,
refetchContacts,
refetchCommunications,
} = useContacts({ apolloAuthReady });
// ---------------------------------------------------------------------------
@@ -301,7 +299,6 @@ const {
async function refetchAllCrmQueries() {
await Promise.all([
refetchContacts(),
refetchCommunications(),
refetchContactInboxes(),
refetchCalendar(),
refetchDeals(),
@@ -461,59 +458,36 @@ const pilotHeaderPhrases = [
const pilotHeaderText = ref("Every step moves you forward");
// ---------------------------------------------------------------------------
// Comm Threads (glue: contacts + commItems + contactInboxes)
// Comm Threads (glue: contacts + contactInboxes — no bulk message loading)
// ---------------------------------------------------------------------------
const commThreads = computed(() => {
const sorted = [...commItems.value].sort((a, b) => a.at.localeCompare(b.at));
const map = new Map<string, CommItem[]>();
for (const item of sorted) {
if (!map.has(item.contact)) map.set(item.contact, []);
map.get(item.contact)?.push(item);
}
const contactById = new Map(contacts.value.map((contact) => [contact.id, contact]));
const inboxesByContactId = new Map<string, ContactInbox[]>();
for (const inbox of contactInboxes.value) {
if (!inboxesByContactId.has(inbox.contactId)) inboxesByContactId.set(inbox.contactId, []);
inboxesByContactId.get(inbox.contactId)?.push(inbox);
inboxesByContactId.get(inbox.contactId)!.push(inbox);
}
const contactIds = new Set<string>([
...contacts.value.map((contact) => contact.id),
...contactInboxes.value.map((inbox) => inbox.contactId),
]);
return [...contactIds]
.map((contactId) => {
const contact = contactById.get(contactId);
const inboxes = inboxesByContactId.get(contactId) ?? [];
const contactName = contact?.name ?? inboxes[0]?.contactName ?? "";
const items = map.get(contactName) ?? [];
const last = items[items.length - 1];
return contacts.value
.map((c) => {
const inboxes = inboxesByContactId.get(c.id) ?? [];
const channels = [
...new Set([
...(contact?.channels ?? []),
...inboxes.map((inbox) => inbox.channel),
...items.map((item) => item.channel),
...c.channels,
...inboxes.map((i) => i.channel),
]),
] as CommItem["channel"][];
const inboxFallbackLast = inboxes
.map((inbox) => inbox.lastMessageAt || inbox.updatedAt)
.filter(Boolean)
.sort()
.at(-1);
return {
id: contactId,
contact: contactName,
avatar: contact?.avatar ?? "",
id: c.id,
contact: c.name,
avatar: c.avatar,
channels,
lastAt: last?.at ?? contact?.lastContactAt ?? inboxFallbackLast ?? "",
lastText: last?.text ?? "No messages yet",
items,
lastAt: c.lastContactAt,
lastText: c.lastMessageText || "No messages yet",
items: [] as CommItem[],
};
})
.filter((thread) => thread.contact)
.filter((t) => t.contact)
.sort((a, b) => b.lastAt.localeCompare(a.lastAt));
});
@@ -722,7 +696,7 @@ const routing = useWorkspaceRouting({
commThreads,
contacts,
deals,
commItems,
clientTimelineItems,
activeChangeMessage,
activeChangeItem,
activeChangeItems,
@@ -958,8 +932,9 @@ const selectedContactEvents = computed(() => {
const selectedContactRecentMessages = computed(() => {
if (!selectedContact.value) return [];
return commItems.value
.filter((item) => item.contact === selectedContact.value?.name && item.kind === "message")
return clientTimelineItems.value
.filter((entry) => entry.contentType === "message" && entry.message)
.map((entry) => entry.message!)
.sort((a, b) => b.at.localeCompare(a.at))
.slice(0, 8);
});