UNPKG

@liveblocks/react-ui

Version:

A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.

503 lines (499 loc) 17.5 kB
"use client"; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { assertNever, console } from '@liveblocks/core'; import { useMarkInboxNotificationAsRead, useDeleteInboxNotification, useInboxNotificationThread, useRoomInfo } from '@liveblocks/react'; import { Slot } from '@radix-ui/react-slot'; import { TooltipProvider } from '@radix-ui/react-tooltip'; import { forwardRef, useState, useCallback, useMemo } from 'react'; import { useComponents } from '../components.js'; import { CheckIcon } from '../icons/Check.js'; import { DeleteIcon } from '../icons/Delete.js'; import { EllipsisIcon } from '../icons/Ellipsis.js'; import { WarningIcon } from '../icons/Warning.js'; import { useOverrides } from '../overrides.js'; import { Timestamp } from '../primitives/Timestamp.js'; import { useCurrentUserId } from '../shared.js'; import { classNames } from '../utils/class-names.js'; import { generateURL } from '../utils/url.js'; import { Avatar } from './internal/Avatar.js'; import { Button } from './internal/Button.js'; import { Dropdown, DropdownItem } from './internal/Dropdown.js'; import { generateInboxNotificationThreadContents, InboxNotificationComment, INBOX_NOTIFICATION_THREAD_MAX_COMMENTS } from './internal/InboxNotificationThread.js'; import { List } from './internal/List.js'; import { Room } from './internal/Room.js'; import { Tooltip } from './internal/Tooltip.js'; import { User } from './internal/User.js'; import { DropdownMenuTrigger } from '@radix-ui/react-dropdown-menu'; const InboxNotificationLayout = forwardRef( ({ inboxNotification, children, aside, title, date, unread, markAsReadOnClick, onClick, href, showActions, overrides, components, className, asChild, ...props }, forwardedRef) => { const $ = useOverrides(overrides); const { Anchor } = useComponents(components); const Component = asChild ? Slot : Anchor; const [isMoreActionOpen, setMoreActionOpen] = useState(false); const markInboxNotificationAsRead = useMarkInboxNotificationAsRead(); const deleteInboxNotification = useDeleteInboxNotification(); const handleClick = useCallback( (event) => { onClick?.(event); const shouldMarkAsReadOnClick = markAsReadOnClick ?? Boolean(href); if (unread && shouldMarkAsReadOnClick) { markInboxNotificationAsRead(inboxNotification.id); } }, [ href, inboxNotification.id, markAsReadOnClick, markInboxNotificationAsRead, onClick, unread ] ); const stopPropagation = useCallback((event) => { event.stopPropagation(); }, []); const preventDefaultAndStopPropagation = useCallback( (event) => { event.preventDefault(); event.stopPropagation(); }, [] ); const handleMoreClick = useCallback((event) => { event.preventDefault(); event.stopPropagation(); setMoreActionOpen((open) => !open); }, []); const handleMarkAsRead = useCallback(() => { markInboxNotificationAsRead(inboxNotification.id); }, [inboxNotification.id, markInboxNotificationAsRead]); const handleDelete = useCallback(() => { deleteInboxNotification(inboxNotification.id); }, [inboxNotification.id, deleteInboxNotification]); return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Component, { className: classNames( "lb-root lb-inbox-notification", showActions === "hover" && "lb-inbox-notification:show-actions-hover", isMoreActionOpen && "lb-inbox-notification:action-open", className ), dir: $.dir, "data-unread": unread ? "" : void 0, "data-kind": inboxNotification.kind, onClick: handleClick, href, ...props, ref: forwardedRef, children: [ aside && /* @__PURE__ */ jsx("div", { className: "lb-inbox-notification-aside", children: aside }), /* @__PURE__ */ jsxs("div", { className: "lb-inbox-notification-content", children: [ /* @__PURE__ */ jsxs("div", { className: "lb-inbox-notification-header", children: [ /* @__PURE__ */ jsx("span", { className: "lb-inbox-notification-title", children: title }), /* @__PURE__ */ jsx("div", { className: "lb-inbox-notification-details", children: /* @__PURE__ */ jsxs("span", { className: "lb-inbox-notification-details-labels", children: [ /* @__PURE__ */ jsx(Timestamp, { locale: $.locale, date, className: "lb-date lb-inbox-notification-date" }), unread && /* @__PURE__ */ jsx("span", { className: "lb-inbox-notification-unread-indicator", role: "presentation" }) ] }) }), showActions && /* @__PURE__ */ jsx("div", { className: "lb-inbox-notification-actions", children: /* @__PURE__ */ jsx(Dropdown, { open: isMoreActionOpen, onOpenChange: setMoreActionOpen, align: "end", content: /* @__PURE__ */ jsxs(Fragment, { children: [ unread ? /* @__PURE__ */ jsx(DropdownItem, { onSelect: handleMarkAsRead, onClick: stopPropagation, icon: /* @__PURE__ */ jsx(CheckIcon, {}), children: $.INBOX_NOTIFICATION_MARK_AS_READ }) : null, /* @__PURE__ */ jsx(DropdownItem, { onSelect: handleDelete, onClick: stopPropagation, icon: /* @__PURE__ */ jsx(DeleteIcon, {}), children: $.INBOX_NOTIFICATION_DELETE }) ] }), children: /* @__PURE__ */ jsx(Tooltip, { content: $.INBOX_NOTIFICATION_MORE, children: /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { className: "lb-inbox-notification-action", onClick: handleMoreClick, onPointerDown: preventDefaultAndStopPropagation, onPointerUp: preventDefaultAndStopPropagation, "aria-label": $.INBOX_NOTIFICATION_MORE, icon: /* @__PURE__ */ jsx(EllipsisIcon, {}) }) }) }) }) }) ] }), /* @__PURE__ */ jsx("div", { className: "lb-inbox-notification-body", children }) ] }) ] }) }); } ); function InboxNotificationIcon({ className, ...props }) { return /* @__PURE__ */ jsx("div", { className: classNames("lb-inbox-notification-icon", className), ...props }); } function InboxNotificationAvatar({ className, ...props }) { return /* @__PURE__ */ jsx(Avatar, { className: classNames("lb-inbox-notification-avatar", className), ...props }); } const InboxNotificationThread = forwardRef( ({ inboxNotification, href, showRoomName = true, showReactions = true, showAttachments = true, showActions = "hover", overrides, ...props }, forwardedRef) => { const $ = useOverrides(overrides); const thread = useInboxNotificationThread(inboxNotification.id); const currentUserId = useCurrentUserId(); const { info } = useRoomInfo(inboxNotification.roomId); const contents = useMemo(() => { const contents2 = generateInboxNotificationThreadContents( inboxNotification, thread, currentUserId ?? "" ); if (contents2.comments.length === 0 || contents2.userIds.length === 0) { return null; } switch (contents2.type) { case "comments": { const reversedUserIds = [...contents2.userIds].reverse(); const firstUserId = reversedUserIds[0]; const aside2 = /* @__PURE__ */ jsx(InboxNotificationAvatar, { userId: firstUserId }); const title2 = $.INBOX_NOTIFICATION_THREAD_COMMENTS_LIST( /* @__PURE__ */ jsx(List, { values: reversedUserIds.map((userId) => /* @__PURE__ */ jsx(User, { userId, replaceSelf: true }, userId)), formatRemaining: $.LIST_REMAINING_USERS, truncate: INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1, locale: $.locale }), showRoomName ? /* @__PURE__ */ jsx(Room, { roomId: thread.roomId }) : void 0, reversedUserIds.length ); const content2 = /* @__PURE__ */ jsx("div", { className: "lb-inbox-notification-comments", children: contents2.comments.map((comment) => /* @__PURE__ */ jsx(InboxNotificationComment, { comment, showHeader: contents2.comments.length > 1, showAttachments, showReactions, overrides }, comment.id)) }); return { unread: contents2.unread, date: contents2.date, aside: aside2, title: title2, content: content2, threadId: thread.id, commentId: contents2.comments[contents2.comments.length - 1].id }; } case "mention": { const mentionUserId = contents2.userIds[0]; const mentionComment = contents2.comments[0]; const aside2 = /* @__PURE__ */ jsx(InboxNotificationAvatar, { userId: mentionUserId }); const title2 = $.INBOX_NOTIFICATION_THREAD_MENTION( /* @__PURE__ */ jsx(User, { userId: mentionUserId }, mentionUserId), showRoomName ? /* @__PURE__ */ jsx(Room, { roomId: thread.roomId }) : void 0 ); const content2 = /* @__PURE__ */ jsx("div", { className: "lb-inbox-notification-comments", children: /* @__PURE__ */ jsx(InboxNotificationComment, { comment: mentionComment, showHeader: false, showAttachments, showReactions, overrides }, mentionComment.id) }); return { unread: contents2.unread, date: contents2.date, aside: aside2, title: title2, content: content2, threadId: thread.id, commentId: mentionComment.id }; } default: return assertNever( contents2, "Unexpected thread inbox notification type" ); } }, [ $, currentUserId, inboxNotification, overrides, showRoomName, showAttachments, showReactions, thread ]); const resolvedHref = useMemo(() => { const resolvedHref2 = href ?? info?.url; return resolvedHref2 ? generateURL(resolvedHref2, void 0, contents?.commentId) : void 0; }, [contents?.commentId, href, info?.url]); if (!contents) { return null; } const { aside, title, content, date, unread } = contents; return /* @__PURE__ */ jsx(InboxNotificationLayout, { inboxNotification, aside, title, date, unread, overrides, href: resolvedHref, showActions, markAsReadOnClick: false, ...props, ref: forwardedRef, children: content }); } ); const InboxNotificationTextMention = forwardRef( ({ inboxNotification, showActions = "hover", showRoomName = true, href, overrides, ...props }, ref) => { const $ = useOverrides(overrides); const { info } = useRoomInfo(inboxNotification.roomId); const resolvedHref = useMemo(() => { const resolvedHref2 = href ?? info?.url; return resolvedHref2 ? generateURL(resolvedHref2) : void 0; }, [href, info?.url]); const unread = useMemo(() => { return !inboxNotification.readAt || inboxNotification.notifiedAt > inboxNotification.readAt; }, [inboxNotification.notifiedAt, inboxNotification.readAt]); return /* @__PURE__ */ jsx(InboxNotificationLayout, { inboxNotification, aside: /* @__PURE__ */ jsx(InboxNotificationAvatar, { userId: inboxNotification.createdBy }), title: $.INBOX_NOTIFICATION_TEXT_MENTION( /* @__PURE__ */ jsx(User, { userId: inboxNotification.createdBy }, inboxNotification.createdBy), showRoomName ? /* @__PURE__ */ jsx(Room, { roomId: inboxNotification.roomId }) : void 0 ), date: inboxNotification.notifiedAt, unread, overrides, showActions, href: resolvedHref, ...props, ref }); } ); const InboxNotificationCustom = forwardRef( ({ inboxNotification, showActions = "hover", title, aside, children, overrides, ...props }, forwardedRef) => { const unread = useMemo(() => { return !inboxNotification.readAt || inboxNotification.notifiedAt > inboxNotification.readAt; }, [inboxNotification.notifiedAt, inboxNotification.readAt]); return /* @__PURE__ */ jsx(InboxNotificationLayout, { inboxNotification, aside, title, date: inboxNotification.notifiedAt, unread, overrides, showActions, ...props, ref: forwardedRef, children }); } ); const InboxNotificationCustomMissing = forwardRef(({ inboxNotification, ...props }, forwardedRef) => { return /* @__PURE__ */ jsxs(InboxNotificationCustom, { inboxNotification, ...props, title: /* @__PURE__ */ jsxs(Fragment, { children: [ "Custom notification kind ", /* @__PURE__ */ jsx("code", { children: inboxNotification.kind }), " is not handled" ] }), aside: /* @__PURE__ */ jsx(InboxNotificationIcon, { children: /* @__PURE__ */ jsx(WarningIcon, {}) }), ref: forwardedRef, "data-missing": "", children: [ "Notifications of this kind won\u2019t be displayed in production. Use the", " ", /* @__PURE__ */ jsx("code", { children: "kinds" }), " prop to define how they should be rendered." ] }); }); const inboxNotificationKindsWarnings = /* @__PURE__ */ new Set(); const InboxNotification = Object.assign( forwardRef( ({ inboxNotification, kinds, ...props }, forwardedRef) => { switch (inboxNotification.kind) { case "thread": { const ResolvedInboxNotificationThread = kinds?.thread ?? InboxNotificationThread; return /* @__PURE__ */ jsx(ResolvedInboxNotificationThread, { inboxNotification, ...props, ref: forwardedRef }); } case "textMention": { const ResolvedInboxNotificationTextMention = kinds?.textMention ?? InboxNotificationTextMention; return /* @__PURE__ */ jsx(ResolvedInboxNotificationTextMention, { inboxNotification, ...props, ref: forwardedRef }); } default: { const ResolvedInboxNotificationCustom = kinds?.[inboxNotification.kind]; if (!ResolvedInboxNotificationCustom) { if (process.env.NODE_ENV !== "production") { if (!inboxNotificationKindsWarnings.has(inboxNotification.kind)) { inboxNotificationKindsWarnings.add(inboxNotification.kind); console.warn( `Custom notification kind "${inboxNotification.kind}" is not handled so notifications of this kind will not be displayed in production. Use the kinds prop to define how they should be rendered.` ); } return /* @__PURE__ */ jsx(InboxNotificationCustomMissing, { inboxNotification, ...props, ref: forwardedRef }); } else { return null; } } return /* @__PURE__ */ jsx(ResolvedInboxNotificationCustom, { inboxNotification, ...props, ref: forwardedRef }); } } } ), { Thread: InboxNotificationThread, TextMention: InboxNotificationTextMention, Custom: InboxNotificationCustom, Icon: InboxNotificationIcon, Avatar: InboxNotificationAvatar } ); export { InboxNotification }; //# sourceMappingURL=InboxNotification.js.map