From a723c5eecdc6b35d479e9d59faf490718314e591 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Fri, 17 Apr 2026 02:29:57 +0000
Subject: [PATCH] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/app-shell.tsx | 58 +++++++++++++++++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)
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}
+
+ )}
);
})}