diff --git a/src/components/app-shell.tsx b/src/components/app-shell.tsx index d72569f..349da7f 100644 --- a/src/components/app-shell.tsx +++ b/src/components/app-shell.tsx @@ -46,10 +46,60 @@ const NAV: NavItem[] = [ { to: "/settings", label: "Settings", icon: Settings, adminOnly: true }, ]; +function useUnreadMessagesCount() { + const { user } = useAuth(); + const uid = user?.id; + const [count, setCount] = useState(0); + + const refresh = useCallback(async () => { + if (!uid) { + setCount(0); + return; + } + const { data: mems } = await supabase + .from("conversation_members") + .select("conversation_id, last_read_at") + .eq("user_id", uid); + if (!mems || mems.length === 0) { + setCount(0); + return; + } + let total = 0; + await Promise.all( + mems.map(async (m) => { + const { count: c } = 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", uid); + total += c ?? 0; + }), + ); + setCount(total); + }, [uid]); + + useEffect(() => { + refresh(); + if (!uid) return; + const ch = supabase + .channel("sidebar-unread") + .on("postgres_changes", { event: "*", schema: "public", table: "messages" }, () => refresh()) + .on("postgres_changes", { event: "UPDATE", schema: "public", table: "conversation_members", filter: `user_id=eq.${uid}` }, () => refresh()) + .subscribe(); + return () => { + supabase.removeChannel(ch); + }; + }, [uid, refresh]); + + return count; +} + export function AppShell({ children }: { children: ReactNode }) { const { user, signOut, isAdmin, roles } = useAuth(); const location = useLocation(); const navigate = useNavigate(); + const unreadMessages = useUnreadMessagesCount(); const handleSignOut = async () => { await signOut(); @@ -79,6 +129,7 @@ export function AppShell({ children }: { children: ReactNode }) { ? location.pathname === "/" : location.pathname.startsWith(item.to); const Icon = item.icon; + const showBadge = item.to === "/messages" && unreadMessages > 0; return ( - {item.label} + {item.label} + {showBadge && ( + + {unreadMessages} + + )} ); })}