/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import { INotification, IRemoteNotification, useNotification } from '@magicbell/react-headless';
import { useTheme } from '../../context/MagicBellThemeContext.js';
import NotificationContent from '../NotificationContent/index.js';
import NotificationMenu from '../NotificationMenu/index.js';
import NotificationState from '../NotificationState/index.js';
import Timestamp from '../Timestamp/index.js';
import { openActionUrl } from './eventHandlers.js';
import NotificationTitle from './NotificationTitle.js';
import StyledContainer from './StyledContainer.js';
export interface Props {
notification: IRemoteNotification;
onClick?: (notification: INotification) => void | boolean;
prose?: boolean;
}
const actionableTags = new Set(['A', 'BUTTON', 'INPUT', 'TEXTAREA', 'SELECT']);
function isActionableElement(event: MouseEvent): [boolean, boolean] {
let el = event.target as HTMLElement;
while (el && el !== event.currentTarget) {
if (actionableTags.has(el.tagName.toUpperCase())) {
const isMenu = el.dataset.magicbellTarget === 'notification-menu';
return [true, isMenu];
}
el = el.parentElement;
}
return [false, false];
}
/**
* Component that renders a notification. When the notification is clicked the
* `onClick` callback is called and the notification is marked as read.
*
* @example
*
*/
export default function ClickableNotification({ notification: rawNotification, onClick, prose }: Props) {
const {
notification: { default: theme },
} = useTheme();
const notification = useNotification(rawNotification);
const handleClick = (event) => {
// ignore event when user clicks inside the menu
if (event.isDefaultPrevented()) return;
const [isAction, isMenu] = isActionableElement(event);
let markAsReadPromise;
// don't mark as read when the user clicks the menu
if (!isMenu) {
markAsReadPromise = notification.markAsRead();
}
// We don't want to invoke the action url when the user clicks a link or button inside the notification.
// Notification content should take precedence.
if (isAction) return;
const onClickResult = onClick?.(notification);
if (onClickResult === false) return;
if (notification.actionUrl) {
// We wait for the markAsRead before navigating to the new url, to prevent race conditions
// between mark and read, and fetching new data on page reload. But let's not wait forever.
const timeout = new Promise((resolve) => setTimeout(resolve, 1_000));
Promise.race([markAsReadPromise, timeout]).then(() => openActionUrl(notification));
}
};
const content = css`
margin: 0 8px !important;
width: 100%;
`;
const actions = css`
display: flex;
padding: 0 5px !important;
flex-direction: column;
align-items: flex-end;
margin-left: auto !important;
font-size: ${theme.fontSize} !important;
`;
return (
{notification.sentAt ?
:
}
);
}