import { ArchiveIcon, InboxIcon } from 'lucide-react' import { useMemo, useRef, useState } from 'react' import { toast } from 'sonner' import AlertError from 'components/ui/AlertError' import InfiniteList from 'components/ui/InfiniteList' import ShimmeringLoader, { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useNotificationsArchiveAllMutation } from 'data/notifications/notifications-v2-archive-all-mutation' import { useNotificationsV2Query } from 'data/notifications/notifications-v2-query' import { useNotificationsSummaryQuery } from 'data/notifications/notifications-v2-summary-query' import { useNotificationsV2UpdateMutation } from 'data/notifications/notifications-v2-update-mutation' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProjectsQuery } from 'data/projects/projects-query' import { useNotificationsStateSnapshot } from 'state/notifications' import { Button, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_, Tabs_Shadcn_, cn, } from 'ui' import NotificationRow from './NotificationRow' import { NotificationsFilter } from './NotificationsFilter' import { ButtonTooltip } from 'components/ui/ButtonTooltip' export const NotificationsPopoverV2 = () => { const [open, setOpen] = useState(false) const [activeTab, setActiveTab] = useState<'inbox' | 'archived'>('inbox') const snap = useNotificationsStateSnapshot() // Storing in ref as no re-rendering required const markedRead = useRef([]) // [Joshen] Just FYI this variable row heights logic should ideally live in InfiniteList // but I ran into some infinite loops issues when I was trying to implement it there // so opting to simplify and implement it here for now const rowHeights = useRef<{ [key: number]: number }>({}) const { data: projects } = useProjectsQuery({ enabled: open }) const { data: organizations } = useOrganizationsQuery({ enabled: open }) const { data, error, isLoading, isError, isSuccess, hasNextPage, isFetchingNextPage, fetchNextPage, } = useNotificationsV2Query( { status: activeTab === 'archived' ? 'archived' : snap.filterStatuses.includes('unread') ? 'new' : undefined, filters: { priority: snap.filterPriorities, organizations: snap.filterOrganizations, projects: snap.filterProjects, }, }, { enabled: open } ) const { data: summary } = useNotificationsSummaryQuery() const { mutate: updateNotifications } = useNotificationsV2UpdateMutation() const { mutate: archiveAllNotifications, isLoading: isArchiving } = useNotificationsArchiveAllMutation({ onSuccess: () => toast.success('Successfully archived all notifications'), }) const notifications = useMemo(() => data?.pages.flatMap((page) => page) ?? [], [data?.pages]) const hasNewNotifications = summary?.unread_count ?? 0 > 0 const hasWarning = summary?.has_warning const hasCritical = summary?.has_critical const markNotificationsRead = () => { if (markedRead.current.length > 0) { updateNotifications({ ids: markedRead.current, status: 'seen' }) } } return ( { setOpen(open) if (!open) markNotificationsRead() }} > {hasCritical && (
)} {hasWarning && !hasCritical && (
)} {!!hasNewNotifications && !hasCritical && !hasWarning && (
)}
} />

Notifications

{ setActiveTab(tab as 'inbox' | 'archived') if (tab === 'archived' && snap.filterStatuses.includes('unread')) { snap.setFilters('unread', 'status') } }} value={activeTab} >
Inbox
9 ? 'px-0.5 w-auto' : 'w-4' )} > {summary?.unread_count}
Archived
{isLoading && (
)} {isError && (
)} {isSuccess && (
{notifications.length > 0 && !(activeTab === 'archived' && snap.filterStatuses.includes('unread')) ? (
} itemProps={{ setRowHeight: (idx: number, height: number) => { if (rowHeights.current) { rowHeights.current = { ...rowHeights.current, [idx]: height } } }, getProject: (ref: string) => projects?.find((project) => project.ref === ref)!, getOrganizationById: (id: number) => organizations?.find((org) => org.id === id)!, getOrganizationBySlug: (slug: string) => organizations?.find((org) => org.slug === slug)!, onUpdateNotificationStatus: (id: string, status: 'archived' | 'seen') => { updateNotifications({ ids: [id], status }) }, queueMarkRead: (id: string) => { if (markedRead.current && !markedRead.current.includes(id)) { markedRead.current.push(id) } }, }} getItemSize={(idx: number) => rowHeights?.current?.[idx] ?? 56} hasNextPage={hasNextPage} isLoadingNextPage={isFetchingNextPage} onLoadNextPage={() => fetchNextPage()} /> ) : (

{activeTab === 'archived' ? `No archived notifications${ snap.numFiltersApplied > 0 ? ` based on the ${snap.numFiltersApplied} filter${ snap.numFiltersApplied > 1 ? 's' : '' } applied` : '' }` : snap.numFiltersApplied > 0 ? `No notifications based on the ${snap.numFiltersApplied} filter${ snap.numFiltersApplied > 1 ? 's' : '' } applied` : 'All caught up'}

{activeTab === 'archived' ? 'Notifications that you have previously archived will be shown here' : 'You will be notified here for any notices on your organizations and projects'}

)}
)}
{notifications.length > 0 && activeTab === 'inbox' && (
)} ) }