@liveblocks/react-ui 3.1.1 → 3.1.2-url1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/components/InboxNotification.cjs +2 -3
  2. package/dist/components/InboxNotification.cjs.map +1 -1
  3. package/dist/components/InboxNotification.js +3 -4
  4. package/dist/components/InboxNotification.js.map +1 -1
  5. package/dist/primitives/Comment/index.cjs +9 -4
  6. package/dist/primitives/Comment/index.cjs.map +1 -1
  7. package/dist/primitives/Comment/index.js +7 -2
  8. package/dist/primitives/Comment/index.js.map +1 -1
  9. package/dist/primitives/Composer/index.cjs +12 -16
  10. package/dist/primitives/Composer/index.cjs.map +1 -1
  11. package/dist/primitives/Composer/index.js +2 -6
  12. package/dist/primitives/Composer/index.js.map +1 -1
  13. package/dist/primitives/Composer/utils.cjs +3 -4
  14. package/dist/primitives/Composer/utils.cjs.map +1 -1
  15. package/dist/primitives/Composer/utils.js +1 -2
  16. package/dist/primitives/Composer/utils.js.map +1 -1
  17. package/dist/primitives/Markdown.cjs +3 -26
  18. package/dist/primitives/Markdown.cjs.map +1 -1
  19. package/dist/primitives/Markdown.js +3 -26
  20. package/dist/primitives/Markdown.js.map +1 -1
  21. package/dist/version.cjs +1 -1
  22. package/dist/version.cjs.map +1 -1
  23. package/dist/version.js +1 -1
  24. package/dist/version.js.map +1 -1
  25. package/package.json +4 -4
  26. package/dist/primitives/Comment/utils.cjs +0 -25
  27. package/dist/primitives/Comment/utils.cjs.map +0 -1
  28. package/dist/primitives/Comment/utils.js +0 -20
  29. package/dist/primitives/Comment/utils.js.map +0 -1
  30. package/dist/utils/url.cjs +0 -22
  31. package/dist/utils/url.cjs.map +0 -1
  32. package/dist/utils/url.js +0 -20
  33. package/dist/utils/url.js.map +0 -1
@@ -19,7 +19,6 @@ var overrides = require('../overrides.cjs');
19
19
  var Timestamp = require('../primitives/Timestamp.cjs');
20
20
  var shared = require('../shared.cjs');
21
21
  var cn = require('../utils/cn.cjs');
22
- var url = require('../utils/url.cjs');
23
22
  var Avatar = require('./internal/Avatar.cjs');
24
23
  var Button = require('./internal/Button.cjs');
25
24
  var CodeBlock = require('./internal/CodeBlock.cjs');
@@ -337,7 +336,7 @@ const InboxNotificationThread = react.forwardRef(
337
336
  ]);
338
337
  const resolvedHref = react.useMemo(() => {
339
338
  const resolvedHref2 = href ?? info?.url;
340
- return resolvedHref2 ? url.generateURL(resolvedHref2, void 0, contents?.commentId) : void 0;
339
+ return resolvedHref2 ? core.generateUrl(resolvedHref2, void 0, contents?.commentId) : void 0;
341
340
  }, [contents?.commentId, href, info?.url]);
