feat: unread message tracking with blue dot indicator

Add ContactThreadRead model to track when users last viewed each contact thread.
Contacts with messages newer than the last read time show a blue dot in the sidebar.
Opening a thread automatically marks it as read via markThreadRead mutation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ruslan Bakiev
2026-02-24 20:25:32 +07:00
parent 643d8d02ba
commit 5492e0d05c
10 changed files with 167 additions and 9 deletions

View File

@@ -108,6 +108,7 @@ const {
avatarSrcForThread,
markAvatarBroken,
contactInitials,
markContactRead,
refetchContacts,
} = useContacts({ apolloAuthReady });
@@ -484,6 +485,7 @@ const commThreads = computed(() => {
channels,
lastAt: c.lastContactAt,
lastText: c.lastMessageText || "No messages yet",
hasUnread: c.hasUnread,
items: [] as CommItem[],
};
})
@@ -660,11 +662,12 @@ const { crmRealtimeState, startCrmRealtime, stopCrmRealtime } = useCrmRealtime({
await Promise.all([refetchAllCrmQueries(), loadTelegramConnectStatus()]);
},
onNewMessage: (msg) => {
// If the message is for the currently open thread → refresh its timeline
// If the message is for the currently open thread → refresh its timeline + mark read
if (msg.contactId === selectedCommThreadId.value) {
void refreshSelectedClientTimeline(selectedCommThreadId.value);
markContactRead(msg.contactId);
}
// Refresh contacts to update sidebar preview (lastMessageText, lastAt)
// Refresh contacts to update sidebar preview (lastMessageText, lastAt, hasUnread)
void refetchContacts();
},
});
@@ -1160,6 +1163,7 @@ watch(selectedCommThreadId, () => {
clientTimelineItems.value = [];
return;
}
markContactRead(selectedCommThreadId.value);
void refreshSelectedClientTimeline(selectedCommThreadId.value).catch(() => undefined);
});