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:
@@ -1,6 +1,6 @@
|
||||
import { ref, computed, watch, watchEffect, type ComputedRef } from "vue";
|
||||
import { useQuery } from "@vue/apollo-composable";
|
||||
import { ContactsQueryDocument } from "~~/graphql/generated";
|
||||
import { useQuery, useMutation } from "@vue/apollo-composable";
|
||||
import { ContactsQueryDocument, MarkThreadReadDocument } from "~~/graphql/generated";
|
||||
|
||||
export type Contact = {
|
||||
id: string;
|
||||
@@ -10,6 +10,7 @@ export type Contact = {
|
||||
lastContactAt: string;
|
||||
lastMessageText: string;
|
||||
lastMessageChannel: string;
|
||||
hasUnread: boolean;
|
||||
description: string;
|
||||
};
|
||||
|
||||
@@ -117,6 +118,19 @@ export function useContacts(opts: { apolloAuthReady: ComputedRef<boolean> }) {
|
||||
|
||||
const selectedContact = computed(() => contacts.value.find((item) => item.id === selectedContactId.value));
|
||||
|
||||
const { mutate: doMarkThreadRead } = useMutation(MarkThreadReadDocument);
|
||||
|
||||
function markContactRead(contactId: string) {
|
||||
if (!contactId) return;
|
||||
// Optimistically update local state
|
||||
const idx = contacts.value.findIndex((c) => c.id === contactId);
|
||||
if (idx >= 0 && contacts.value[idx]!.hasUnread) {
|
||||
contacts.value[idx] = { ...contacts.value[idx]!, hasUnread: false };
|
||||
}
|
||||
// Fire-and-forget backend call
|
||||
void doMarkThreadRead({ contactId }).catch(() => undefined);
|
||||
}
|
||||
|
||||
const brokenAvatarByContactId = ref<Record<string, boolean>>({});
|
||||
|
||||
function contactInitials(name: string) {
|
||||
@@ -159,6 +173,7 @@ export function useContacts(opts: { apolloAuthReady: ComputedRef<boolean> }) {
|
||||
avatarSrcForThread,
|
||||
markAvatarBroken,
|
||||
contactInitials,
|
||||
markContactRead,
|
||||
refetchContacts,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user