import React, {JSXElementConstructor, useContext} from 'react'; import {GroupAddIcon} from '../icons/material/GroupAdd'; import {PeopleIcon} from '../icons/material/People'; import {FileDownloadDoneIcon} from '../icons/material/FileDownloadDone'; import { DatabaseNotification, DatabaseNotificationAction, } from './database-notification'; import {MixedImage} from '../ui/images/mixed-image'; import {Button} from '../ui/buttons/button'; import {SiteConfigContext} from '../core/settings/site-config-context'; import {Line} from './notification-line'; import {SvgIconProps} from '../icons/svg-icon'; import clsx from 'clsx'; import {useMarkNotificationsAsRead} from './requests/use-mark-notifications-as-read'; import {useNavigate} from '../utils/hooks/use-navigate'; import {isAbsoluteUrl} from '../utils/urls/is-absolute-url'; import {Link} from 'react-router-dom'; import {useSettings} from '../core/settings/use-settings'; const iconMap = { 'group-add': GroupAddIcon, people: PeopleIcon, 'export-csv': FileDownloadDoneIcon, } as Record>; interface NotificationListProps { notifications: DatabaseNotification[]; className?: string; } export function NotificationList({ notifications, className, }: NotificationListProps) { const {notifications: config} = useContext(SiteConfigContext); return (
{notifications.map((notification, index) => { const isLast = notifications.length - 1 === index; const Renderer = config?.renderMap?.[notification.type] || NotificationListItem; return ( ); })}
); } export interface NotificationListItemProps { notification: DatabaseNotification; onActionButtonClick?: ButtonActionsProps['onActionClick']; lineIconRenderer?: JSXElementConstructor<{icon: string}>; isLast: boolean; } export function NotificationListItem({ notification, onActionButtonClick, lineIconRenderer, isLast, }: NotificationListItemProps) { const markAsRead = useMarkNotificationsAsRead(); const navigate = useNavigate(); const mainAction = notification.data.mainAction; const showUnreadIndicator = !notification.data.image && !notification.read_at; return (
{ if (!markAsRead.isPending && !notification.read_at) { markAsRead.mutate({ids: [notification.id]}); } if (mainAction?.action) { if (isAbsoluteUrl(mainAction.action)) { window.open(mainAction.action, '_blank')?.focus(); } else { navigate(mainAction.action); } } }} className={clsx( 'flex items-start gap-14 px-32 py-20 bg-alt relative', !isLast && 'border-b', mainAction?.action && 'cursor-pointer', !notification.read_at ? 'bg-paper hover:bg-primary/10' : 'hover:bg-hover', )} title={mainAction?.label ? mainAction.label : undefined} > {showUnreadIndicator && (
)} {notification.data.image && ( )}
{notification.data.lines.map((line, index) => ( ))}
); } interface ButtonActionsProps { notification: DatabaseNotification; onActionClick?: ( e: React.MouseEvent, action: DatabaseNotificationAction, ) => void; } function ButtonActions({notification, onActionClick}: ButtonActionsProps) { const {base_url} = useSettings(); if (!notification.data.buttonActions) return null; // if there's no action handler provided, assume action is internal url and render a link return (
{notification.data.buttonActions.map((action, index) => ( ))}
); }