342
341
  const handleSubscribeChange = react.useCallback(() => {
343
342
  if (subscriptionStatus === "subscribed") {
@@ -388,7 +387,7 @@ const InboxNotificationTextMention = react.forwardRef(
388
387
  const { info } = react$1.useRoomInfo(inboxNotification.roomId);
389
388
  const resolvedHref = react.useMemo(() => {
390
389
  const resolvedHref2 = href ?? info?.url;
391
- return resolvedHref2 ? url.generateURL(resolvedHref2) : void 0;
390
+ return resolvedHref2 ? core.sanitizeUrl(resolvedHref2) ?? void 0 : void 0;
392
391
  }, [href, info?.url]);
393
392
  const unread = react.useMemo(() => {
394
393
  return !inboxNotification.readAt || inboxNotification.notifiedAt > inboxNotification.readAt;
@@ -1 +1 @@
1
- {"version":3,"file":"InboxNotification.cjs","sources":["../../src/components/InboxNotification.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n InboxNotificationCustomData,\n InboxNotificationData,\n InboxNotificationTextMentionData,\n InboxNotificationThreadData,\n KDAD,\n} from \"@liveblocks/core\";\nimport { assertNever, console } from \"@liveblocks/core\";\nimport {\n useDeleteInboxNotification,\n useInboxNotificationThread,\n useMarkInboxNotificationAsRead,\n useRoomInfo,\n} from \"@liveblocks/react\";\nimport { useRoomThreadSubscription } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { TooltipProvider } from \"@radix-ui/react-tooltip\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n ComponentType,\n MouseEvent as ReactMouseEvent,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\nimport { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { WarningIcon } from \"../icons/Warning\";\nimport type {\n CommentOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { generateURL } from \"../utils/url\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport {\n generateInboxNotificationThreadContents,\n INBOX_NOTIFICATION_THREAD_MAX_COMMENTS,\n InboxNotificationComment,\n} from \"./internal/InboxNotificationThread\";\nimport { List } from \"./internal/List\";\nimport { Room } from \"./internal/Room\";\nimport { Tooltip } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ntype ComponentTypeWithRef<\n T extends keyof JSX.IntrinsicElements,\n P,\n> = ComponentType<P & Pick<ComponentProps<T>, \"ref\">>;\n\ntype InboxNotificationKinds<KS extends KDAD = KDAD> = {\n // For some reason, we cannot directly use KDAD in the mapped type line\n // below, because it will result in '{}' rather than picking up the\n // definition from the user-provided 'ActivitiesData'. Might be an internal\n // TS optimization, so we're making it a param to defer the resolution.\n [K in KS]: ComponentTypeWithRef<\"a\", InboxNotificationCustomKindProps<K>>;\n} & {\n thread: ComponentTypeWithRef<\"a\", InboxNotificationThreadKindProps>;\n textMention: ComponentTypeWithRef<\"a\", InboxNotificationTextMentionKindProps>;\n};\n\ninterface InboxNotificationSharedProps {\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n}\n\nexport interface InboxNotificationProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n\n /**\n * Override specific kinds of inbox notifications.\n */\n kinds?: Partial<InboxNotificationKinds>;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n InboxNotificationOverrides &\n ThreadOverrides &\n CommentOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport interface InboxNotificationThreadProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationThreadData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n}\n\nexport interface InboxNotificationTextMentionProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationTextMentionData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n}\n\nexport interface InboxNotificationInspectorProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n}\n\nexport interface InboxNotificationCustomProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps,\n SlotProp {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationCustomData;\n\n /**\n * The inbox notification's content.\n */\n children: ReactNode;\n\n /**\n * The inbox notification's title.\n */\n title: ReactNode;\n\n /**\n * The inbox notification's aside content.\n * Can be combined with `InboxNotification.Icon` or `InboxNotification.Avatar` to easily follow default styles.\n */\n aside?: ReactNode;\n\n /**\n * Whether to mark the inbox notification as read when clicked.\n */\n markAsReadOnClick?: boolean;\n}\n\nexport type InboxNotificationThreadKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationThreadData;\n};\n\nexport type InboxNotificationTextMentionKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationTextMentionData;\n};\n\nexport type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationCustomData<K>;\n};\n\ninterface InboxNotificationLayoutProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps,\n SlotProp {\n inboxNotification: InboxNotificationData;\n aside?: ReactNode;\n title: ReactNode;\n date: Date | string | number;\n unread?: boolean;\n overrides?: Partial<GlobalOverrides & InboxNotificationOverrides>;\n components?: Partial<GlobalComponents>;\n markAsReadOnClick?: boolean;\n\n /**\n * @internal\n */\n additionalDropdownItemsBefore?: ReactNode;\n\n /**\n * @internal\n */\n additionalDropdownItemsAfter?: ReactNode;\n}\n\nexport type InboxNotificationIconProps = ComponentProps<\"div\">;\n\nexport type InboxNotificationAvatarProps = AvatarProps;\n\nconst InboxNotificationLayout = forwardRef<\n HTMLAnchorElement,\n InboxNotificationLayoutProps\n>(\n (\n {\n inboxNotification,\n children,\n aside,\n title,\n date,\n unread,\n markAsReadOnClick,\n onClick,\n href,\n showActions,\n overrides,\n components,\n className,\n asChild,\n additionalDropdownItemsBefore,\n additionalDropdownItemsAfter,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const { Anchor } = useComponents(components);\n const Component = asChild ? Slot : Anchor;\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const markInboxNotificationAsRead = useMarkInboxNotificationAsRead();\n const deleteInboxNotification = useDeleteInboxNotification();\n\n const handleClick = useCallback(\n (event: ReactMouseEvent<HTMLAnchorElement, MouseEvent>) => {\n onClick?.(event);\n\n const shouldMarkAsReadOnClick = markAsReadOnClick ?? Boolean(href);\n\n if (unread && shouldMarkAsReadOnClick) {\n markInboxNotificationAsRead(inboxNotification.id);\n }\n },\n [\n href,\n inboxNotification.id,\n markAsReadOnClick,\n markInboxNotificationAsRead,\n onClick,\n unread,\n ]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const preventDefaultAndStopPropagation = useCallback(\n (event: SyntheticEvent) => {\n event.preventDefault();\n event.stopPropagation();\n },\n []\n );\n\n const handleMoreClick = useCallback((event: ReactMouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n setMoreActionOpen((open) => !open);\n }, []);\n\n const handleMarkAsRead = useCallback(() => {\n markInboxNotificationAsRead(inboxNotification.id);\n }, [inboxNotification.id, markInboxNotificationAsRead]);\n\n const handleDelete = useCallback(() => {\n deleteInboxNotification(inboxNotification.id);\n }, [inboxNotification.id, deleteInboxNotification]);\n\n return (\n <TooltipProvider>\n <Component\n className={cn(\n \"lb-root lb-inbox-notification\",\n showActions === \"hover\" &&\n \"lb-inbox-notification:show-actions-hover\",\n isMoreActionOpen && \"lb-inbox-notification:action-open\",\n className\n )}\n dir={$.dir}\n data-unread={unread ? \"\" : undefined}\n data-kind={inboxNotification.kind}\n onClick={handleClick}\n href={href}\n {...props}\n ref={forwardedRef}\n >\n {aside && <div className=\"lb-inbox-notification-aside\">{aside}</div>}\n <div className=\"lb-inbox-notification-content\">\n <div className=\"lb-inbox-notification-header\">\n <span className=\"lb-inbox-notification-title\">{title}</span>\n <div className=\"lb-inbox-notification-details\">\n <span className=\"lb-inbox-notification-details-labels\">\n <Timestamp\n locale={$.locale}\n date={date}\n className=\"lb-date lb-inbox-notification-date\"\n />\n {unread && (\n <span\n className=\"lb-inbox-notification-unread-indicator\"\n role=\"presentation\"\n />\n )}\n </span>\n </div>\n {showActions && (\n <div className=\"lb-inbox-notification-actions\">\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={\n <>\n {additionalDropdownItemsBefore}\n {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n icon={<CheckIcon />}\n >\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n icon={<DeleteIcon />}\n >\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\n {additionalDropdownItemsAfter}\n </>\n }\n >\n <Tooltip content={$.INBOX_NOTIFICATION_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-inbox-notification-action\"\n onClick={handleMoreClick}\n onPointerDown={preventDefaultAndStopPropagation}\n onPointerUp={preventDefaultAndStopPropagation}\n aria-label={$.INBOX_NOTIFICATION_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n </div>\n )}\n </div>\n <div className=\"lb-inbox-notification-body\">{children}</div>\n </div>\n </Component>\n </TooltipProvider>\n );\n }\n);\n\nfunction InboxNotificationIcon({\n className,\n ...props\n}: InboxNotificationIconProps) {\n return (\n <div className={cn(\"lb-inbox-notification-icon\", className)} {...props} />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={cn(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\nconst InboxNotificationThread = forwardRef<\n HTMLAnchorElement,\n InboxNotificationThreadProps\n>(\n (\n {\n inboxNotification,\n href,\n showRoomName = true,\n showReactions = true,\n showAttachments = true,\n showActions = \"hover\",\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const thread = useInboxNotificationThread(inboxNotification.id);\n const {\n status: subscriptionStatus,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const currentUserId = useCurrentUserId();\n const { info } = useRoomInfo(inboxNotification.roomId);\n const contents = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\n );\n\n if (contents.comments.length === 0 || contents.userIds.length === 0) {\n return null;\n }\n\n switch (contents.type) {\n case \"comments\": {\n const reversedUserIds = [...contents.userIds].reverse();\n const firstUserId = reversedUserIds[0]!;\n\n const aside = <InboxNotificationAvatar userId={firstUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_COMMENTS_LIST(\n <List\n values={reversedUserIds.map((userId) => (\n <User key={userId} userId={userId} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\n locale={$.locale}\n />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined,\n reversedUserIds.length\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n {contents.comments.map((comment) => (\n <InboxNotificationComment\n key={comment.id}\n comment={comment}\n showHeader={contents.comments.length > 1}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n ))}\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: contents.comments[contents.comments.length - 1]!.id,\n };\n }\n\n case \"mention\": {\n const mentionUserId = contents.userIds[0]!;\n const mentionComment = contents.comments[0]!;\n\n const aside = <InboxNotificationAvatar userId={mentionUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_MENTION(\n <User key={mentionUserId} userId={mentionUserId} />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n <InboxNotificationComment\n key={mentionComment.id}\n comment={mentionComment}\n showHeader={false}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: mentionComment.id,\n };\n }\n\n default:\n return assertNever(\n contents,\n \"Unexpected thread inbox notification type\"\n );\n }\n }, [\n $,\n currentUserId,\n inboxNotification,\n overrides,\n showRoomName,\n showAttachments,\n showReactions,\n thread,\n ]);\n // Add the thread ID and comment ID to the `href`.\n // And use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref\n ? generateURL(resolvedHref, undefined, contents?.commentId)\n : undefined;\n }, [contents?.commentId, href, info?.url]);\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n if (!contents) {\n return null;\n }\n\n const { aside, title, content, date, unread } = contents;\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={date}\n unread={unread}\n overrides={overrides}\n href={resolvedHref}\n showActions={showActions}\n markAsReadOnClick={false}\n additionalDropdownItemsBefore={\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n }\n {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n href,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\n const { info } = useRoomInfo(inboxNotification.roomId);\n // Use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref ? generateURL(resolvedHref) : undefined;\n }, [href, info?.url]);\n\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={<InboxNotificationAvatar userId={inboxNotification.createdBy} />}\n title={$.INBOX_NOTIFICATION_TEXT_MENTION(\n <User\n key={inboxNotification.createdBy}\n userId={inboxNotification.createdBy}\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n href={resolvedHref}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\nconst InboxNotificationCustom = forwardRef<\n HTMLAnchorElement,\n InboxNotificationCustomProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n title,\n aside,\n children,\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationInspector = forwardRef<\n HTMLAnchorElement,\n InboxNotificationInspectorProps\n>(\n (\n { inboxNotification, showActions = \"hover\", overrides, ...props },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n title={<code>{inboxNotification.id}</code>}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n data-inspector=\"\"\n >\n <CodeBlock\n title=\"Data\"\n code={JSON.stringify(inboxNotification, null, 2)}\n />\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationCustomMissing = forwardRef<\n HTMLAnchorElement,\n Omit<InboxNotificationCustomProps, \"children\" | \"title\" | \"aside\">\n>(({ inboxNotification, ...props }, forwardedRef) => {\n return (\n <InboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n title={\n <>\n Custom notification kind <code>{inboxNotification.kind}</code> is not\n handled\n </>\n }\n aside={\n <InboxNotificationIcon>\n <WarningIcon />\n </InboxNotificationIcon>\n }\n ref={forwardedRef}\n data-missing=\"\"\n >\n {/* TODO: Add link to the docs */}\n Notifications of this kind won’t be displayed in production. Use the{\" \"}\n <code>kinds</code> prop to define how they should be rendered.\n </InboxNotificationCustom>\n );\n});\n\n// Keeps track of which inbox notification kinds it has warned about already.\nconst inboxNotificationKindsWarnings: Set<string> = new Set();\n\n/**\n * Displays a single inbox notification.\n *\n * @example\n * <>\n * {inboxNotifications.map((inboxNotification) => (\n * <InboxNotification\n * key={inboxNotification.id}\n * inboxNotification={inboxNotification}\n * href={`/rooms/${inboxNotification.roomId}`\n * />\n * ))}\n * </>\n */\nexport const InboxNotification = Object.assign(\n forwardRef<HTMLAnchorElement, InboxNotificationProps>(\n ({ inboxNotification, kinds, ...props }, forwardedRef) => {\n switch (inboxNotification.kind) {\n case \"thread\": {\n const ResolvedInboxNotificationThread =\n kinds?.thread ?? InboxNotificationThread;\n\n return (\n <ResolvedInboxNotificationThread\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n case \"textMention\": {\n const ResolvedInboxNotificationTextMention =\n kinds?.textMention ?? InboxNotificationTextMention;\n\n return (\n <ResolvedInboxNotificationTextMention\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n default: {\n const ResolvedInboxNotificationCustom =\n kinds?.[inboxNotification.kind];\n\n if (!ResolvedInboxNotificationCustom) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!inboxNotificationKindsWarnings.has(inboxNotification.kind)) {\n inboxNotificationKindsWarnings.add(inboxNotification.kind);\n // TODO: Add link to the docs\n console.warn(\n `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.`\n );\n }\n\n return (\n <InboxNotificationCustomMissing\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n } else {\n // Don't render anything in production if this inbox notification kind is not defined.\n return null;\n }\n }\n\n return (\n <ResolvedInboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n }\n }\n ),\n {\n /**\n * Displays a thread inbox notification kind.\n */\n Thread: InboxNotificationThread,\n\n /**\n * Displays a text mention inbox notification kind.\n */\n TextMention: InboxNotificationTextMention,\n\n /**\n * Displays a custom inbox notification kind.\n */\n Custom: InboxNotificationCustom,\n\n /**\n * Display the inbox notification's data, which can be useful during development.\n *\n * @example\n * <InboxNotification\n * inboxNotification={inboxNotification}\n * kinds={{\n * $custom: InboxNotification.Inspector,\n * }}\n * />\n */\n Inspector: InboxNotificationInspector,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkPA;AAAgC;AAK5B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AAEA;AACE;AAAgD;AAClD;AACF;AACA;AACE;AACkB;AAClB;AACA;AACA;AACA;AACF;AAGF;AACE;AAAsB;AAGxB;AAAyC;AAErC;AACA;AAAsB;AACxB;AACC;AAGH;AACE;AACA;AACA;AAAiC;AAGnC;AACE;AAAgD;AAGlD;AACE;AAA4C;AAG9C;AACG;AACE;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEJ;AAAU;AAAc;AAA+B;AAAM;AAC7D;AAAc;AACb;AAAC;AAAc;AACb;AAAC;AAAe;AAA+B;AAAM;AACpD;AAAc;AACZ;AAAe;AACd;AAAC;AACW;AACV;AACU;AACZ;AAEG;AACW;AACL;AACP;AAAA;AAEJ;AACF;AAEG;AAAc;AACZ;AACO;AACQ;AACR;AAEJ;AACG;AAAA;AAEE;AACW;AACD;AACQ;AAEd;AAEH;AACH;AACW;AACD;AACS;AAEf;AACL;AACC;AAAA;AACH;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AACM;AACtB;AACF;AACF;AACF;AACF;AAAA;AAEJ;AACC;AAAc;AAA8B;AAAS;AAAA;AACxD;AAAA;AACF;AACF;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAAyD;AAAO;AAErE;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACwD;AACnD;AAGV;AAEA;AAAgC;AAK5B;AACE;AACA;AACe;AACC;AACE;AACJ;AACd;AACG;AAIL;AACA;AACA;AAAM;AACI;AACR;AACA;AAEF;AACA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AACE;AAAO;AAGT;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AAAkB;AAA2B;AAC/C;AACkB;AACgC;AACzC;AACZ;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AAEC;AACuC;AACvC;AACA;AACA;AAEH;AAIL;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC2C;AAC9D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAiC;AAAe;AACjC;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AAEU;AACG;AACZ;AACA;AACA;AACF;AAIJ;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AACS;AAC5B;AACF;AAGE;AAAO;AACL;AACA;AACF;AACJ;AACC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIF;AACE;AAEA;AAEI;AAGN;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACE;AAAsB;AAGxB;AACE;AAAO;AAGT;AAEA;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AAEhB;AACW;AACD;AAKK;AAMR;AACR;AAEE;AACC;AAEJ;AACH;AAGN;AAEA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACA;AACG;AAIL;AACA;AAEA;AACE;AAEA;AAAkD;AAGpD;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AAE2B;AAC5B;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACM;AACF;AACJ;AACF;AAGN;AAEA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAEJ;AACH;AAGN;AAEA;AAAmC;AAQ/B;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAwB;AAAG;AACX;AACxB;AACA;AACA;AACI;AACC;AACU;AAEd;AACO;AACyC;AACjD;AACF;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAEF;AAAE;AAAA;AAC0B;AAAwB;AAAK;AAAO;AAAA;AAEhE;AAGC;AACc;AACf;AAEG;AACQ;AAEqB;AAAA;AACmC;AACpE;AAAK;AAAK;AAAO;AAAA;AAGxB;AAGA;AAgBO;AAAiC;AACtC;AAEI;AAAgC;AAE5B;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACE;AACE;AACE;AAEA;AAAQ;AACyC;AACjD;AAGF;AACG;AACC;AACI;AACC;AACP;AAIF;AAAO;AACT;AAGF;AACG;AACC;AACI;AACC;AACP;AAEJ;AACF;AACF;AACF;AACA;AAIU;AAKK;AAKL;AAaG;AACL;AACE;AAEZ;;"}
1
+ {"version":3,"file":"InboxNotification.cjs","sources":["../../src/components/InboxNotification.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n InboxNotificationCustomData,\n InboxNotificationData,\n InboxNotificationTextMentionData,\n InboxNotificationThreadData,\n KDAD,\n} from \"@liveblocks/core\";\nimport {\n assertNever,\n console,\n generateUrl,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport {\n useDeleteInboxNotification,\n useInboxNotificationThread,\n useMarkInboxNotificationAsRead,\n useRoomInfo,\n} from \"@liveblocks/react\";\nimport { useRoomThreadSubscription } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { TooltipProvider } from \"@radix-ui/react-tooltip\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n ComponentType,\n MouseEvent as ReactMouseEvent,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\nimport { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { WarningIcon } from \"../icons/Warning\";\nimport type {\n CommentOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport {\n generateInboxNotificationThreadContents,\n INBOX_NOTIFICATION_THREAD_MAX_COMMENTS,\n InboxNotificationComment,\n} from \"./internal/InboxNotificationThread\";\nimport { List } from \"./internal/List\";\nimport { Room } from \"./internal/Room\";\nimport { Tooltip } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ntype ComponentTypeWithRef<\n T extends keyof JSX.IntrinsicElements,\n P,\n> = ComponentType<P & Pick<ComponentProps<T>, \"ref\">>;\n\ntype InboxNotificationKinds<KS extends KDAD = KDAD> = {\n // For some reason, we cannot directly use KDAD in the mapped type line\n // below, because it will result in '{}' rather than picking up the\n // definition from the user-provided 'ActivitiesData'. Might be an internal\n // TS optimization, so we're making it a param to defer the resolution.\n [K in KS]: ComponentTypeWithRef<\"a\", InboxNotificationCustomKindProps<K>>;\n} & {\n thread: ComponentTypeWithRef<\"a\", InboxNotificationThreadKindProps>;\n textMention: ComponentTypeWithRef<\"a\", InboxNotificationTextMentionKindProps>;\n};\n\ninterface InboxNotificationSharedProps {\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n}\n\nexport interface InboxNotificationProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n\n /**\n * Override specific kinds of inbox notifications.\n */\n kinds?: Partial<InboxNotificationKinds>;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n InboxNotificationOverrides &\n ThreadOverrides &\n CommentOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport interface InboxNotificationThreadProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationThreadData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n}\n\nexport interface InboxNotificationTextMentionProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationTextMentionData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n}\n\nexport interface InboxNotificationInspectorProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n}\n\nexport interface InboxNotificationCustomProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps,\n SlotProp {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationCustomData;\n\n /**\n * The inbox notification's content.\n */\n children: ReactNode;\n\n /**\n * The inbox notification's title.\n */\n title: ReactNode;\n\n /**\n * The inbox notification's aside content.\n * Can be combined with `InboxNotification.Icon` or `InboxNotification.Avatar` to easily follow default styles.\n */\n aside?: ReactNode;\n\n /**\n * Whether to mark the inbox notification as read when clicked.\n */\n markAsReadOnClick?: boolean;\n}\n\nexport type InboxNotificationThreadKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationThreadData;\n};\n\nexport type InboxNotificationTextMentionKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationTextMentionData;\n};\n\nexport type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationCustomData<K>;\n};\n\ninterface InboxNotificationLayoutProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps,\n SlotProp {\n inboxNotification: InboxNotificationData;\n aside?: ReactNode;\n title: ReactNode;\n date: Date | string | number;\n unread?: boolean;\n overrides?: Partial<GlobalOverrides & InboxNotificationOverrides>;\n components?: Partial<GlobalComponents>;\n markAsReadOnClick?: boolean;\n\n /**\n * @internal\n */\n additionalDropdownItemsBefore?: ReactNode;\n\n /**\n * @internal\n */\n additionalDropdownItemsAfter?: ReactNode;\n}\n\nexport type InboxNotificationIconProps = ComponentProps<\"div\">;\n\nexport type InboxNotificationAvatarProps = AvatarProps;\n\nconst InboxNotificationLayout = forwardRef<\n HTMLAnchorElement,\n InboxNotificationLayoutProps\n>(\n (\n {\n inboxNotification,\n children,\n aside,\n title,\n date,\n unread,\n markAsReadOnClick,\n onClick,\n href,\n showActions,\n overrides,\n components,\n className,\n asChild,\n additionalDropdownItemsBefore,\n additionalDropdownItemsAfter,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const { Anchor } = useComponents(components);\n const Component = asChild ? Slot : Anchor;\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const markInboxNotificationAsRead = useMarkInboxNotificationAsRead();\n const deleteInboxNotification = useDeleteInboxNotification();\n\n const handleClick = useCallback(\n (event: ReactMouseEvent<HTMLAnchorElement, MouseEvent>) => {\n onClick?.(event);\n\n const shouldMarkAsReadOnClick = markAsReadOnClick ?? Boolean(href);\n\n if (unread && shouldMarkAsReadOnClick) {\n markInboxNotificationAsRead(inboxNotification.id);\n }\n },\n [\n href,\n inboxNotification.id,\n markAsReadOnClick,\n markInboxNotificationAsRead,\n onClick,\n unread,\n ]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const preventDefaultAndStopPropagation = useCallback(\n (event: SyntheticEvent) => {\n event.preventDefault();\n event.stopPropagation();\n },\n []\n );\n\n const handleMoreClick = useCallback((event: ReactMouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n setMoreActionOpen((open) => !open);\n }, []);\n\n const handleMarkAsRead = useCallback(() => {\n markInboxNotificationAsRead(inboxNotification.id);\n }, [inboxNotification.id, markInboxNotificationAsRead]);\n\n const handleDelete = useCallback(() => {\n deleteInboxNotification(inboxNotification.id);\n }, [inboxNotification.id, deleteInboxNotification]);\n\n return (\n <TooltipProvider>\n <Component\n className={cn(\n \"lb-root lb-inbox-notification\",\n showActions === \"hover\" &&\n \"lb-inbox-notification:show-actions-hover\",\n isMoreActionOpen && \"lb-inbox-notification:action-open\",\n className\n )}\n dir={$.dir}\n data-unread={unread ? \"\" : undefined}\n data-kind={inboxNotification.kind}\n onClick={handleClick}\n href={href}\n {...props}\n ref={forwardedRef}\n >\n {aside && <div className=\"lb-inbox-notification-aside\">{aside}</div>}\n <div className=\"lb-inbox-notification-content\">\n <div className=\"lb-inbox-notification-header\">\n <span className=\"lb-inbox-notification-title\">{title}</span>\n <div className=\"lb-inbox-notification-details\">\n <span className=\"lb-inbox-notification-details-labels\">\n <Timestamp\n locale={$.locale}\n date={date}\n className=\"lb-date lb-inbox-notification-date\"\n />\n {unread && (\n <span\n className=\"lb-inbox-notification-unread-indicator\"\n role=\"presentation\"\n />\n )}\n </span>\n </div>\n {showActions && (\n <div className=\"lb-inbox-notification-actions\">\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={\n <>\n {additionalDropdownItemsBefore}\n {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n icon={<CheckIcon />}\n >\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n icon={<DeleteIcon />}\n >\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\n {additionalDropdownItemsAfter}\n </>\n }\n >\n <Tooltip content={$.INBOX_NOTIFICATION_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-inbox-notification-action\"\n onClick={handleMoreClick}\n onPointerDown={preventDefaultAndStopPropagation}\n onPointerUp={preventDefaultAndStopPropagation}\n aria-label={$.INBOX_NOTIFICATION_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n </div>\n )}\n </div>\n <div className=\"lb-inbox-notification-body\">{children}</div>\n </div>\n </Component>\n </TooltipProvider>\n );\n }\n);\n\nfunction InboxNotificationIcon({\n className,\n ...props\n}: InboxNotificationIconProps) {\n return (\n <div className={cn(\"lb-inbox-notification-icon\", className)} {...props} />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={cn(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\nconst InboxNotificationThread = forwardRef<\n HTMLAnchorElement,\n InboxNotificationThreadProps\n>(\n (\n {\n inboxNotification,\n href,\n showRoomName = true,\n showReactions = true,\n showAttachments = true,\n showActions = \"hover\",\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const thread = useInboxNotificationThread(inboxNotification.id);\n const {\n status: subscriptionStatus,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const currentUserId = useCurrentUserId();\n const { info } = useRoomInfo(inboxNotification.roomId);\n const contents = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\n );\n\n if (contents.comments.length === 0 || contents.userIds.length === 0) {\n return null;\n }\n\n switch (contents.type) {\n case \"comments\": {\n const reversedUserIds = [...contents.userIds].reverse();\n const firstUserId = reversedUserIds[0]!;\n\n const aside = <InboxNotificationAvatar userId={firstUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_COMMENTS_LIST(\n <List\n values={reversedUserIds.map((userId) => (\n <User key={userId} userId={userId} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\n locale={$.locale}\n />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined,\n reversedUserIds.length\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n {contents.comments.map((comment) => (\n <InboxNotificationComment\n key={comment.id}\n comment={comment}\n showHeader={contents.comments.length > 1}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n ))}\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: contents.comments[contents.comments.length - 1]!.id,\n };\n }\n\n case \"mention\": {\n const mentionUserId = contents.userIds[0]!;\n const mentionComment = contents.comments[0]!;\n\n const aside = <InboxNotificationAvatar userId={mentionUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_MENTION(\n <User key={mentionUserId} userId={mentionUserId} />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n <InboxNotificationComment\n key={mentionComment.id}\n comment={mentionComment}\n showHeader={false}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: mentionComment.id,\n };\n }\n\n default:\n return assertNever(\n contents,\n \"Unexpected thread inbox notification type\"\n );\n }\n }, [\n $,\n currentUserId,\n inboxNotification,\n overrides,\n showRoomName,\n showAttachments,\n showReactions,\n thread,\n ]);\n // Use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref\n ? // Set the comment ID as the URL hash.\n generateUrl(resolvedHref, undefined, contents?.commentId)\n : undefined;\n }, [contents?.commentId, href, info?.url]);\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n if (!contents) {\n return null;\n }\n\n const { aside, title, content, date, unread } = contents;\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={date}\n unread={unread}\n overrides={overrides}\n href={resolvedHref}\n showActions={showActions}\n markAsReadOnClick={false}\n additionalDropdownItemsBefore={\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n }\n {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n href,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\n const { info } = useRoomInfo(inboxNotification.roomId);\n // Use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref\n ? (sanitizeUrl(resolvedHref) ?? undefined)\n : undefined;\n }, [href, info?.url]);\n\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={<InboxNotificationAvatar userId={inboxNotification.createdBy} />}\n title={$.INBOX_NOTIFICATION_TEXT_MENTION(\n <User\n key={inboxNotification.createdBy}\n userId={inboxNotification.createdBy}\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n href={resolvedHref}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\nconst InboxNotificationCustom = forwardRef<\n HTMLAnchorElement,\n InboxNotificationCustomProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n title,\n aside,\n children,\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationInspector = forwardRef<\n HTMLAnchorElement,\n InboxNotificationInspectorProps\n>(\n (\n { inboxNotification, showActions = \"hover\", overrides, ...props },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n title={<code>{inboxNotification.id}</code>}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n data-inspector=\"\"\n >\n <CodeBlock\n title=\"Data\"\n code={JSON.stringify(inboxNotification, null, 2)}\n />\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationCustomMissing = forwardRef<\n HTMLAnchorElement,\n Omit<InboxNotificationCustomProps, \"children\" | \"title\" | \"aside\">\n>(({ inboxNotification, ...props }, forwardedRef) => {\n return (\n <InboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n title={\n <>\n Custom notification kind <code>{inboxNotification.kind}</code> is not\n handled\n </>\n }\n aside={\n <InboxNotificationIcon>\n <WarningIcon />\n </InboxNotificationIcon>\n }\n ref={forwardedRef}\n data-missing=\"\"\n >\n {/* TODO: Add link to the docs */}\n Notifications of this kind won’t be displayed in production. Use the{\" \"}\n <code>kinds</code> prop to define how they should be rendered.\n </InboxNotificationCustom>\n );\n});\n\n// Keeps track of which inbox notification kinds it has warned about already.\nconst inboxNotificationKindsWarnings: Set<string> = new Set();\n\n/**\n * Displays a single inbox notification.\n *\n * @example\n * <>\n * {inboxNotifications.map((inboxNotification) => (\n * <InboxNotification\n * key={inboxNotification.id}\n * inboxNotification={inboxNotification}\n * href={`/rooms/${inboxNotification.roomId}`\n * />\n * ))}\n * </>\n */\nexport const InboxNotification = Object.assign(\n forwardRef<HTMLAnchorElement, InboxNotificationProps>(\n ({ inboxNotification, kinds, ...props }, forwardedRef) => {\n switch (inboxNotification.kind) {\n case \"thread\": {\n const ResolvedInboxNotificationThread =\n kinds?.thread ?? InboxNotificationThread;\n\n return (\n <ResolvedInboxNotificationThread\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n case \"textMention\": {\n const ResolvedInboxNotificationTextMention =\n kinds?.textMention ?? InboxNotificationTextMention;\n\n return (\n <ResolvedInboxNotificationTextMention\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n default: {\n const ResolvedInboxNotificationCustom =\n kinds?.[inboxNotification.kind];\n\n if (!ResolvedInboxNotificationCustom) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!inboxNotificationKindsWarnings.has(inboxNotification.kind)) {\n inboxNotificationKindsWarnings.add(inboxNotification.kind);\n // TODO: Add link to the docs\n console.warn(\n `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.`\n );\n }\n\n return (\n <InboxNotificationCustomMissing\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n } else {\n // Don't render anything in production if this inbox notification kind is not defined.\n return null;\n }\n }\n\n return (\n <ResolvedInboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n }\n }\n ),\n {\n /**\n * Displays a thread inbox notification kind.\n */\n Thread: InboxNotificationThread,\n\n /**\n * Displays a text mention inbox notification kind.\n */\n TextMention: InboxNotificationTextMention,\n\n /**\n * Displays a custom inbox notification kind.\n */\n Custom: InboxNotificationCustom,\n\n /**\n * Display the inbox notification's data, which can be useful during development.\n *\n * @example\n * <InboxNotification\n * inboxNotification={inboxNotification}\n * kinds={{\n * $custom: InboxNotification.Inspector,\n * }}\n * />\n */\n Inspector: InboxNotificationInspector,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsPA;AAAgC;AAK5B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AAEA;AACE;AAAgD;AAClD;AACF;AACA;AACE;AACkB;AAClB;AACA;AACA;AACA;AACF;AAGF;AACE;AAAsB;AAGxB;AAAyC;AAErC;AACA;AAAsB;AACxB;AACC;AAGH;AACE;AACA;AACA;AAAiC;AAGnC;AACE;AAAgD;AAGlD;AACE;AAA4C;AAG9C;AACG;AACE;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEJ;AAAU;AAAc;AAA+B;AAAM;AAC7D;AAAc;AACb;AAAC;AAAc;AACb;AAAC;AAAe;AAA+B;AAAM;AACpD;AAAc;AACZ;AAAe;AACd;AAAC;AACW;AACV;AACU;AACZ;AAEG;AACW;AACL;AACP;AAAA;AAEJ;AACF;AAEG;AAAc;AACZ;AACO;AACQ;AACR;AAEJ;AACG;AAAA;AAEE;AACW;AACD;AACQ;AAEd;AAEH;AACH;AACW;AACD;AACS;AAEf;AACL;AACC;AAAA;AACH;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AACM;AACtB;AACF;AACF;AACF;AACF;AAAA;AAEJ;AACC;AAAc;AAA8B;AAAS;AAAA;AACxD;AAAA;AACF;AACF;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAAyD;AAAO;AAErE;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACwD;AACnD;AAGV;AAEA;AAAgC;AAK5B;AACE;AACA;AACe;AACC;AACE;AACJ;AACd;AACG;AAIL;AACA;AACA;AAAM;AACI;AACR;AACA;AAEF;AACA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AACE;AAAO;AAGT;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AAAkB;AAA2B;AAC/C;AACkB;AACgC;AACzC;AACZ;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AAEC;AACuC;AACvC;AACA;AACA;AAEH;AAIL;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC2C;AAC9D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAiC;AAAe;AACjC;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AAEU;AACG;AACZ;AACA;AACA;AACF;AAIJ;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AACS;AAC5B;AACF;AAGE;AAAO;AACL;AACA;AACF;AACJ;AACC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGF;AACE;AAEA;AAGI;AAGN;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACE;AAAsB;AAGxB;AACE;AAAO;AAGT;AAEA;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AAEhB;AACW;AACD;AAKK;AAMR;AACR;AAEE;AACC;AAEJ;AACH;AAGN;AAEA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACA;AACG;AAIL;AACA;AAEA;AACE;AAEA;AAEI;AAGN;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AAE2B;AAC5B;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACM;AACF;AACJ;AACF;AAGN;AAEA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAEJ;AACH;AAGN;AAEA;AAAmC;AAQ/B;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAwB;AAAG;AACX;AACxB;AACA;AACA;AACI;AACC;AACU;AAEd;AACO;AACyC;AACjD;AACF;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAEF;AAAE;AAAA;AAC0B;AAAwB;AAAK;AAAO;AAAA;AAEhE;AAGC;AACc;AACf;AAEG;AACQ;AAEqB;AAAA;AACmC;AACpE;AAAK;AAAK;AAAO;AAAA;AAGxB;AAGA;AAgBO;AAAiC;AACtC;AAEI;AAAgC;AAE5B;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACE;AACE;AACE;AAEA;AAAQ;AACyC;AACjD;AAGF;AACG;AACC;AACI;AACC;AACP;AAIF;AAAO;AACT;AAGF;AACG;AACC;AACI;AACC;AACP;AAEJ;AACF;AACF;AACF;AACA;AAIU;AAKK;AAKL;AAaG;AACL;AACE;AAEZ;;"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { assertNever, console } from '@liveblocks/core';
3
+ import { assertNever, generateUrl, sanitizeUrl, console } from '@liveblocks/core';
4
4
  import { useMarkInboxNotificationAsRead, useDeleteInboxNotification, useInboxNotificationThread, useRoomInfo } from '@liveblocks/react';
5
5
  import { useRoomThreadSubscription } from '@liveblocks/react/_private';
6
6
  import { Slot } from '@radix-ui/react-slot';
@@ -17,7 +17,6 @@ import { useOverrides } from '../overrides.js';
17
17
  import { Timestamp } from '../primitives/Timestamp.js';
18
18
  import { useCurrentUserId } from '../shared.js';
19
19
  import { cn } from '../utils/cn.js';
20
- import { generateURL } from '../utils/url.js';
21
20
  import { Avatar } from './internal/Avatar.js';
22
21
  import { Button } from './internal/Button.js';
23
22
  import { CodeBlock } from './internal/CodeBlock.js';
@@ -335,7 +334,7 @@ const InboxNotificationThread = forwardRef(
335
334
  ]);
336
335
  const resolvedHref = useMemo(() => {
337
336
  const resolvedHref2 = href ?? info?.url;
338
- return resolvedHref2 ? generateURL(resolvedHref2, void 0, contents?.commentId) : void 0;
337
+ return resolvedHref2 ? generateUrl(resolvedHref2, void 0, contents?.commentId) : void 0;
339
338
  }, [contents?.commentId, href, info?.url]);
340
339
  const handleSubscribeChange = useCallback(() => {
341
340
  if (subscriptionStatus === "subscribed") {
@@ -386,7 +385,7 @@ const InboxNotificationTextMention = forwardRef(
386
385
  const { info } = useRoomInfo(inboxNotification.roomId);
387
386
  const resolvedHref = useMemo(() => {
388
387
  const resolvedHref2 = href ?? info?.url;
389
- return resolvedHref2 ? generateURL(resolvedHref2) : void 0;
388
+ return resolvedHref2 ? sanitizeUrl(resolvedHref2) ?? void 0 : void 0;
390
389
  }, [href, info?.url]);
391
390
  const unread = useMemo(() => {
392
391
  return !inboxNotification.readAt || inboxNotification.notifiedAt > inboxNotification.readAt;
@@ -1 +1 @@
1
- {"version":3,"file":"InboxNotification.js","sources":["../../src/components/InboxNotification.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n InboxNotificationCustomData,\n InboxNotificationData,\n InboxNotificationTextMentionData,\n InboxNotificationThreadData,\n KDAD,\n} from \"@liveblocks/core\";\nimport { assertNever, console } from \"@liveblocks/core\";\nimport {\n useDeleteInboxNotification,\n useInboxNotificationThread,\n useMarkInboxNotificationAsRead,\n useRoomInfo,\n} from \"@liveblocks/react\";\nimport { useRoomThreadSubscription } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { TooltipProvider } from \"@radix-ui/react-tooltip\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n ComponentType,\n MouseEvent as ReactMouseEvent,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\nimport { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { WarningIcon } from \"../icons/Warning\";\nimport type {\n CommentOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { generateURL } from \"../utils/url\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport {\n generateInboxNotificationThreadContents,\n INBOX_NOTIFICATION_THREAD_MAX_COMMENTS,\n InboxNotificationComment,\n} from \"./internal/InboxNotificationThread\";\nimport { List } from \"./internal/List\";\nimport { Room } from \"./internal/Room\";\nimport { Tooltip } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ntype ComponentTypeWithRef<\n T extends keyof JSX.IntrinsicElements,\n P,\n> = ComponentType<P & Pick<ComponentProps<T>, \"ref\">>;\n\ntype InboxNotificationKinds<KS extends KDAD = KDAD> = {\n // For some reason, we cannot directly use KDAD in the mapped type line\n // below, because it will result in '{}' rather than picking up the\n // definition from the user-provided 'ActivitiesData'. Might be an internal\n // TS optimization, so we're making it a param to defer the resolution.\n [K in KS]: ComponentTypeWithRef<\"a\", InboxNotificationCustomKindProps<K>>;\n} & {\n thread: ComponentTypeWithRef<\"a\", InboxNotificationThreadKindProps>;\n textMention: ComponentTypeWithRef<\"a\", InboxNotificationTextMentionKindProps>;\n};\n\ninterface InboxNotificationSharedProps {\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n}\n\nexport interface InboxNotificationProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n\n /**\n * Override specific kinds of inbox notifications.\n */\n kinds?: Partial<InboxNotificationKinds>;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n InboxNotificationOverrides &\n ThreadOverrides &\n CommentOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport interface InboxNotificationThreadProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationThreadData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n}\n\nexport interface InboxNotificationTextMentionProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationTextMentionData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n}\n\nexport interface InboxNotificationInspectorProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n}\n\nexport interface InboxNotificationCustomProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps,\n SlotProp {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationCustomData;\n\n /**\n * The inbox notification's content.\n */\n children: ReactNode;\n\n /**\n * The inbox notification's title.\n */\n title: ReactNode;\n\n /**\n * The inbox notification's aside content.\n * Can be combined with `InboxNotification.Icon` or `InboxNotification.Avatar` to easily follow default styles.\n */\n aside?: ReactNode;\n\n /**\n * Whether to mark the inbox notification as read when clicked.\n */\n markAsReadOnClick?: boolean;\n}\n\nexport type InboxNotificationThreadKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationThreadData;\n};\n\nexport type InboxNotificationTextMentionKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationTextMentionData;\n};\n\nexport type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationCustomData<K>;\n};\n\ninterface InboxNotificationLayoutProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps,\n SlotProp {\n inboxNotification: InboxNotificationData;\n aside?: ReactNode;\n title: ReactNode;\n date: Date | string | number;\n unread?: boolean;\n overrides?: Partial<GlobalOverrides & InboxNotificationOverrides>;\n components?: Partial<GlobalComponents>;\n markAsReadOnClick?: boolean;\n\n /**\n * @internal\n */\n additionalDropdownItemsBefore?: ReactNode;\n\n /**\n * @internal\n */\n additionalDropdownItemsAfter?: ReactNode;\n}\n\nexport type InboxNotificationIconProps = ComponentProps<\"div\">;\n\nexport type InboxNotificationAvatarProps = AvatarProps;\n\nconst InboxNotificationLayout = forwardRef<\n HTMLAnchorElement,\n InboxNotificationLayoutProps\n>(\n (\n {\n inboxNotification,\n children,\n aside,\n title,\n date,\n unread,\n markAsReadOnClick,\n onClick,\n href,\n showActions,\n overrides,\n components,\n className,\n asChild,\n additionalDropdownItemsBefore,\n additionalDropdownItemsAfter,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const { Anchor } = useComponents(components);\n const Component = asChild ? Slot : Anchor;\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const markInboxNotificationAsRead = useMarkInboxNotificationAsRead();\n const deleteInboxNotification = useDeleteInboxNotification();\n\n const handleClick = useCallback(\n (event: ReactMouseEvent<HTMLAnchorElement, MouseEvent>) => {\n onClick?.(event);\n\n const shouldMarkAsReadOnClick = markAsReadOnClick ?? Boolean(href);\n\n if (unread && shouldMarkAsReadOnClick) {\n markInboxNotificationAsRead(inboxNotification.id);\n }\n },\n [\n href,\n inboxNotification.id,\n markAsReadOnClick,\n markInboxNotificationAsRead,\n onClick,\n unread,\n ]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const preventDefaultAndStopPropagation = useCallback(\n (event: SyntheticEvent) => {\n event.preventDefault();\n event.stopPropagation();\n },\n []\n );\n\n const handleMoreClick = useCallback((event: ReactMouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n setMoreActionOpen((open) => !open);\n }, []);\n\n const handleMarkAsRead = useCallback(() => {\n markInboxNotificationAsRead(inboxNotification.id);\n }, [inboxNotification.id, markInboxNotificationAsRead]);\n\n const handleDelete = useCallback(() => {\n deleteInboxNotification(inboxNotification.id);\n }, [inboxNotification.id, deleteInboxNotification]);\n\n return (\n <TooltipProvider>\n <Component\n className={cn(\n \"lb-root lb-inbox-notification\",\n showActions === \"hover\" &&\n \"lb-inbox-notification:show-actions-hover\",\n isMoreActionOpen && \"lb-inbox-notification:action-open\",\n className\n )}\n dir={$.dir}\n data-unread={unread ? \"\" : undefined}\n data-kind={inboxNotification.kind}\n onClick={handleClick}\n href={href}\n {...props}\n ref={forwardedRef}\n >\n {aside && <div className=\"lb-inbox-notification-aside\">{aside}</div>}\n <div className=\"lb-inbox-notification-content\">\n <div className=\"lb-inbox-notification-header\">\n <span className=\"lb-inbox-notification-title\">{title}</span>\n <div className=\"lb-inbox-notification-details\">\n <span className=\"lb-inbox-notification-details-labels\">\n <Timestamp\n locale={$.locale}\n date={date}\n className=\"lb-date lb-inbox-notification-date\"\n />\n {unread && (\n <span\n className=\"lb-inbox-notification-unread-indicator\"\n role=\"presentation\"\n />\n )}\n </span>\n </div>\n {showActions && (\n <div className=\"lb-inbox-notification-actions\">\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={\n <>\n {additionalDropdownItemsBefore}\n {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n icon={<CheckIcon />}\n >\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n icon={<DeleteIcon />}\n >\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\n {additionalDropdownItemsAfter}\n </>\n }\n >\n <Tooltip content={$.INBOX_NOTIFICATION_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-inbox-notification-action\"\n onClick={handleMoreClick}\n onPointerDown={preventDefaultAndStopPropagation}\n onPointerUp={preventDefaultAndStopPropagation}\n aria-label={$.INBOX_NOTIFICATION_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n </div>\n )}\n </div>\n <div className=\"lb-inbox-notification-body\">{children}</div>\n </div>\n </Component>\n </TooltipProvider>\n );\n }\n);\n\nfunction InboxNotificationIcon({\n className,\n ...props\n}: InboxNotificationIconProps) {\n return (\n <div className={cn(\"lb-inbox-notification-icon\", className)} {...props} />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={cn(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\nconst InboxNotificationThread = forwardRef<\n HTMLAnchorElement,\n InboxNotificationThreadProps\n>(\n (\n {\n inboxNotification,\n href,\n showRoomName = true,\n showReactions = true,\n showAttachments = true,\n showActions = \"hover\",\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const thread = useInboxNotificationThread(inboxNotification.id);\n const {\n status: subscriptionStatus,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const currentUserId = useCurrentUserId();\n const { info } = useRoomInfo(inboxNotification.roomId);\n const contents = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\n );\n\n if (contents.comments.length === 0 || contents.userIds.length === 0) {\n return null;\n }\n\n switch (contents.type) {\n case \"comments\": {\n const reversedUserIds = [...contents.userIds].reverse();\n const firstUserId = reversedUserIds[0]!;\n\n const aside = <InboxNotificationAvatar userId={firstUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_COMMENTS_LIST(\n <List\n values={reversedUserIds.map((userId) => (\n <User key={userId} userId={userId} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\n locale={$.locale}\n />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined,\n reversedUserIds.length\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n {contents.comments.map((comment) => (\n <InboxNotificationComment\n key={comment.id}\n comment={comment}\n showHeader={contents.comments.length > 1}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n ))}\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: contents.comments[contents.comments.length - 1]!.id,\n };\n }\n\n case \"mention\": {\n const mentionUserId = contents.userIds[0]!;\n const mentionComment = contents.comments[0]!;\n\n const aside = <InboxNotificationAvatar userId={mentionUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_MENTION(\n <User key={mentionUserId} userId={mentionUserId} />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n <InboxNotificationComment\n key={mentionComment.id}\n comment={mentionComment}\n showHeader={false}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: mentionComment.id,\n };\n }\n\n default:\n return assertNever(\n contents,\n \"Unexpected thread inbox notification type\"\n );\n }\n }, [\n $,\n currentUserId,\n inboxNotification,\n overrides,\n showRoomName,\n showAttachments,\n showReactions,\n thread,\n ]);\n // Add the thread ID and comment ID to the `href`.\n // And use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref\n ? generateURL(resolvedHref, undefined, contents?.commentId)\n : undefined;\n }, [contents?.commentId, href, info?.url]);\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n if (!contents) {\n return null;\n }\n\n const { aside, title, content, date, unread } = contents;\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={date}\n unread={unread}\n overrides={overrides}\n href={resolvedHref}\n showActions={showActions}\n markAsReadOnClick={false}\n additionalDropdownItemsBefore={\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n }\n {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n href,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\n const { info } = useRoomInfo(inboxNotification.roomId);\n // Use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref ? generateURL(resolvedHref) : undefined;\n }, [href, info?.url]);\n\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={<InboxNotificationAvatar userId={inboxNotification.createdBy} />}\n title={$.INBOX_NOTIFICATION_TEXT_MENTION(\n <User\n key={inboxNotification.createdBy}\n userId={inboxNotification.createdBy}\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n href={resolvedHref}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\nconst InboxNotificationCustom = forwardRef<\n HTMLAnchorElement,\n InboxNotificationCustomProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n title,\n aside,\n children,\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationInspector = forwardRef<\n HTMLAnchorElement,\n InboxNotificationInspectorProps\n>(\n (\n { inboxNotification, showActions = \"hover\", overrides, ...props },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n title={<code>{inboxNotification.id}</code>}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n data-inspector=\"\"\n >\n <CodeBlock\n title=\"Data\"\n code={JSON.stringify(inboxNotification, null, 2)}\n />\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationCustomMissing = forwardRef<\n HTMLAnchorElement,\n Omit<InboxNotificationCustomProps, \"children\" | \"title\" | \"aside\">\n>(({ inboxNotification, ...props }, forwardedRef) => {\n return (\n <InboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n title={\n <>\n Custom notification kind <code>{inboxNotification.kind}</code> is not\n handled\n </>\n }\n aside={\n <InboxNotificationIcon>\n <WarningIcon />\n </InboxNotificationIcon>\n }\n ref={forwardedRef}\n data-missing=\"\"\n >\n {/* TODO: Add link to the docs */}\n Notifications of this kind won’t be displayed in production. Use the{\" \"}\n <code>kinds</code> prop to define how they should be rendered.\n </InboxNotificationCustom>\n );\n});\n\n// Keeps track of which inbox notification kinds it has warned about already.\nconst inboxNotificationKindsWarnings: Set<string> = new Set();\n\n/**\n * Displays a single inbox notification.\n *\n * @example\n * <>\n * {inboxNotifications.map((inboxNotification) => (\n * <InboxNotification\n * key={inboxNotification.id}\n * inboxNotification={inboxNotification}\n * href={`/rooms/${inboxNotification.roomId}`\n * />\n * ))}\n * </>\n */\nexport const InboxNotification = Object.assign(\n forwardRef<HTMLAnchorElement, InboxNotificationProps>(\n ({ inboxNotification, kinds, ...props }, forwardedRef) => {\n switch (inboxNotification.kind) {\n case \"thread\": {\n const ResolvedInboxNotificationThread =\n kinds?.thread ?? InboxNotificationThread;\n\n return (\n <ResolvedInboxNotificationThread\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n case \"textMention\": {\n const ResolvedInboxNotificationTextMention =\n kinds?.textMention ?? InboxNotificationTextMention;\n\n return (\n <ResolvedInboxNotificationTextMention\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n default: {\n const ResolvedInboxNotificationCustom =\n kinds?.[inboxNotification.kind];\n\n if (!ResolvedInboxNotificationCustom) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!inboxNotificationKindsWarnings.has(inboxNotification.kind)) {\n inboxNotificationKindsWarnings.add(inboxNotification.kind);\n // TODO: Add link to the docs\n console.warn(\n `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.`\n );\n }\n\n return (\n <InboxNotificationCustomMissing\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n } else {\n // Don't render anything in production if this inbox notification kind is not defined.\n return null;\n }\n }\n\n return (\n <ResolvedInboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n }\n }\n ),\n {\n /**\n * Displays a thread inbox notification kind.\n */\n Thread: InboxNotificationThread,\n\n /**\n * Displays a text mention inbox notification kind.\n */\n TextMention: InboxNotificationTextMention,\n\n /**\n * Displays a custom inbox notification kind.\n */\n Custom: InboxNotificationCustom,\n\n /**\n * Display the inbox notification's data, which can be useful during development.\n *\n * @example\n * <InboxNotification\n * inboxNotification={inboxNotification}\n * kinds={{\n * $custom: InboxNotification.Inspector,\n * }}\n * />\n */\n Inspector: InboxNotificationInspector,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkPA;AAAgC;AAK5B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AAEA;AACE;AAAgD;AAClD;AACF;AACA;AACE;AACkB;AAClB;AACA;AACA;AACA;AACF;AAGF;AACE;AAAsB;AAGxB;AAAyC;AAErC;AACA;AAAsB;AACxB;AACC;AAGH;AACE;AACA;AACA;AAAiC;AAGnC;AACE;AAAgD;AAGlD;AACE;AAA4C;AAG9C;AACG;AACE;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEJ;AAAU;AAAc;AAA+B;AAAM;AAC7D;AAAc;AACb;AAAC;AAAc;AACb;AAAC;AAAe;AAA+B;AAAM;AACpD;AAAc;AACZ;AAAe;AACd;AAAC;AACW;AACV;AACU;AACZ;AAEG;AACW;AACL;AACP;AAAA;AAEJ;AACF;AAEG;AAAc;AACZ;AACO;AACQ;AACR;AAEJ;AACG;AAAA;AAEE;AACW;AACD;AACQ;AAEd;AAEH;AACH;AACW;AACD;AACS;AAEf;AACL;AACC;AAAA;AACH;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AACM;AACtB;AACF;AACF;AACF;AACF;AAAA;AAEJ;AACC;AAAc;AAA8B;AAAS;AAAA;AACxD;AAAA;AACF;AACF;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAAyD;AAAO;AAErE;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACwD;AACnD;AAGV;AAEA;AAAgC;AAK5B;AACE;AACA;AACe;AACC;AACE;AACJ;AACd;AACG;AAIL;AACA;AACA;AAAM;AACI;AACR;AACA;AAEF;AACA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AACE;AAAO;AAGT;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AAAkB;AAA2B;AAC/C;AACkB;AACgC;AACzC;AACZ;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AAEC;AACuC;AACvC;AACA;AACA;AAEH;AAIL;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC2C;AAC9D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAiC;AAAe;AACjC;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AAEU;AACG;AACZ;AACA;AACA;AACF;AAIJ;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AACS;AAC5B;AACF;AAGE;AAAO;AACL;AACA;AACF;AACJ;AACC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIF;AACE;AAEA;AAEI;AAGN;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACE;AAAsB;AAGxB;AACE;AAAO;AAGT;AAEA;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AAEhB;AACW;AACD;AAKK;AAMR;AACR;AAEE;AACC;AAEJ;AACH;AAGN;AAEA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACA;AACG;AAIL;AACA;AAEA;AACE;AAEA;AAAkD;AAGpD;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AAE2B;AAC5B;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACM;AACF;AACJ;AACF;AAGN;AAEA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAEJ;AACH;AAGN;AAEA;AAAmC;AAQ/B;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAwB;AAAG;AACX;AACxB;AACA;AACA;AACI;AACC;AACU;AAEd;AACO;AACyC;AACjD;AACF;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAEF;AAAE;AAAA;AAC0B;AAAwB;AAAK;AAAO;AAAA;AAEhE;AAGC;AACc;AACf;AAEG;AACQ;AAEqB;AAAA;AACmC;AACpE;AAAK;AAAK;AAAO;AAAA;AAGxB;AAGA;AAgBO;AAAiC;AACtC;AAEI;AAAgC;AAE5B;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACE;AACE;AACE;AAEA;AAAQ;AACyC;AACjD;AAGF;AACG;AACC;AACI;AACC;AACP;AAIF;AAAO;AACT;AAGF;AACG;AACC;AACI;AACC;AACP;AAEJ;AACF;AACF;AACF;AACA;AAIU;AAKK;AAKL;AAaG;AACL;AACE;AAEZ;;"}
1
+ {"version":3,"file":"InboxNotification.js","sources":["../../src/components/InboxNotification.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n InboxNotificationCustomData,\n InboxNotificationData,\n InboxNotificationTextMentionData,\n InboxNotificationThreadData,\n KDAD,\n} from \"@liveblocks/core\";\nimport {\n assertNever,\n console,\n generateUrl,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport {\n useDeleteInboxNotification,\n useInboxNotificationThread,\n useMarkInboxNotificationAsRead,\n useRoomInfo,\n} from \"@liveblocks/react\";\nimport { useRoomThreadSubscription } from \"@liveblocks/react/_private\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { TooltipProvider } from \"@radix-ui/react-tooltip\";\nimport type {\n ComponentProps,\n ComponentPropsWithoutRef,\n ComponentType,\n MouseEvent as ReactMouseEvent,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\nimport { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\nimport { BellIcon } from \"../icons/Bell\";\nimport { BellCrossedIcon } from \"../icons/BellCrossed\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { WarningIcon } from \"../icons/Warning\";\nimport type {\n CommentOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport { CodeBlock } from \"./internal/CodeBlock\";\nimport { Dropdown, DropdownItem, DropdownTrigger } from \"./internal/Dropdown\";\nimport {\n generateInboxNotificationThreadContents,\n INBOX_NOTIFICATION_THREAD_MAX_COMMENTS,\n InboxNotificationComment,\n} from \"./internal/InboxNotificationThread\";\nimport { List } from \"./internal/List\";\nimport { Room } from \"./internal/Room\";\nimport { Tooltip } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ntype ComponentTypeWithRef<\n T extends keyof JSX.IntrinsicElements,\n P,\n> = ComponentType<P & Pick<ComponentProps<T>, \"ref\">>;\n\ntype InboxNotificationKinds<KS extends KDAD = KDAD> = {\n // For some reason, we cannot directly use KDAD in the mapped type line\n // below, because it will result in '{}' rather than picking up the\n // definition from the user-provided 'ActivitiesData'. Might be an internal\n // TS optimization, so we're making it a param to defer the resolution.\n [K in KS]: ComponentTypeWithRef<\"a\", InboxNotificationCustomKindProps<K>>;\n} & {\n thread: ComponentTypeWithRef<\"a\", InboxNotificationThreadKindProps>;\n textMention: ComponentTypeWithRef<\"a\", InboxNotificationTextMentionKindProps>;\n};\n\ninterface InboxNotificationSharedProps {\n /**\n * How to show or hide the actions.\n */\n showActions?: boolean | \"hover\";\n}\n\nexport interface InboxNotificationProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n\n /**\n * Override specific kinds of inbox notifications.\n */\n kinds?: Partial<InboxNotificationKinds>;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides &\n InboxNotificationOverrides &\n ThreadOverrides &\n CommentOverrides\n >;\n\n /**\n * Override the component's components.\n */\n components?: Partial<GlobalComponents>;\n}\n\nexport interface InboxNotificationThreadProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationThreadData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n\n /**\n * Whether to show reactions.\n */\n showReactions?: boolean;\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n}\n\nexport interface InboxNotificationTextMentionProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationTextMentionData;\n\n /**\n * Whether to show the room name in the title.\n */\n showRoomName?: boolean;\n}\n\nexport interface InboxNotificationInspectorProps\n extends Omit<InboxNotificationProps, \"kinds\" | \"children\">,\n InboxNotificationSharedProps {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationData;\n}\n\nexport interface InboxNotificationCustomProps\n extends Omit<InboxNotificationProps, \"kinds\">,\n InboxNotificationSharedProps,\n SlotProp {\n /**\n * The inbox notification to display.\n */\n inboxNotification: InboxNotificationCustomData;\n\n /**\n * The inbox notification's content.\n */\n children: ReactNode;\n\n /**\n * The inbox notification's title.\n */\n title: ReactNode;\n\n /**\n * The inbox notification's aside content.\n * Can be combined with `InboxNotification.Icon` or `InboxNotification.Avatar` to easily follow default styles.\n */\n aside?: ReactNode;\n\n /**\n * Whether to mark the inbox notification as read when clicked.\n */\n markAsReadOnClick?: boolean;\n}\n\nexport type InboxNotificationThreadKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationThreadData;\n};\n\nexport type InboxNotificationTextMentionKindProps = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationTextMentionData;\n};\n\nexport type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<\n InboxNotificationProps,\n \"kinds\"\n> & {\n inboxNotification: InboxNotificationCustomData<K>;\n};\n\ninterface InboxNotificationLayoutProps\n extends Omit<ComponentPropsWithoutRef<\"a\">, \"title\">,\n InboxNotificationSharedProps,\n SlotProp {\n inboxNotification: InboxNotificationData;\n aside?: ReactNode;\n title: ReactNode;\n date: Date | string | number;\n unread?: boolean;\n overrides?: Partial<GlobalOverrides & InboxNotificationOverrides>;\n components?: Partial<GlobalComponents>;\n markAsReadOnClick?: boolean;\n\n /**\n * @internal\n */\n additionalDropdownItemsBefore?: ReactNode;\n\n /**\n * @internal\n */\n additionalDropdownItemsAfter?: ReactNode;\n}\n\nexport type InboxNotificationIconProps = ComponentProps<\"div\">;\n\nexport type InboxNotificationAvatarProps = AvatarProps;\n\nconst InboxNotificationLayout = forwardRef<\n HTMLAnchorElement,\n InboxNotificationLayoutProps\n>(\n (\n {\n inboxNotification,\n children,\n aside,\n title,\n date,\n unread,\n markAsReadOnClick,\n onClick,\n href,\n showActions,\n overrides,\n components,\n className,\n asChild,\n additionalDropdownItemsBefore,\n additionalDropdownItemsAfter,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const { Anchor } = useComponents(components);\n const Component = asChild ? Slot : Anchor;\n const [isMoreActionOpen, setMoreActionOpen] = useState(false);\n const markInboxNotificationAsRead = useMarkInboxNotificationAsRead();\n const deleteInboxNotification = useDeleteInboxNotification();\n\n const handleClick = useCallback(\n (event: ReactMouseEvent<HTMLAnchorElement, MouseEvent>) => {\n onClick?.(event);\n\n const shouldMarkAsReadOnClick = markAsReadOnClick ?? Boolean(href);\n\n if (unread && shouldMarkAsReadOnClick) {\n markInboxNotificationAsRead(inboxNotification.id);\n }\n },\n [\n href,\n inboxNotification.id,\n markAsReadOnClick,\n markInboxNotificationAsRead,\n onClick,\n unread,\n ]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const preventDefaultAndStopPropagation = useCallback(\n (event: SyntheticEvent) => {\n event.preventDefault();\n event.stopPropagation();\n },\n []\n );\n\n const handleMoreClick = useCallback((event: ReactMouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n setMoreActionOpen((open) => !open);\n }, []);\n\n const handleMarkAsRead = useCallback(() => {\n markInboxNotificationAsRead(inboxNotification.id);\n }, [inboxNotification.id, markInboxNotificationAsRead]);\n\n const handleDelete = useCallback(() => {\n deleteInboxNotification(inboxNotification.id);\n }, [inboxNotification.id, deleteInboxNotification]);\n\n return (\n <TooltipProvider>\n <Component\n className={cn(\n \"lb-root lb-inbox-notification\",\n showActions === \"hover\" &&\n \"lb-inbox-notification:show-actions-hover\",\n isMoreActionOpen && \"lb-inbox-notification:action-open\",\n className\n )}\n dir={$.dir}\n data-unread={unread ? \"\" : undefined}\n data-kind={inboxNotification.kind}\n onClick={handleClick}\n href={href}\n {...props}\n ref={forwardedRef}\n >\n {aside && <div className=\"lb-inbox-notification-aside\">{aside}</div>}\n <div className=\"lb-inbox-notification-content\">\n <div className=\"lb-inbox-notification-header\">\n <span className=\"lb-inbox-notification-title\">{title}</span>\n <div className=\"lb-inbox-notification-details\">\n <span className=\"lb-inbox-notification-details-labels\">\n <Timestamp\n locale={$.locale}\n date={date}\n className=\"lb-date lb-inbox-notification-date\"\n />\n {unread && (\n <span\n className=\"lb-inbox-notification-unread-indicator\"\n role=\"presentation\"\n />\n )}\n </span>\n </div>\n {showActions && (\n <div className=\"lb-inbox-notification-actions\">\n <Dropdown\n open={isMoreActionOpen}\n onOpenChange={setMoreActionOpen}\n align=\"end\"\n content={\n <>\n {additionalDropdownItemsBefore}\n {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n icon={<CheckIcon />}\n >\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n icon={<DeleteIcon />}\n >\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\n {additionalDropdownItemsAfter}\n </>\n }\n >\n <Tooltip content={$.INBOX_NOTIFICATION_MORE}>\n <DropdownTrigger asChild>\n <Button\n className=\"lb-inbox-notification-action\"\n onClick={handleMoreClick}\n onPointerDown={preventDefaultAndStopPropagation}\n onPointerUp={preventDefaultAndStopPropagation}\n aria-label={$.INBOX_NOTIFICATION_MORE}\n icon={<EllipsisIcon />}\n />\n </DropdownTrigger>\n </Tooltip>\n </Dropdown>\n </div>\n )}\n </div>\n <div className=\"lb-inbox-notification-body\">{children}</div>\n </div>\n </Component>\n </TooltipProvider>\n );\n }\n);\n\nfunction InboxNotificationIcon({\n className,\n ...props\n}: InboxNotificationIconProps) {\n return (\n <div className={cn(\"lb-inbox-notification-icon\", className)} {...props} />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={cn(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\nconst InboxNotificationThread = forwardRef<\n HTMLAnchorElement,\n InboxNotificationThreadProps\n>(\n (\n {\n inboxNotification,\n href,\n showRoomName = true,\n showReactions = true,\n showAttachments = true,\n showActions = \"hover\",\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const thread = useInboxNotificationThread(inboxNotification.id);\n const {\n status: subscriptionStatus,\n subscribe,\n unsubscribe,\n } = useRoomThreadSubscription(thread.roomId, thread.id);\n const currentUserId = useCurrentUserId();\n const { info } = useRoomInfo(inboxNotification.roomId);\n const contents = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\n );\n\n if (contents.comments.length === 0 || contents.userIds.length === 0) {\n return null;\n }\n\n switch (contents.type) {\n case \"comments\": {\n const reversedUserIds = [...contents.userIds].reverse();\n const firstUserId = reversedUserIds[0]!;\n\n const aside = <InboxNotificationAvatar userId={firstUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_COMMENTS_LIST(\n <List\n values={reversedUserIds.map((userId) => (\n <User key={userId} userId={userId} replaceSelf />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\n locale={$.locale}\n />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined,\n reversedUserIds.length\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n {contents.comments.map((comment) => (\n <InboxNotificationComment\n key={comment.id}\n comment={comment}\n showHeader={contents.comments.length > 1}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n ))}\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: contents.comments[contents.comments.length - 1]!.id,\n };\n }\n\n case \"mention\": {\n const mentionUserId = contents.userIds[0]!;\n const mentionComment = contents.comments[0]!;\n\n const aside = <InboxNotificationAvatar userId={mentionUserId} />;\n const title = $.INBOX_NOTIFICATION_THREAD_MENTION(\n <User key={mentionUserId} userId={mentionUserId} />,\n showRoomName ? <Room roomId={thread.roomId} /> : undefined\n );\n const content = (\n <div className=\"lb-inbox-notification-comments\">\n <InboxNotificationComment\n key={mentionComment.id}\n comment={mentionComment}\n showHeader={false}\n showAttachments={showAttachments}\n showReactions={showReactions}\n overrides={overrides}\n />\n </div>\n );\n\n return {\n unread: contents.unread,\n date: contents.date,\n aside,\n title,\n content,\n threadId: thread.id,\n commentId: mentionComment.id,\n };\n }\n\n default:\n return assertNever(\n contents,\n \"Unexpected thread inbox notification type\"\n );\n }\n }, [\n $,\n currentUserId,\n inboxNotification,\n overrides,\n showRoomName,\n showAttachments,\n showReactions,\n thread,\n ]);\n // Use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref\n ? // Set the comment ID as the URL hash.\n generateUrl(resolvedHref, undefined, contents?.commentId)\n : undefined;\n }, [contents?.commentId, href, info?.url]);\n\n const handleSubscribeChange = useCallback(() => {\n if (subscriptionStatus === \"subscribed\") {\n unsubscribe();\n } else {\n subscribe();\n }\n }, [subscriptionStatus, subscribe, unsubscribe]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n if (!contents) {\n return null;\n }\n\n const { aside, title, content, date, unread } = contents;\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={date}\n unread={unread}\n overrides={overrides}\n href={resolvedHref}\n showActions={showActions}\n markAsReadOnClick={false}\n additionalDropdownItemsBefore={\n <DropdownItem\n onSelect={handleSubscribeChange}\n onClick={stopPropagation}\n icon={\n subscriptionStatus === \"subscribed\" ? (\n <BellCrossedIcon />\n ) : (\n <BellIcon />\n )\n }\n >\n {subscriptionStatus === \"subscribed\"\n ? $.THREAD_UNSUBSCRIBE\n : $.THREAD_SUBSCRIBE}\n </DropdownItem>\n }\n {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n href,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\n const { info } = useRoomInfo(inboxNotification.roomId);\n // Use URL from `resolveRoomsInfo` if `href` isn't set.\n const resolvedHref = useMemo(() => {\n const resolvedHref = href ?? info?.url;\n\n return resolvedHref\n ? (sanitizeUrl(resolvedHref) ?? undefined)\n : undefined;\n }, [href, info?.url]);\n\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={<InboxNotificationAvatar userId={inboxNotification.createdBy} />}\n title={$.INBOX_NOTIFICATION_TEXT_MENTION(\n <User\n key={inboxNotification.createdBy}\n userId={inboxNotification.createdBy}\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n href={resolvedHref}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\nconst InboxNotificationCustom = forwardRef<\n HTMLAnchorElement,\n InboxNotificationCustomProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n title,\n aside,\n children,\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n aside={aside}\n title={title}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationInspector = forwardRef<\n HTMLAnchorElement,\n InboxNotificationInspectorProps\n>(\n (\n { inboxNotification, showActions = \"hover\", overrides, ...props },\n forwardedRef\n ) => {\n const unread = useMemo(() => {\n return (\n !inboxNotification.readAt ||\n inboxNotification.notifiedAt > inboxNotification.readAt\n );\n }, [inboxNotification.notifiedAt, inboxNotification.readAt]);\n\n return (\n <InboxNotificationLayout\n inboxNotification={inboxNotification}\n title={<code>{inboxNotification.id}</code>}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={forwardedRef}\n data-inspector=\"\"\n >\n <CodeBlock\n title=\"Data\"\n code={JSON.stringify(inboxNotification, null, 2)}\n />\n </InboxNotificationLayout>\n );\n }\n);\n\nconst InboxNotificationCustomMissing = forwardRef<\n HTMLAnchorElement,\n Omit<InboxNotificationCustomProps, \"children\" | \"title\" | \"aside\">\n>(({ inboxNotification, ...props }, forwardedRef) => {\n return (\n <InboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n title={\n <>\n Custom notification kind <code>{inboxNotification.kind}</code> is not\n handled\n </>\n }\n aside={\n <InboxNotificationIcon>\n <WarningIcon />\n </InboxNotificationIcon>\n }\n ref={forwardedRef}\n data-missing=\"\"\n >\n {/* TODO: Add link to the docs */}\n Notifications of this kind won’t be displayed in production. Use the{\" \"}\n <code>kinds</code> prop to define how they should be rendered.\n </InboxNotificationCustom>\n );\n});\n\n// Keeps track of which inbox notification kinds it has warned about already.\nconst inboxNotificationKindsWarnings: Set<string> = new Set();\n\n/**\n * Displays a single inbox notification.\n *\n * @example\n * <>\n * {inboxNotifications.map((inboxNotification) => (\n * <InboxNotification\n * key={inboxNotification.id}\n * inboxNotification={inboxNotification}\n * href={`/rooms/${inboxNotification.roomId}`\n * />\n * ))}\n * </>\n */\nexport const InboxNotification = Object.assign(\n forwardRef<HTMLAnchorElement, InboxNotificationProps>(\n ({ inboxNotification, kinds, ...props }, forwardedRef) => {\n switch (inboxNotification.kind) {\n case \"thread\": {\n const ResolvedInboxNotificationThread =\n kinds?.thread ?? InboxNotificationThread;\n\n return (\n <ResolvedInboxNotificationThread\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n case \"textMention\": {\n const ResolvedInboxNotificationTextMention =\n kinds?.textMention ?? InboxNotificationTextMention;\n\n return (\n <ResolvedInboxNotificationTextMention\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n\n default: {\n const ResolvedInboxNotificationCustom =\n kinds?.[inboxNotification.kind];\n\n if (!ResolvedInboxNotificationCustom) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!inboxNotificationKindsWarnings.has(inboxNotification.kind)) {\n inboxNotificationKindsWarnings.add(inboxNotification.kind);\n // TODO: Add link to the docs\n console.warn(\n `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.`\n );\n }\n\n return (\n <InboxNotificationCustomMissing\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n } else {\n // Don't render anything in production if this inbox notification kind is not defined.\n return null;\n }\n }\n\n return (\n <ResolvedInboxNotificationCustom\n inboxNotification={inboxNotification}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n }\n }\n ),\n {\n /**\n * Displays a thread inbox notification kind.\n */\n Thread: InboxNotificationThread,\n\n /**\n * Displays a text mention inbox notification kind.\n */\n TextMention: InboxNotificationTextMention,\n\n /**\n * Displays a custom inbox notification kind.\n */\n Custom: InboxNotificationCustom,\n\n /**\n * Display the inbox notification's data, which can be useful during development.\n *\n * @example\n * <InboxNotification\n * inboxNotification={inboxNotification}\n * kinds={{\n * $custom: InboxNotification.Inspector,\n * }}\n * />\n */\n Inspector: InboxNotificationInspector,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsPA;AAAgC;AAK5B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AAEA;AACE;AAAgD;AAClD;AACF;AACA;AACE;AACkB;AAClB;AACA;AACA;AACA;AACF;AAGF;AACE;AAAsB;AAGxB;AAAyC;AAErC;AACA;AAAsB;AACxB;AACC;AAGH;AACE;AACA;AACA;AAAiC;AAGnC;AACE;AAAgD;AAGlD;AACE;AAA4C;AAG9C;AACG;AACE;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEJ;AAAU;AAAc;AAA+B;AAAM;AAC7D;AAAc;AACb;AAAC;AAAc;AACb;AAAC;AAAe;AAA+B;AAAM;AACpD;AAAc;AACZ;AAAe;AACd;AAAC;AACW;AACV;AACU;AACZ;AAEG;AACW;AACL;AACP;AAAA;AAEJ;AACF;AAEG;AAAc;AACZ;AACO;AACQ;AACR;AAEJ;AACG;AAAA;AAEE;AACW;AACD;AACQ;AAEd;AAEH;AACH;AACW;AACD;AACS;AAEf;AACL;AACC;AAAA;AACH;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AACM;AACtB;AACF;AACF;AACF;AACF;AAAA;AAEJ;AACC;AAAc;AAA8B;AAAS;AAAA;AACxD;AAAA;AACF;AACF;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAAyD;AAAO;AAErE;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACwD;AACnD;AAGV;AAEA;AAAgC;AAK5B;AACE;AACA;AACe;AACC;AACE;AACJ;AACd;AACG;AAIL;AACA;AACA;AAAM;AACI;AACR;AACA;AAEF;AACA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AACE;AAAO;AAGT;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AAAkB;AAA2B;AAC/C;AACkB;AACgC;AACzC;AACZ;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AAEC;AACuC;AACvC;AACA;AACA;AAEH;AAIL;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC2C;AAC9D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAiC;AAAe;AACjC;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AAEU;AACG;AACZ;AACA;AACA;AACF;AAIJ;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AACS;AAC5B;AACF;AAGE;AAAO;AACL;AACA;AACF;AACJ;AACC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGF;AACE;AAEA;AAGI;AAGN;AACE;AACE;AAAY;AAEZ;AAAU;AACZ;AAGF;AACE;AAAsB;AAGxB;AACE;AAAO;AAGT;AAEA;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AAEhB;AACW;AACD;AAKK;AAMR;AACR;AAEE;AACC;AAEJ;AACH;AAGN;AAEA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACA;AACG;AAIL;AACA;AAEA;AACE;AAEA;AAEI;AAGN;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AAE2B;AAC5B;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACM;AACF;AACJ;AACF;AAGN;AAEA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAEJ;AACH;AAGN;AAEA;AAAmC;AAQ/B;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAwB;AAAG;AACX;AACxB;AACA;AACA;AACI;AACC;AACU;AAEd;AACO;AACyC;AACjD;AACF;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAEF;AAAE;AAAA;AAC0B;AAAwB;AAAK;AAAO;AAAA;AAEhE;AAGC;AACc;AACf;AAEG;AACQ;AAEqB;AAAA;AACmC;AACpE;AAAK;AAAK;AAAO;AAAA;AAGxB;AAGA;AAgBO;AAAiC;AACtC;AAEI;AAAgC;AAE5B;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACG;AACC;AACI;AACC;AACP;AAEJ;AAGE;AAGA;AACE;AACE;AACE;AAEA;AAAQ;AACyC;AACjD;AAGF;AACG;AACC;AACI;AACC;AACP;AAIF;AAAO;AACT;AAGF;AACG;AACC;AACI;AACC;AACP;AAEJ;AACF;AACF;AACF;AACA;AAIU;AAKK;AAKL;AAaG;AACL;AACE;AAEZ;;"}
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
+ var core = require('@liveblocks/core');
4
5
  var reactSlot = require('@radix-ui/react-slot');
5
6
  var react = require('react');
6
7
  var constants = require('../../constants.cjs');
7
- var utils = require('./utils.cjs');
8
8
 
9
9
  const COMMENT_MENTION_NAME = "CommentMention";
10
10
  const COMMENT_BODY_NAME = "CommentBody";
@@ -67,14 +67,19 @@ const CommentBody = react.forwardRef(
67
67
  return /* @__PURE__ */ jsxRuntime.jsx("p", {
68
68
  style: { minHeight: "1lh" },
69
69
  children: block.children.map((inline, index2) => {
70
- if (utils.isCommentBodyMention(inline)) {
70
+ if (core.isCommentBodyMention(inline)) {
71
71
  const { type: _, ...mention } = inline;
72
72
  return mention.id ? /* @__PURE__ */ jsxRuntime.jsx(Mention, {
73
73
  mention
74
74
  }, index2) : null;
75
75
  }
76
- if (utils.isCommentBodyLink(inline)) {
77
- const href = utils.toAbsoluteUrl(inline.url) ?? inline.url;
76
+ if (core.isCommentBodyLink(inline)) {
77
+ const href = core.sanitizeUrl(inline.url);
78
+ if (href === null) {
79
+ return /* @__PURE__ */ jsxRuntime.jsx("span", {
80
+ children: inline.text ?? inline.url
81
+ }, index2);
82
+ }
78
83
  return /* @__PURE__ */ jsxRuntime.jsx(Link, {
79
84
  href,
80
85
  children: inline.text ?? inline.url
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../src/primitives/Comment/index.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport { MENTION_CHARACTER } from \"../../constants\";\nimport type {\n CommentBodyComponents,\n CommentBodyProps,\n CommentLinkProps,\n CommentMentionProps,\n} from \"./types\";\nimport {\n isCommentBodyLink,\n isCommentBodyMention,\n toAbsoluteUrl,\n} from \"./utils\";\n\nconst COMMENT_MENTION_NAME = \"CommentMention\";\nconst COMMENT_BODY_NAME = \"CommentBody\";\nconst COMMENT_LINK_NAME = \"CommentLink\";\n\n/**\n * Displays mentions within `Comment.Body`.\n *\n * @example\n * <Comment.Mention>@{mention.id}</Comment.Mention>\n */\nconst CommentMention = forwardRef<HTMLSpanElement, CommentMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Comment.Body`.\n *\n * @example\n * <Comment.Link href={href}>{children}</Comment.Link>\n */\nconst CommentLink = forwardRef<HTMLAnchorElement, CommentLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultBodyComponents: CommentBodyComponents = {\n Mention: ({ mention }) => {\n return (\n <CommentMention>\n {MENTION_CHARACTER}\n {mention.id}\n </CommentMention>\n );\n },\n Link: ({ href, children }) => {\n return <CommentLink href={href}>{children}</CommentLink>;\n },\n};\n\n/**\n * Displays a comment body.\n *\n * @example\n * <Comment.Body body={comment.body} />\n */\nconst CommentBody = forwardRef<HTMLDivElement, CommentBodyProps>(\n ({ body, components, style, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"div\";\n const { Mention, Link } = useMemo(\n () => ({ ...defaultBodyComponents, ...components }),\n [components]\n );\n\n if (!body || !body?.content) {\n return null;\n }\n\n return (\n <Component\n {...props}\n style={{ whiteSpace: \"break-spaces\", ...style }}\n ref={forwardedRef}\n >\n {body.content.map((block, index) => {\n switch (block.type) {\n case \"paragraph\":\n return (\n <p key={index} style={{ minHeight: \"1lh\" }}>\n {block.children.map((inline, index) => {\n if (isCommentBodyMention(inline)) {\n const { type: _, ...mention } = inline;\n\n return mention.id ? (\n <Mention mention={mention} key={index} />\n ) : null;\n }\n\n if (isCommentBodyLink(inline)) {\n const href = toAbsoluteUrl(inline.url) ?? inline.url;\n\n return (\n <Link href={href} key={index}>\n {inline.text ?? inline.url}\n </Link>\n );\n }\n\n // <code><s><em><strong>text</strong></s></em></code>\n let children: ReactNode = inline.text;\n\n if (inline.bold) {\n children = <strong key={index}>{children}</strong>;\n }\n\n if (inline.italic) {\n children = <em key={index}>{children}</em>;\n }\n\n if (inline.strikethrough) {\n children = <s key={index}>{children}</s>;\n }\n\n if (inline.code) {\n children = <code key={index}>{children}</code>;\n }\n\n return <span key={index}>{children}</span>;\n })}\n </p>\n );\n default:\n return null;\n }\n })}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n CommentBody.displayName = COMMENT_BODY_NAME;\n CommentMention.displayName = COMMENT_MENTION_NAME;\n CommentLink.displayName = COMMENT_LINK_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Comment.*\nexport { CommentBody as Body, CommentLink as Link, CommentMention as Mention };\n"],"names":["forwardRef","Slot","jsx","jsxs","MENTION_CHARACTER","useMemo","index","isCommentBodyMention","isCommentBodyLink","toAbsoluteUrl"],"mappings":";;;;;;;;AAiBA,MAAM,oBAAuB,GAAA,gBAAA,CAAA;AAC7B,MAAM,iBAAoB,GAAA,aAAA,CAAA;AAC1B,MAAM,iBAAoB,GAAA,aAAA,CAAA;AAQ1B,MAAM,cAAiB,GAAAA,gBAAA;AAAA,EACrB,CAAC,EAAE,QAAA,EAAU,OAAY,EAAA,GAAA,KAAA,IAAS,YAAiB,KAAA;AACjD,IAAM,MAAA,SAAA,GAAY,UAAUC,cAAO,GAAA,MAAA,CAAA;AAEnC,IAAA,uBACGC,cAAA,CAAA,SAAA,EAAA;AAAA,MAAW,GAAG,KAAA;AAAA,MAAO,GAAK,EAAA,YAAA;AAAA,MACxB,QAAA;AAAA,KACH,CAAA,CAAA;AAAA,GAEJ;AACF,EAAA;AAQA,MAAM,WAAc,GAAAF,gBAAA;AAAA,EAClB,CAAC,EAAE,QAAA,EAAU,OAAY,EAAA,GAAA,KAAA,IAAS,YAAiB,KAAA;AACjD,IAAM,MAAA,SAAA,GAAY,UAAUC,cAAO,GAAA,GAAA,CAAA;AAEnC,IAAA,uBACGC,cAAA,CAAA,SAAA,EAAA;AAAA,MACC,MAAO,EAAA,QAAA;AAAA,MACP,GAAI,EAAA,8BAAA;AAAA,MACH,GAAG,KAAA;AAAA,MACJ,GAAK,EAAA,YAAA;AAAA,MAEJ,QAAA;AAAA,KACH,CAAA,CAAA;AAAA,GAEJ;AACF,EAAA;AAEA,MAAM,qBAA+C,GAAA;AAAA,EACnD,OAAS,EAAA,CAAC,EAAE,OAAA,EAAc,KAAA;AACxB,IAAA,uBACGC,eAAA,CAAA,cAAA,EAAA;AAAA,MACE,QAAA,EAAA;AAAA,QAAAC,2BAAA;AAAA,QACA,OAAQ,CAAA,EAAA;AAAA,OAAA;AAAA,KACX,CAAA,CAAA;AAAA,GAEJ;AAAA,EACA,IAAM,EAAA,CAAC,EAAE,IAAA,EAAM,UAAe,KAAA;AAC5B,IAAA,uBAAQF,cAAA,CAAA,WAAA,EAAA;AAAA,MAAY,IAAA;AAAA,MAAa,QAAA;AAAA,KAAS,CAAA,CAAA;AAAA,GAC5C;AACF,CAAA,CAAA;AAQA,MAAM,WAAc,GAAAF,gBAAA;AAAA,EAClB,CAAC,EAAE,IAAM,EAAA,UAAA,EAAY,OAAO,OAAY,EAAA,GAAA,KAAA,IAAS,YAAiB,KAAA;AAChE,IAAM,MAAA,SAAA,GAAY,UAAUC,cAAO,GAAA,KAAA,CAAA;AACnC,IAAM,MAAA,EAAE,OAAS,EAAA,IAAA,EAAS,GAAAI,aAAA;AAAA,MACxB,OAAO,EAAE,GAAG,qBAAA,EAAuB,GAAG,UAAW,EAAA,CAAA;AAAA,MACjD,CAAC,UAAU,CAAA;AAAA,KACb,CAAA;AAEA,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,EAAM,OAAS,EAAA;AAC3B,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AAEA,IAAA,uBACGH,cAAA,CAAA,SAAA,EAAA;AAAA,MACE,GAAG,KAAA;AAAA,MACJ,KAAO,EAAA,EAAE,UAAY,EAAA,cAAA,EAAgB,GAAG,KAAM,EAAA;AAAA,MAC9C,GAAK,EAAA,YAAA;AAAA,MAEJ,QAAK,EAAA,IAAA,CAAA,OAAA,CAAQ,GAAI,CAAA,CAAC,OAAO,KAAU,KAAA;AAClC,QAAA,QAAQ,MAAM,IAAM;AAAA,UAClB,KAAK,WAAA;AACH,YAAA,uBACGA,cAAA,CAAA,GAAA,EAAA;AAAA,cAAc,KAAA,EAAO,EAAE,SAAA,EAAW,KAAM,EAAA;AAAA,cACtC,QAAM,EAAA,KAAA,CAAA,QAAA,CAAS,GAAI,CAAA,CAAC,QAAQI,MAAU,KAAA;AACrC,gBAAI,IAAAC,0BAAA,CAAqB,MAAM,CAAG,EAAA;AAChC,kBAAA,MAAM,EAAE,IAAA,EAAM,CAAM,EAAA,GAAA,OAAA,EAAY,GAAA,MAAA,CAAA;AAEhC,kBAAO,OAAA,OAAA,CAAQ,qBACZL,cAAA,CAAA,OAAA,EAAA;AAAA,oBAAQ,OAAA;AAAA,mBAAA,EAAuBI,MAAO,CACrC,GAAA,IAAA,CAAA;AAAA,iBACN;AAEA,gBAAI,IAAAE,uBAAA,CAAkB,MAAM,CAAG,EAAA;AAC7B,kBAAA,MAAM,IAAO,GAAAC,mBAAA,CAAc,MAAO,CAAA,GAAG,KAAK,MAAO,CAAA,GAAA,CAAA;AAEjD,kBAAA,uBACGP,cAAA,CAAA,IAAA,EAAA;AAAA,oBAAK,IAAA;AAAA,oBACH,QAAA,EAAA,MAAA,CAAO,QAAQ,MAAO,CAAA,GAAA;AAAA,mBAAA,EADFI,MAEvB,CAAA,CAAA;AAAA,iBAEJ;AAGA,gBAAA,IAAI,WAAsB,MAAO,CAAA,IAAA,CAAA;AAEjC,gBAAA,IAAI,OAAO,IAAM,EAAA;AACf,kBAAA,QAAA,mBAAYJ,cAAA,CAAA,QAAA,EAAA;AAAA,oBAAoB,QAAA;AAAA,mBAAA,EAARI,MAAiB,CAAA,CAAA;AAAA,iBAC3C;AAEA,gBAAA,IAAI,OAAO,MAAQ,EAAA;AACjB,kBAAA,QAAA,mBAAYJ,cAAA,CAAA,IAAA,EAAA;AAAA,oBAAgB,QAAA;AAAA,mBAAA,EAARI,MAAiB,CAAA,CAAA;AAAA,iBACvC;AAEA,gBAAA,IAAI,OAAO,aAAe,EAAA;AACxB,kBAAA,QAAA,mBAAYJ,cAAA,CAAA,GAAA,EAAA;AAAA,oBAAe,QAAA;AAAA,mBAAA,EAARI,MAAiB,CAAA,CAAA;AAAA,iBACtC;AAEA,gBAAA,IAAI,OAAO,IAAM,EAAA;AACf,kBAAA,QAAA,mBAAYJ,cAAA,CAAA,MAAA,EAAA;AAAA,oBAAkB,QAAA;AAAA,mBAAA,EAARI,MAAiB,CAAA,CAAA;AAAA,iBACzC;AAEA,gBAAA,uBAAQJ,cAAA,CAAA,MAAA,EAAA;AAAA,kBAAkB,QAAA;AAAA,iBAAA,EAARI,MAAiB,CAAA,CAAA;AAAA,eACpC,CAAA;AAAA,aAAA,EAxCK,KAyCR,CAAA,CAAA;AAAA,UAEJ;AACE,YAAO,OAAA,IAAA,CAAA;AAAA,SACX;AAAA,OACD,CAAA;AAAA,KACH,CAAA,CAAA;AAAA,GAEJ;AACF,EAAA;AAEA,IAAI,OAAA,CAAQ,GAAI,CAAA,QAAA,KAAa,YAAc,EAAA;AACzC,EAAA,WAAA,CAAY,WAAc,GAAA,iBAAA,CAAA;AAC1B,EAAA,cAAA,CAAe,WAAc,GAAA,oBAAA,CAAA;AAC7B,EAAA,WAAA,CAAY,WAAc,GAAA,iBAAA,CAAA;AAC5B;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../src/primitives/Comment/index.tsx"],"sourcesContent":["import {\n isCommentBodyLink,\n isCommentBodyMention,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport { MENTION_CHARACTER } from \"../../constants\";\nimport type {\n CommentBodyComponents,\n CommentBodyProps,\n CommentLinkProps,\n CommentMentionProps,\n} from \"./types\";\n\nconst COMMENT_MENTION_NAME = \"CommentMention\";\nconst COMMENT_BODY_NAME = \"CommentBody\";\nconst COMMENT_LINK_NAME = \"CommentLink\";\n\n/**\n * Displays mentions within `Comment.Body`.\n *\n * @example\n * <Comment.Mention>@{mention.id}</Comment.Mention>\n */\nconst CommentMention = forwardRef<HTMLSpanElement, CommentMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Comment.Body`.\n *\n * @example\n * <Comment.Link href={href}>{children}</Comment.Link>\n */\nconst CommentLink = forwardRef<HTMLAnchorElement, CommentLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultBodyComponents: CommentBodyComponents = {\n Mention: ({ mention }) => {\n return (\n <CommentMention>\n {MENTION_CHARACTER}\n {mention.id}\n </CommentMention>\n );\n },\n Link: ({ href, children }) => {\n return <CommentLink href={href}>{children}</CommentLink>;\n },\n};\n\n/**\n * Displays a comment body.\n *\n * @example\n * <Comment.Body body={comment.body} />\n */\nconst CommentBody = forwardRef<HTMLDivElement, CommentBodyProps>(\n ({ body, components, style, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"div\";\n const { Mention, Link } = useMemo(\n () => ({ ...defaultBodyComponents, ...components }),\n [components]\n );\n\n if (!body || !body?.content) {\n return null;\n }\n\n return (\n <Component\n {...props}\n style={{ whiteSpace: \"break-spaces\", ...style }}\n ref={forwardedRef}\n >\n {body.content.map((block, index) => {\n switch (block.type) {\n case \"paragraph\":\n return (\n <p key={index} style={{ minHeight: \"1lh\" }}>\n {block.children.map((inline, index) => {\n if (isCommentBodyMention(inline)) {\n const { type: _, ...mention } = inline;\n\n return mention.id ? (\n <Mention mention={mention} key={index} />\n ) : null;\n }\n\n if (isCommentBodyLink(inline)) {\n const href = sanitizeUrl(inline.url);\n\n // If the URL is invalid, its text/URL are used as plain text.\n if (href === null) {\n return (\n <span key={index}>{inline.text ?? inline.url}</span>\n );\n }\n\n return (\n <Link href={href} key={index}>\n {inline.text ?? inline.url}\n </Link>\n );\n }\n\n // <code><s><em><strong>text</strong></s></em></code>\n let children: ReactNode = inline.text;\n\n if (inline.bold) {\n children = <strong key={index}>{children}</strong>;\n }\n\n if (inline.italic) {\n children = <em key={index}>{children}</em>;\n }\n\n if (inline.strikethrough) {\n children = <s key={index}>{children}</s>;\n }\n\n if (inline.code) {\n children = <code key={index}>{children}</code>;\n }\n\n return <span key={index}>{children}</span>;\n })}\n </p>\n );\n default:\n return null;\n }\n })}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n CommentBody.displayName = COMMENT_BODY_NAME;\n CommentMention.displayName = COMMENT_MENTION_NAME;\n CommentLink.displayName = COMMENT_LINK_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Comment.*\nexport { CommentBody as Body, CommentLink as Link, CommentMention as Mention };\n"],"names":["forwardRef","Slot","jsx","jsxs","MENTION_CHARACTER","useMemo","index","isCommentBodyMention","isCommentBodyLink","sanitizeUrl"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { isCommentBodyMention, isCommentBodyLink, sanitizeUrl } from '@liveblocks/core';
2
3
  import { Slot } from '@radix-ui/react-slot';
3
4
  import { forwardRef, useMemo } from 'react';
4
5
  import { MENTION_CHARACTER } from '../../constants.js';
5
- import { isCommentBodyMention, isCommentBodyLink, toAbsoluteUrl } from './utils.js';
6
6
 
7
7
  const COMMENT_MENTION_NAME = "CommentMention";
8
8
  const COMMENT_BODY_NAME = "CommentBody";
@@ -72,7 +72,12 @@ const CommentBody = forwardRef(
72
72
  }, index2) : null;
73
73
  }
74
74
  if (isCommentBodyLink(inline)) {
75
- const href = toAbsoluteUrl(inline.url) ?? inline.url;
75
+ const href = sanitizeUrl(inline.url);
76
+ if (href === null) {
77
+ return /* @__PURE__ */ jsx("span", {
78
+ children: inline.text ?? inline.url
79
+ }, index2);
80
+ }
76
81
  return /* @__PURE__ */ jsx(Link, {
77
82
  href,
78
83
  children: inline.text ?? inline.url
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/primitives/Comment/index.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport { MENTION_CHARACTER } from \"../../constants\";\nimport type {\n CommentBodyComponents,\n CommentBodyProps,\n CommentLinkProps,\n CommentMentionProps,\n} from \"./types\";\nimport {\n isCommentBodyLink,\n isCommentBodyMention,\n toAbsoluteUrl,\n} from \"./utils\";\n\nconst COMMENT_MENTION_NAME = \"CommentMention\";\nconst COMMENT_BODY_NAME = \"CommentBody\";\nconst COMMENT_LINK_NAME = \"CommentLink\";\n\n/**\n * Displays mentions within `Comment.Body`.\n *\n * @example\n * <Comment.Mention>@{mention.id}</Comment.Mention>\n */\nconst CommentMention = forwardRef<HTMLSpanElement, CommentMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Comment.Body`.\n *\n * @example\n * <Comment.Link href={href}>{children}</Comment.Link>\n */\nconst CommentLink = forwardRef<HTMLAnchorElement, CommentLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultBodyComponents: CommentBodyComponents = {\n Mention: ({ mention }) => {\n return (\n <CommentMention>\n {MENTION_CHARACTER}\n {mention.id}\n </CommentMention>\n );\n },\n Link: ({ href, children }) => {\n return <CommentLink href={href}>{children}</CommentLink>;\n },\n};\n\n/**\n * Displays a comment body.\n *\n * @example\n * <Comment.Body body={comment.body} />\n */\nconst CommentBody = forwardRef<HTMLDivElement, CommentBodyProps>(\n ({ body, components, style, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"div\";\n const { Mention, Link } = useMemo(\n () => ({ ...defaultBodyComponents, ...components }),\n [components]\n );\n\n if (!body || !body?.content) {\n return null;\n }\n\n return (\n <Component\n {...props}\n style={{ whiteSpace: \"break-spaces\", ...style }}\n ref={forwardedRef}\n >\n {body.content.map((block, index) => {\n switch (block.type) {\n case \"paragraph\":\n return (\n <p key={index} style={{ minHeight: \"1lh\" }}>\n {block.children.map((inline, index) => {\n if (isCommentBodyMention(inline)) {\n const { type: _, ...mention } = inline;\n\n return mention.id ? (\n <Mention mention={mention} key={index} />\n ) : null;\n }\n\n if (isCommentBodyLink(inline)) {\n const href = toAbsoluteUrl(inline.url) ?? inline.url;\n\n return (\n <Link href={href} key={index}>\n {inline.text ?? inline.url}\n </Link>\n );\n }\n\n // <code><s><em><strong>text</strong></s></em></code>\n let children: ReactNode = inline.text;\n\n if (inline.bold) {\n children = <strong key={index}>{children}</strong>;\n }\n\n if (inline.italic) {\n children = <em key={index}>{children}</em>;\n }\n\n if (inline.strikethrough) {\n children = <s key={index}>{children}</s>;\n }\n\n if (inline.code) {\n children = <code key={index}>{children}</code>;\n }\n\n return <span key={index}>{children}</span>;\n })}\n </p>\n );\n default:\n return null;\n }\n })}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n CommentBody.displayName = COMMENT_BODY_NAME;\n CommentMention.displayName = COMMENT_MENTION_NAME;\n CommentLink.displayName = COMMENT_LINK_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Comment.*\nexport { CommentBody as Body, CommentLink as Link, CommentMention as Mention };\n"],"names":["index"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/primitives/Comment/index.tsx"],"sourcesContent":["import {\n isCommentBodyLink,\n isCommentBodyMention,\n sanitizeUrl,\n} from \"@liveblocks/core\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport { MENTION_CHARACTER } from \"../../constants\";\nimport type {\n CommentBodyComponents,\n CommentBodyProps,\n CommentLinkProps,\n CommentMentionProps,\n} from \"./types\";\n\nconst COMMENT_MENTION_NAME = \"CommentMention\";\nconst COMMENT_BODY_NAME = \"CommentBody\";\nconst COMMENT_LINK_NAME = \"CommentLink\";\n\n/**\n * Displays mentions within `Comment.Body`.\n *\n * @example\n * <Comment.Mention>@{mention.id}</Comment.Mention>\n */\nconst CommentMention = forwardRef<HTMLSpanElement, CommentMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Comment.Body`.\n *\n * @example\n * <Comment.Link href={href}>{children}</Comment.Link>\n */\nconst CommentLink = forwardRef<HTMLAnchorElement, CommentLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultBodyComponents: CommentBodyComponents = {\n Mention: ({ mention }) => {\n return (\n <CommentMention>\n {MENTION_CHARACTER}\n {mention.id}\n </CommentMention>\n );\n },\n Link: ({ href, children }) => {\n return <CommentLink href={href}>{children}</CommentLink>;\n },\n};\n\n/**\n * Displays a comment body.\n *\n * @example\n * <Comment.Body body={comment.body} />\n */\nconst CommentBody = forwardRef<HTMLDivElement, CommentBodyProps>(\n ({ body, components, style, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"div\";\n const { Mention, Link } = useMemo(\n () => ({ ...defaultBodyComponents, ...components }),\n [components]\n );\n\n if (!body || !body?.content) {\n return null;\n }\n\n return (\n <Component\n {...props}\n style={{ whiteSpace: \"break-spaces\", ...style }}\n ref={forwardedRef}\n >\n {body.content.map((block, index) => {\n switch (block.type) {\n case \"paragraph\":\n return (\n <p key={index} style={{ minHeight: \"1lh\" }}>\n {block.children.map((inline, index) => {\n if (isCommentBodyMention(inline)) {\n const { type: _, ...mention } = inline;\n\n return mention.id ? (\n <Mention mention={mention} key={index} />\n ) : null;\n }\n\n if (isCommentBodyLink(inline)) {\n const href = sanitizeUrl(inline.url);\n\n // If the URL is invalid, its text/URL are used as plain text.\n if (href === null) {\n return (\n <span key={index}>{inline.text ?? inline.url}</span>\n );\n }\n\n return (\n <Link href={href} key={index}>\n {inline.text ?? inline.url}\n </Link>\n );\n }\n\n // <code><s><em><strong>text</strong></s></em></code>\n let children: ReactNode = inline.text;\n\n if (inline.bold) {\n children = <strong key={index}>{children}</strong>;\n }\n\n if (inline.italic) {\n children = <em key={index}>{children}</em>;\n }\n\n if (inline.strikethrough) {\n children = <s key={index}>{children}</s>;\n }\n\n if (inline.code) {\n children = <code key={index}>{children}</code>;\n }\n\n return <span key={index}>{children}</span>;\n })}\n </p>\n );\n default:\n return null;\n }\n })}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n CommentBody.displayName = COMMENT_BODY_NAME;\n CommentMention.displayName = COMMENT_MENTION_NAME;\n CommentLink.displayName = COMMENT_LINK_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Comment.*\nexport { CommentBody as Body, CommentLink as Link, CommentMention as Mention };\n"],"names":["index"],"mappings":"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"}