'use client'; import { useState } from 'react'; import { Bell } from 'lucide-react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { apiFetch } from '@/lib/api/client'; import { useNotifications } from '@/hooks/use-notifications'; import { NotificationItem } from './notification-item'; interface NotificationListResponse { data: Array<{ id: string; type: string; title: string; description: string | null; link: string | null; isRead: boolean; createdAt: Date; }>; total: number; } export function NotificationBell() { const { unreadCount } = useNotifications(); const queryClient = useQueryClient(); // Track popover open state so we only fire the list fetch when the user // actually opens the bell. Without this, an instance of NotificationBell // mounted alongside would populate the same ['notifications', // 'list'] cache key without the gating Inbox carefully applies, defeating // Inbox's enabled-on-open optimization. const [open, setOpen] = useState(false); const { data, isLoading } = useQuery({ queryKey: ['notifications', 'list'], queryFn: () => apiFetch('/api/v1/notifications?limit=20'), staleTime: 30_000, enabled: open, }); const markReadMutation = useMutation({ mutationFn: (notificationId: string) => apiFetch(`/api/v1/notifications/${notificationId}`, { method: 'PATCH' }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['notifications'] }); }, }); const markAllReadMutation = useMutation({ mutationFn: () => apiFetch('/api/v1/notifications/read-all', { method: 'POST' }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['notifications'] }); }, }); const notifications = data?.data ?? []; return ( {unreadCount > 0 && ( {unreadCount > 99 ? '99+' : unreadCount} )} {/* Header */} Notifications {unreadCount > 0 && ( markAllReadMutation.mutate()} disabled={markAllReadMutation.isPending} > Mark all read )} {/* Notification list */} {isLoading ? ( Loading... ) : notifications.length === 0 ? ( No notifications ) : ( {notifications.map((notification) => ( markReadMutation.mutate(id)} /> ))} )} ); }