Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
import { useEffect, useState, useCallback } from "react";
|
|
import { supabase } from "@/integrations/supabase/client";
|
|
import { useAuth } from "@/lib/auth";
|
|
|
|
interface Counts {
|
|
messages: number;
|
|
tasks: number;
|
|
notifications: number;
|
|
}
|
|
|
|
export function useBubbleCounts(): Counts {
|
|
const { user } = useAuth();
|
|
const [counts, setCounts] = useState<Counts>({ messages: 0, tasks: 0, notifications: 0 });
|
|
|
|
const load = useCallback(async () => {
|
|
if (!user) {
|
|
setCounts({ messages: 0, tasks: 0, notifications: 0 });
|
|
return;
|
|
}
|
|
const [memberRes, msgRes, taskRes, notifRes] = await Promise.all([
|
|
supabase.from("conversation_members").select("conversation_id, last_read_at").eq("user_id", user.id),
|
|
// unread messages: query per-convo handled below
|
|
Promise.resolve(null),
|
|
supabase
|
|
.from("task_assignees")
|
|
.select("task_id, tasks!inner(status, due_date)")
|
|
.eq("user_id", user.id)
|
|
.eq("tasks.status", "incomplete"),
|
|
supabase
|
|
.from("notifications")
|
|
.select("id", { count: "exact", head: true })
|
|
.eq("user_id", user.id)
|
|
.is("read_at", null),
|
|
]);
|
|
|
|
let unreadMessages = 0;
|
|
const memberRows = memberRes.data ?? [];
|
|
if (memberRows.length) {
|
|
// For each conversation, count messages newer than last_read_at and not from self
|
|
const results = await Promise.all(
|
|
memberRows.map(async (m) => {
|
|
const { count } = await supabase
|
|
.from("messages")
|
|
.select("id", { count: "exact", head: true })
|
|
.eq("conversation_id", m.conversation_id)
|
|
.gt("created_at", m.last_read_at)
|
|
.neq("sender_id", user.id);
|
|
return count ?? 0;
|
|
}),
|
|
);
|
|
unreadMessages = results.reduce((a, b) => a + b, 0);
|
|
}
|
|
|
|
const taskCount = (taskRes.data ?? []).length;
|
|
setCounts({
|
|
messages: unreadMessages,
|
|
tasks: taskCount,
|
|
notifications: notifRes.count ?? 0,
|
|
});
|
|
}, [user]);
|
|
|
|
useEffect(() => {
|
|
if (!user) return;
|
|
load();
|
|
const ch = supabase
|
|
.channel(`bubbles-${user.id}-${Math.random().toString(36).slice(2)}`)
|
|
.on("postgres_changes", { event: "*", schema: "public", table: "messages" }, () => load())
|
|
.on("postgres_changes", { event: "*", schema: "public", table: "task_assignees" }, () => load())
|
|
.on("postgres_changes", { event: "*", schema: "public", table: "tasks" }, () => load())
|
|
.on("postgres_changes", { event: "*", schema: "public", table: "notifications", filter: `user_id=eq.${user.id}` }, () => load())
|
|
.on("postgres_changes", { event: "*", schema: "public", table: "conversation_members", filter: `user_id=eq.${user.id}` }, () => load())
|
|
.subscribe();
|
|
return () => {
|
|
supabase.removeChannel(ch);
|
|
};
|
|
}, [user, load]);
|
|
|
|
return counts;
|
|
}
|