import { useFetcher } from "@remix-run/react"; import { useCallback, useEffect, useRef, useState } from "react"; import simplur from "simplur"; import { NotificationIcon } from "~/assets/icons/NotificationIcon"; import { Button } from "~/components/primitives/Buttons"; import { Popover, PopoverContent, PopoverTrigger } from "~/components/primitives/Popover"; import { SimpleTooltip } from "~/components/primitives/Tooltip"; import { usePlatformNotifications } from "~/routes/resources.platform-notifications"; import { NotificationCard } from "./NotificationCard"; type Notification = { id: string; friendlyId: string; scope: string; priority: number; payload: { version: string; data: { title: string; description: string; image?: string; actionLabel?: string; actionUrl?: string; dismissOnAction?: boolean; }; }; isRead: boolean; }; export function NotificationPanel({ isCollapsed, hasIncident, organizationId, projectId, }: { isCollapsed: boolean; hasIncident: boolean; organizationId: string; projectId: string; }) { const { notifications } = usePlatformNotifications(organizationId, projectId) as { notifications: Notification[]; }; const [dismissedIds, setDismissedIds] = useState>(new Set()); const dismissFetcher = useFetcher(); const seenIdsRef = useRef>(new Set()); const seenFetcher = useFetcher(); const clickedIdsRef = useRef>(new Set()); const clickFetcher = useFetcher(); const visibleNotifications = notifications.filter((n) => !dismissedIds.has(n.id)); const notification = visibleNotifications[0] ?? null; const handleDismiss = useCallback((id: string) => { setDismissedIds((prev) => new Set(prev).add(id)); dismissFetcher.submit( {}, { method: "POST", action: `/resources/platform-notifications/${id}/dismiss`, } ); }, []); const fireClickBeacon = useCallback((id: string) => { if (clickedIdsRef.current.has(id)) return; clickedIdsRef.current.add(id); clickFetcher.submit( {}, { method: "POST", action: `/resources/platform-notifications/${id}/clicked`, } ); }, []); // Fire seen beacon const fireSeenBeacon = useCallback((n: Notification) => { if (seenIdsRef.current.has(n.id)) return; seenIdsRef.current.add(n.id); seenFetcher.submit( {}, { method: "POST", action: `/resources/platform-notifications/${n.id}/seen`, } ); }, []); // Beacon current notification on mount useEffect(() => { if (notification && !hasIncident) { fireSeenBeacon(notification); } }, [notification?.id, hasIncident]); if (!notification) { return null; } const { title, description, image, actionUrl, dismissOnAction } = notification.payload.data; const card = ( handleDismiss(notification.id)} onCardClick={() => { fireClickBeacon(notification.id); if (dismissOnAction) { handleDismiss(notification.id); } }} onLinkClick={() => fireClickBeacon(notification.id)} /> ); return (
{isCollapsed ? (
} content={simplur`${visibleNotifications.length} notification[|s]`} side="right" sideOffset={8} disableHoverableContent /> ) : ( card )} {card}
); }