107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
import type { SupportConversation } from "~/types";
|
|
|
|
const toConversationMap = (items: SupportConversation[]) =>
|
|
Object.fromEntries(items.map((item) => [item.id, item])) as Record<string, SupportConversation>;
|
|
|
|
export function useSupportUnread() {
|
|
const { user, token } = useAuth();
|
|
const { connect, onConversationUpdated, onMessageCreated } = useSupportRealtime();
|
|
|
|
const conversations = useState<Record<string, SupportConversation>>("support-unread-conversations", () => ({}));
|
|
const unreadCount = useState<number>("support-unread-count", () => 0);
|
|
const initialized = useState<boolean>("support-unread-initialized", () => false);
|
|
const loading = useState<boolean>("support-unread-loading", () => false);
|
|
const offConversationUpdated = useState<(() => void) | null>("support-unread-off-conversation", () => null);
|
|
const offMessageCreated = useState<(() => void) | null>("support-unread-off-message", () => null);
|
|
|
|
const recomputeUnreadCount = () => {
|
|
const items = Object.values(conversations.value);
|
|
|
|
unreadCount.value = user.value?.role === "admin"
|
|
? items.filter((item) => item.unreadForAdmin).length
|
|
: items.some((item) => item.unreadForUser) ? 1 : 0;
|
|
};
|
|
|
|
const replaceConversations = (items: SupportConversation[]) => {
|
|
conversations.value = toConversationMap(items);
|
|
recomputeUnreadCount();
|
|
};
|
|
|
|
const upsertConversation = (item: SupportConversation) => {
|
|
conversations.value = {
|
|
...conversations.value,
|
|
[item.id]: item
|
|
};
|
|
recomputeUnreadCount();
|
|
};
|
|
|
|
const clearState = () => {
|
|
conversations.value = {};
|
|
unreadCount.value = 0;
|
|
initialized.value = false;
|
|
offConversationUpdated.value?.();
|
|
offConversationUpdated.value = null;
|
|
offMessageCreated.value?.();
|
|
offMessageCreated.value = null;
|
|
};
|
|
|
|
const refreshUnread = async () => {
|
|
if (!user.value || !token.value || loading.value) {
|
|
return;
|
|
}
|
|
|
|
loading.value = true;
|
|
|
|
try {
|
|
if (user.value.role === "admin") {
|
|
const response = await useChatApi<SupportConversation[]>("/admin/support/conversations");
|
|
replaceConversations(response);
|
|
} else {
|
|
const response = await useChatApi<SupportConversation>("/support/conversation");
|
|
replaceConversations([response]);
|
|
}
|
|
} catch {
|
|
// Ignore unread refresh failures in shell navigation.
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
const ensureRealtime = () => {
|
|
if (!process.client || !token.value || offConversationUpdated.value || offMessageCreated.value) {
|
|
return;
|
|
}
|
|
|
|
connect();
|
|
offConversationUpdated.value = onConversationUpdated(({ conversation }) => {
|
|
if (conversation) {
|
|
upsertConversation(conversation);
|
|
}
|
|
});
|
|
offMessageCreated.value = onMessageCreated(({ conversation }) => {
|
|
if (conversation) {
|
|
upsertConversation(conversation);
|
|
}
|
|
});
|
|
};
|
|
|
|
const initializeUnread = async () => {
|
|
if (initialized.value || !user.value || !token.value) {
|
|
return;
|
|
}
|
|
|
|
await refreshUnread();
|
|
ensureRealtime();
|
|
initialized.value = true;
|
|
};
|
|
|
|
return {
|
|
unreadCount,
|
|
initializeUnread,
|
|
refreshUnread,
|
|
replaceConversations,
|
|
upsertConversation,
|
|
clearState
|
|
};
|
|
}
|