@liveblocks/react-ui 2.5.2 → 2.7.0-beta2

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 (104) hide show
  1. package/dist/components/Comment.js +61 -3
  2. package/dist/components/Comment.js.map +1 -1
  3. package/dist/components/Comment.mjs +62 -5
  4. package/dist/components/Comment.mjs.map +1 -1
  5. package/dist/components/Composer.js +217 -101
  6. package/dist/components/Composer.js.map +1 -1
  7. package/dist/components/Composer.mjs +220 -104
  8. package/dist/components/Composer.mjs.map +1 -1
  9. package/dist/components/InboxNotification.js +20 -4
  10. package/dist/components/InboxNotification.js.map +1 -1
  11. package/dist/components/InboxNotification.mjs +20 -4
  12. package/dist/components/InboxNotification.mjs.map +1 -1
  13. package/dist/components/Thread.js +2 -0
  14. package/dist/components/Thread.js.map +1 -1
  15. package/dist/components/Thread.mjs +2 -0
  16. package/dist/components/Thread.mjs.map +1 -1
  17. package/dist/components/internal/Attachment.js +207 -0
  18. package/dist/components/internal/Attachment.js.map +1 -0
  19. package/dist/components/internal/Attachment.mjs +205 -0
  20. package/dist/components/internal/Attachment.mjs.map +1 -0
  21. package/dist/components/internal/InboxNotificationThread.js +10 -2
  22. package/dist/components/internal/InboxNotificationThread.js.map +1 -1
  23. package/dist/components/internal/InboxNotificationThread.mjs +11 -3
  24. package/dist/components/internal/InboxNotificationThread.mjs.map +1 -1
  25. package/dist/icons/Attachment.js +15 -0
  26. package/dist/icons/Attachment.js.map +1 -0
  27. package/dist/icons/Attachment.mjs +13 -0
  28. package/dist/icons/Attachment.mjs.map +1 -0
  29. package/dist/icons/Spinner.js +3 -9
  30. package/dist/icons/Spinner.js.map +1 -1
  31. package/dist/icons/Spinner.mjs +4 -10
  32. package/dist/icons/Spinner.mjs.map +1 -1
  33. package/dist/icons/{Missing.js → Warning.js} +3 -3
  34. package/dist/icons/{Missing.js.map → Warning.js.map} +1 -1
  35. package/dist/icons/{Missing.mjs → Warning.mjs} +3 -3
  36. package/dist/icons/{Missing.mjs.map → Warning.mjs.map} +1 -1
  37. package/dist/index.d.mts +70 -4
  38. package/dist/index.d.ts +70 -4
  39. package/dist/index.js.map +1 -1
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/overrides.js +5 -0
  42. package/dist/overrides.js.map +1 -1
  43. package/dist/overrides.mjs +5 -0
  44. package/dist/overrides.mjs.map +1 -1
  45. package/dist/primitives/Composer/contexts.js +17 -3
  46. package/dist/primitives/Composer/contexts.js.map +1 -1
  47. package/dist/primitives/Composer/contexts.mjs +15 -4
  48. package/dist/primitives/Composer/contexts.mjs.map +1 -1
  49. package/dist/primitives/Composer/index.js +185 -26
  50. package/dist/primitives/Composer/index.js.map +1 -1
  51. package/dist/primitives/Composer/index.mjs +188 -31
  52. package/dist/primitives/Composer/index.mjs.map +1 -1
  53. package/dist/primitives/Composer/utils.js +224 -0
  54. package/dist/primitives/Composer/utils.js.map +1 -1
  55. package/dist/primitives/Composer/utils.mjs +222 -1
  56. package/dist/primitives/Composer/utils.mjs.map +1 -1
  57. package/dist/primitives/EmojiPicker/utils.js +2 -2
  58. package/dist/primitives/EmojiPicker/utils.js.map +1 -1
  59. package/dist/primitives/EmojiPicker/utils.mjs +1 -1
  60. package/dist/primitives/EmojiPicker/utils.mjs.map +1 -1
  61. package/dist/primitives/FileSize.js +33 -0
  62. package/dist/primitives/FileSize.js.map +1 -0
  63. package/dist/primitives/FileSize.mjs +31 -0
  64. package/dist/primitives/FileSize.mjs.map +1 -0
  65. package/dist/primitives/index.d.mts +83 -3
  66. package/dist/primitives/index.d.ts +83 -3
  67. package/dist/primitives/index.js +4 -0
  68. package/dist/primitives/index.js.map +1 -1
  69. package/dist/primitives/index.mjs +2 -0
  70. package/dist/primitives/index.mjs.map +1 -1
  71. package/dist/utils/download.js +14 -0
  72. package/dist/utils/download.js.map +1 -0
  73. package/dist/utils/download.mjs +12 -0
  74. package/dist/utils/download.mjs.map +1 -0
  75. package/dist/utils/format-file-size.js +45 -0
  76. package/dist/utils/format-file-size.js.map +1 -0
  77. package/dist/utils/format-file-size.mjs +43 -0
  78. package/dist/utils/format-file-size.mjs.map +1 -0
  79. package/dist/utils/intl.js +6 -0
  80. package/dist/utils/intl.js.map +1 -1
  81. package/dist/utils/intl.mjs +6 -1
  82. package/dist/utils/intl.mjs.map +1 -1
  83. package/dist/utils/use-initial.js +2 -1
  84. package/dist/utils/use-initial.js.map +1 -1
  85. package/dist/utils/use-initial.mjs +3 -2
  86. package/dist/utils/use-initial.mjs.map +1 -1
  87. package/dist/version.js +1 -1
  88. package/dist/version.js.map +1 -1
  89. package/dist/version.mjs +1 -1
  90. package/dist/version.mjs.map +1 -1
  91. package/package.json +4 -4
  92. package/src/styles/dark/index.css +1 -0
  93. package/src/styles/index.css +296 -62
  94. package/src/styles/utils.css +44 -0
  95. package/styles/dark/attributes.css +1 -1
  96. package/styles/dark/attributes.css.map +1 -1
  97. package/styles/dark/media-query.css +1 -1
  98. package/styles/dark/media-query.css.map +1 -1
  99. package/styles.css +1 -1
  100. package/styles.css.map +1 -1
  101. package/dist/utils/chunk.js +0 -12
  102. package/dist/utils/chunk.js.map +0 -1
  103. package/dist/utils/chunk.mjs +0 -10
  104. package/dist/utils/chunk.mjs.map +0 -1
@@ -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 { 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 React, { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { MissingIcon } from \"../icons/Missing\";\nimport type {\n CommentOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { generateURL } from \"../utils/url\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\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 & InboxNotificationOverrides & 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\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 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\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 ...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={classNames(\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-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 {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n >\n <CheckIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n >\n <DeleteIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\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 >\n <EllipsisIcon className=\"lb-button-icon\" />\n </Button>\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\n className={classNames(\"lb-inbox-notification-icon\", className)}\n {...props}\n />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={classNames(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Displays a thread inbox notification.\n */\nconst InboxNotificationThread = forwardRef<\n HTMLAnchorElement,\n InboxNotificationThreadProps\n>(\n (\n {\n inboxNotification,\n href,\n showRoomName = true,\n showActions = \"hover\",\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const thread = useInboxNotificationThread(inboxNotification.id);\n const currentUserId = useCurrentUserId();\n // TODO: If you provide `href` (or plan to), we shouldn't run this hook. We should find a way to conditionally run it.\n // Because of batching and the fact that the same hook will be called within <Room /> in the notification's title,\n // it's not a big deal, the only scenario where it would be superfluous would be if the user provides their own\n // `href` AND disables room names in the title via `showRoomName={false}`.\n const { info } = useRoomInfo(inboxNotification.roomId);\n const { unread, date, aside, title, content, commentId } = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\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, index) => (\n <User\n key={userId}\n userId={userId}\n capitalize={index === 0}\n replaceSelf\n />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\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 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} capitalize />,\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 />\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 }, [$, currentUserId, inboxNotification, overrides, showRoomName, thread]);\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, commentId)\n : undefined;\n }, [commentId, href, info?.url]);\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 {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\n/**\n * Displays a text mention notification kind.\n */\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\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 capitalize\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\n/**\n * Displays a custom notification kind.\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 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 <MissingIcon />\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 Thread: InboxNotificationThread,\n TextMention: InboxNotificationTextMention,\n Custom: InboxNotificationCustom,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6MA;AAAgC;AAK5B;AACE;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;AAEK;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEM;AAAc;AACxB;AAAc;AACZ;AAAc;AACZ;AAAe;AACf;AAAc;AACZ;AAAe;AACb;AACW;AACV;AACU;AAGT;AACW;AACL;AAMV;AAAc;AACZ;AACO;AACQ;AACR;AAIC;AACW;AACD;AAER;AAAoB;AAIxB;AACW;AACD;AAER;AAAqB;AAG1B;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AAEb;AAAuB;AAQrC;AAAc;AAGrB;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAC8D;AACzD;AAGV;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACgE;AAC3D;AAGV;AAKA;AAAgC;AAK5B;AACE;AACA;AACe;AACD;AACd;AACG;AAIL;AACA;AACA;AAKA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AACM;AACL;AACsB;AACX;AAEd;AACkB;AACgC;AACrD;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AACc;AACb;AACuC;AACvC;AAMR;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC0C;AAC7D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAU;AAAuB;AAAyB;AAAC;AAC5C;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AACqB;AACX;AACG;AAKlB;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AACS;AAC5B;AACF;AAGE;AAAO;AACL;AACA;AACF;AACJ;AAIF;AACE;AAEA;AAEI;AAGN;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AACf;AACC;AAGP;AAGN;AAKA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACG;AAIL;AAEA;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AACwB;AACG;AAChB;AACZ;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACI;AACJ;AACF;AAGN;AAKA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAGP;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAKF;AAKA;AAEG;AACQ;AAOnB;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;AACU;AACK;AACL;AACF;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 { assertNever, console } from \"@liveblocks/core\";\nimport {\n useDeleteInboxNotification,\n useInboxNotificationThread,\n useMarkInboxNotificationAsRead,\n useRoomInfo,\n} from \"@liveblocks/react\";\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 React, { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\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} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { generateURL } from \"../utils/url\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\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 & InboxNotificationOverrides & 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 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\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 ...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={classNames(\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-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 {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n >\n <CheckIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n >\n <DeleteIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\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 >\n <EllipsisIcon className=\"lb-button-icon\" />\n </Button>\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\n className={classNames(\"lb-inbox-notification-icon\", className)}\n {...props}\n />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={classNames(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Displays a thread inbox notification.\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 currentUserId = useCurrentUserId();\n // TODO: If you provide `href` (or plan to), we shouldn't run this hook. We should find a way to conditionally run it.\n // Because of batching and the fact that the same hook will be called within <Room /> in the notification's title,\n // it's not a big deal, the only scenario where it would be superfluous would be if the user provides their own\n // `href` AND disables room names in the title via `showRoomName={false}`.\n const { info } = useRoomInfo(inboxNotification.roomId);\n const { unread, date, aside, title, content, commentId } = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\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, index) => (\n <User\n key={userId}\n userId={userId}\n capitalize={index === 0}\n replaceSelf\n />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\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} capitalize />,\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, commentId)\n : undefined;\n }, [commentId, href, info?.url]);\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 {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\n/**\n * Displays a text mention notification kind.\n */\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\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 capitalize\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\n/**\n * Displays a custom notification kind.\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 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 Thread: InboxNotificationThread,\n TextMention: InboxNotificationTextMention,\n Custom: InboxNotificationCustom,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuNA;AAAgC;AAK5B;AACE;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;AAEK;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEM;AAAc;AACxB;AAAc;AACZ;AAAc;AACZ;AAAe;AACf;AAAc;AACZ;AAAe;AACb;AACW;AACV;AACU;AAGT;AACW;AACL;AAMV;AAAc;AACZ;AACO;AACQ;AACR;AAIC;AACW;AACD;AAER;AAAoB;AAIxB;AACW;AACD;AAER;AAAqB;AAG1B;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AAEb;AAAuB;AAQrC;AAAc;AAGrB;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAC8D;AACzD;AAGV;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACgE;AAC3D;AAGV;AAKA;AAAgC;AAK5B;AACE;AACA;AACe;AACC;AACE;AACJ;AACd;AACG;AAIL;AACA;AACA;AAKA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AACM;AACL;AACsB;AACX;AAEd;AACkB;AACgC;AACrD;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AACc;AACb;AACuC;AACvC;AACA;AACA;AAMR;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC0C;AAC7D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAU;AAAuB;AAAyB;AAAC;AAC5C;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AACqB;AACX;AACG;AACZ;AACA;AACA;AAKN;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;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AACf;AACC;AAGP;AAGN;AAKA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACG;AAIL;AAEA;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AACwB;AACG;AAChB;AACZ;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACI;AACJ;AACF;AAGN;AAKA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAGP;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAKF;AAKA;AAEG;AACQ;AAOnB;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;AACU;AACK;AACL;AACF;AACE;AAEZ;;"}
@@ -8,7 +8,7 @@ import { useComponents } from '../components.mjs';
8
8
  import { CheckIcon } from '../icons/Check.mjs';
9
9
  import { DeleteIcon } from '../icons/Delete.mjs';
10
10
  import { EllipsisIcon } from '../icons/Ellipsis.mjs';
11
- import { MissingIcon } from '../icons/Missing.mjs';
11
+ import { WarningIcon } from '../icons/Warning.mjs';
12
12
  import { useOverrides } from '../overrides.mjs';
13
13
  import { Timestamp } from '../primitives/Timestamp.mjs';
14
14
  import { useCurrentUserId } from '../shared.mjs';
@@ -176,6 +176,8 @@ const InboxNotificationThread = forwardRef(
176
176
  inboxNotification,
177
177
  href,
178
178
  showRoomName = true,
179
+ showReactions = true,
180
+ showAttachments = true,
179
181
  showActions = "hover",
180
182
  overrides,
181
183
  ...props
@@ -219,6 +221,8 @@ const InboxNotificationThread = forwardRef(
219
221
  key: comment.id,
220
222
  comment,
221
223
  showHeader: contents.comments.length > 1,
224
+ showAttachments,
225
+ showReactions,
222
226
  overrides
223
227
  })));
224
228
  return {
@@ -252,7 +256,10 @@ const InboxNotificationThread = forwardRef(
252
256
  }, /* @__PURE__ */ React__default.createElement(InboxNotificationComment, {
253
257
  key: mentionComment.id,
254
258
  comment: mentionComment,
255
- showHeader: false
259
+ showHeader: false,
260
+ showAttachments,
261
+ showReactions,
262
+ overrides
256
263
  }));
257
264
  return {
258
265
  unread: contents.unread,
@@ -270,7 +277,16 @@ const InboxNotificationThread = forwardRef(
270
277
  "Unexpected thread inbox notification type"
271
278
  );
272
279
  }
273
- }, [$, currentUserId, inboxNotification, overrides, showRoomName, thread]);
280
+ }, [
281
+ $,
282
+ currentUserId,
283
+ inboxNotification,
284
+ overrides,
285
+ showRoomName,
286
+ showAttachments,
287
+ showReactions,
288
+ thread
289
+ ]);
274
290
  const resolvedHref = useMemo(() => {
275
291
  const resolvedHref2 = href ?? info?.url;
276
292
  return resolvedHref2 ? generateURL(resolvedHref2, void 0, commentId) : void 0;
@@ -357,7 +373,7 @@ const InboxNotificationCustomMissing = forwardRef(({ inboxNotification, ...props
357
373
  inboxNotification,
358
374
  ...props,
359
375
  title: /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, "Custom notification kind ", /* @__PURE__ */ React__default.createElement("code", null, inboxNotification.kind), " is not handled"),
360
- aside: /* @__PURE__ */ React__default.createElement(InboxNotificationIcon, null, /* @__PURE__ */ React__default.createElement(MissingIcon, null)),
376
+ aside: /* @__PURE__ */ React__default.createElement(InboxNotificationIcon, null, /* @__PURE__ */ React__default.createElement(WarningIcon, null)),
361
377
  ref: forwardedRef,
362
378
  "data-missing": ""
363
379
  }, "Notifications of this kind won\u2019t be displayed in production. Use the", " ", /* @__PURE__ */ React__default.createElement("code", null, "kinds"), " prop to define how they should be rendered.");
@@ -1 +1 @@
1
- {"version":3,"file":"InboxNotification.mjs","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 { 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 React, { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\nimport { CheckIcon } from \"../icons/Check\";\nimport { DeleteIcon } from \"../icons/Delete\";\nimport { EllipsisIcon } from \"../icons/Ellipsis\";\nimport { MissingIcon } from \"../icons/Missing\";\nimport type {\n CommentOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { generateURL } from \"../utils/url\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\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 & InboxNotificationOverrides & 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\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 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\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 ...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={classNames(\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-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 {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n >\n <CheckIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n >\n <DeleteIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\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 >\n <EllipsisIcon className=\"lb-button-icon\" />\n </Button>\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\n className={classNames(\"lb-inbox-notification-icon\", className)}\n {...props}\n />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={classNames(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Displays a thread inbox notification.\n */\nconst InboxNotificationThread = forwardRef<\n HTMLAnchorElement,\n InboxNotificationThreadProps\n>(\n (\n {\n inboxNotification,\n href,\n showRoomName = true,\n showActions = \"hover\",\n overrides,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const thread = useInboxNotificationThread(inboxNotification.id);\n const currentUserId = useCurrentUserId();\n // TODO: If you provide `href` (or plan to), we shouldn't run this hook. We should find a way to conditionally run it.\n // Because of batching and the fact that the same hook will be called within <Room /> in the notification's title,\n // it's not a big deal, the only scenario where it would be superfluous would be if the user provides their own\n // `href` AND disables room names in the title via `showRoomName={false}`.\n const { info } = useRoomInfo(inboxNotification.roomId);\n const { unread, date, aside, title, content, commentId } = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\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, index) => (\n <User\n key={userId}\n userId={userId}\n capitalize={index === 0}\n replaceSelf\n />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\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 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} capitalize />,\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 />\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 }, [$, currentUserId, inboxNotification, overrides, showRoomName, thread]);\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, commentId)\n : undefined;\n }, [commentId, href, info?.url]);\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 {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\n/**\n * Displays a text mention notification kind.\n */\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\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 capitalize\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\n/**\n * Displays a custom notification kind.\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 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 <MissingIcon />\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 Thread: InboxNotificationThread,\n TextMention: InboxNotificationTextMention,\n Custom: InboxNotificationCustom,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6MA;AAAgC;AAK5B;AACE;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;AAEK;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEM;AAAc;AACxB;AAAc;AACZ;AAAc;AACZ;AAAe;AACf;AAAc;AACZ;AAAe;AACb;AACW;AACV;AACU;AAGT;AACW;AACL;AAMV;AAAc;AACZ;AACO;AACQ;AACR;AAIC;AACW;AACD;AAER;AAAoB;AAIxB;AACW;AACD;AAER;AAAqB;AAG1B;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AAEb;AAAuB;AAQrC;AAAc;AAGrB;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAC8D;AACzD;AAGV;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACgE;AAC3D;AAGV;AAKA;AAAgC;AAK5B;AACE;AACA;AACe;AACD;AACd;AACG;AAIL;AACA;AACA;AAKA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AACM;AACL;AACsB;AACX;AAEd;AACkB;AACgC;AACrD;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AACc;AACb;AACuC;AACvC;AAMR;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC0C;AAC7D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAU;AAAuB;AAAyB;AAAC;AAC5C;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AACqB;AACX;AACG;AAKlB;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AACS;AAC5B;AACF;AAGE;AAAO;AACL;AACA;AACF;AACJ;AAIF;AACE;AAEA;AAEI;AAGN;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AACf;AACC;AAGP;AAGN;AAKA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACG;AAIL;AAEA;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AACwB;AACG;AAChB;AACZ;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACI;AACJ;AACF;AAGN;AAKA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAGP;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAKF;AAKA;AAEG;AACQ;AAOnB;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;AACU;AACK;AACL;AACF;AACE;AAEZ;;"}
1
+ {"version":3,"file":"InboxNotification.mjs","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 { 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 React, { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport type { GlobalComponents } from \"../components\";\nimport { useComponents } from \"../components\";\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} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { Timestamp } from \"../primitives/Timestamp\";\nimport { useCurrentUserId } from \"../shared\";\nimport type { SlotProp } from \"../types\";\nimport { classNames } from \"../utils/class-names\";\nimport { generateURL } from \"../utils/url\";\nimport { Avatar, type AvatarProps } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\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 & InboxNotificationOverrides & 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 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\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 ...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={classNames(\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-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 {unread ? (\n <DropdownItem\n onSelect={handleMarkAsRead}\n onClick={stopPropagation}\n >\n <CheckIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_MARK_AS_READ}\n </DropdownItem>\n ) : null}\n <DropdownItem\n onSelect={handleDelete}\n onClick={stopPropagation}\n >\n <DeleteIcon className=\"lb-dropdown-item-icon\" />\n {$.INBOX_NOTIFICATION_DELETE}\n </DropdownItem>\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 >\n <EllipsisIcon className=\"lb-button-icon\" />\n </Button>\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\n className={classNames(\"lb-inbox-notification-icon\", className)}\n {...props}\n />\n );\n}\n\nfunction InboxNotificationAvatar({\n className,\n ...props\n}: InboxNotificationAvatarProps) {\n return (\n <Avatar\n className={classNames(\"lb-inbox-notification-avatar\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Displays a thread inbox notification.\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 currentUserId = useCurrentUserId();\n // TODO: If you provide `href` (or plan to), we shouldn't run this hook. We should find a way to conditionally run it.\n // Because of batching and the fact that the same hook will be called within <Room /> in the notification's title,\n // it's not a big deal, the only scenario where it would be superfluous would be if the user provides their own\n // `href` AND disables room names in the title via `showRoomName={false}`.\n const { info } = useRoomInfo(inboxNotification.roomId);\n const { unread, date, aside, title, content, commentId } = useMemo(() => {\n const contents = generateInboxNotificationThreadContents(\n inboxNotification,\n thread,\n currentUserId ?? \"\"\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, index) => (\n <User\n key={userId}\n userId={userId}\n capitalize={index === 0}\n replaceSelf\n />\n ))}\n formatRemaining={$.LIST_REMAINING_USERS}\n truncate={INBOX_NOTIFICATION_THREAD_MAX_COMMENTS - 1}\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} capitalize />,\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, commentId)\n : undefined;\n }, [commentId, href, info?.url]);\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 {...props}\n ref={forwardedRef}\n >\n {content}\n </InboxNotificationLayout>\n );\n }\n);\n\n/**\n * Displays a text mention notification kind.\n */\nconst InboxNotificationTextMention = forwardRef<\n HTMLAnchorElement,\n InboxNotificationTextMentionProps\n>(\n (\n {\n inboxNotification,\n showActions = \"hover\",\n showRoomName = true,\n overrides,\n ...props\n },\n ref\n ) => {\n const $ = useOverrides(overrides);\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 capitalize\n />,\n showRoomName ? <Room roomId={inboxNotification.roomId} /> : undefined\n )}\n date={inboxNotification.notifiedAt}\n unread={unread}\n overrides={overrides}\n showActions={showActions}\n {...props}\n ref={ref}\n />\n );\n }\n);\n\n/**\n * Displays a custom notification kind.\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 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 Thread: InboxNotificationThread,\n TextMention: InboxNotificationTextMention,\n Custom: InboxNotificationCustom,\n Icon: InboxNotificationIcon,\n Avatar: InboxNotificationAvatar,\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAuNA;AAAgC;AAK5B;AACE;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;AAEK;AACY;AACT;AAEE;AACkB;AACpB;AACF;AACO;AACoB;AACE;AACpB;AACT;AACI;AACC;AAEM;AAAc;AACxB;AAAc;AACZ;AAAc;AACZ;AAAe;AACf;AAAc;AACZ;AAAe;AACb;AACW;AACV;AACU;AAGT;AACW;AACL;AAMV;AAAc;AACZ;AACO;AACQ;AACR;AAIC;AACW;AACD;AAER;AAAoB;AAIxB;AACW;AACD;AAER;AAAqB;AAG1B;AAGD;AAAmB;AACjB;AAAuB;AACrB;AACW;AACD;AACM;AACF;AACC;AAEb;AAAuB;AAQrC;AAAc;AAGrB;AAGN;AAEA;AAA+B;AAC7B;AAEF;AACE;AACG;AAC8D;AACzD;AAGV;AAEA;AAAiC;AAC/B;AAEF;AACE;AACG;AACgE;AAC3D;AAGV;AAKA;AAAgC;AAK5B;AACE;AACA;AACe;AACC;AACE;AACJ;AACd;AACG;AAIL;AACA;AACA;AAKA;AACA;AACE;AAAiB;AACf;AACA;AACiB;AAGnB;AAAuB;AAEnB;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAEI;AACM;AACL;AACsB;AACX;AAEd;AACkB;AACgC;AACrD;AACgB;AAAoB;AAAa;AACjC;AAElB;AACG;AAAc;AAEV;AACc;AACb;AACuC;AACvC;AACA;AACA;AAMR;AAAO;AACY;AACF;AACf;AACA;AACA;AACiB;AAC0C;AAC7D;AACF;AAGE;AACA;AAEA;AAAe;AAAgC;AAC/C;AAAgB;AACb;AAAU;AAAuB;AAAyB;AAAC;AAC5C;AAAoB;AAAa;AAEnD;AACG;AAAc;AACZ;AACqB;AACX;AACG;AACZ;AACA;AACA;AAKN;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;AACG;AACC;AACA;AACA;AACA;AACA;AACA;AACM;AACN;AACmB;AACf;AACC;AAGP;AAGN;AAKA;AAAqC;AAKjC;AACE;AACc;AACC;AACf;AACG;AAIL;AAEA;AACE;AAEmD;AAIrD;AACG;AACC;AACQ;AAAkD;AAAW;AAC5D;AACN;AACwB;AACG;AAChB;AACZ;AACgB;AAA+B;AAAa;AAC9D;AACwB;AACxB;AACA;AACA;AACI;AACJ;AACF;AAGN;AAKA;AAAgC;AAK5B;AACE;AACc;AACd;AACA;AACA;AACA;AACG;AAIL;AACE;AAEmD;AAIrD;AACG;AACC;AACA;AACA;AACwB;AACxB;AACA;AACA;AACI;AACC;AAGP;AAGN;AAEA;AAIE;AACG;AACC;AACI;AAKF;AAKA;AAEG;AACQ;AAOnB;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;AACU;AACK;AACL;AACF;AACE;AAEZ;;"}
@@ -50,6 +50,7 @@ const Thread = React.forwardRef(
50
50
  onThreadDelete,
51
51
  onAuthorClick,
52
52
  onMentionClick,
53
+ onAttachmentClick,
53
54
  overrides: overrides$1,
54
55
  className,
55
56
  ...props
@@ -153,6 +154,7 @@ const Thread = React.forwardRef(
153
154
  onCommentDelete: handleCommentDelete,
154
155
  onAuthorClick,
155
156
  onMentionClick,
157
+ onAttachmentClick,
156
158
  autoMarkReadThreadId: index === lastCommentIndex && isUnread ? thread.id : void 0,
157
159
  additionalActionsClassName: isFirstComment ? "lb-thread-actions" : void 0,
158
160
  additionalActions: isFirstComment && showResolveAction ? /* @__PURE__ */ React.createElement(Tooltip.Tooltip, {
@@ -1 +1 @@
1
- {"version":3,"file":"Thread.js","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkThreadAsResolved,\n useMarkThreadAsUnresolved,\n useThreadSubscription,\n} from \"@liveblocks/react\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkThreadAsResolved();\n const markThreadAsUnresolved = useMarkThreadAsUnresolved();\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const { status: subscriptionStatus, unreadSince } = useThreadSubscription(\n thread.id\n );\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n {thread.resolved ? (\n <ResolvedIcon className=\"lb-button-icon\" />\n ) : (\n <ResolveIcon className=\"lb-button-icon\" />\n )}\n </Button>\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n }}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsIO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAoD;AAC3C;AAET;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AAEK;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEJ;AAAc;AAEX;AACA;AAGA;AACG;AACc;AACH;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACiB;AACjB;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAIL;AAAuB;AAEvB;AAAsB;AAK7B;AAKV;AAGG;AAAsB;AACpB;AACW;AACI;AAEb;AAAe;AACb;AAAwB;AAO/B;AAKH;AACW;AACO;AACuC;AAC7C;AACe;AACP;AACnB;AAIR;AAGN;;"}
1
+ {"version":3,"file":"Thread.js","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkThreadAsResolved,\n useMarkThreadAsUnresolved,\n useThreadSubscription,\n} from \"@liveblocks/react\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkThreadAsResolved();\n const markThreadAsUnresolved = useMarkThreadAsUnresolved();\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const { status: subscriptionStatus, unreadSince } = useThreadSubscription(\n thread.id\n );\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n {thread.resolved ? (\n <ResolvedIcon className=\"lb-button-icon\" />\n ) : (\n <ResolveIcon className=\"lb-button-icon\" />\n )}\n </Button>\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n }}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgJO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAoD;AAC3C;AAET;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AAEK;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEJ;AAAc;AAEX;AACA;AAGA;AACG;AACc;AACH;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAIL;AAAuB;AAEvB;AAAsB;AAK7B;AAKV;AAGG;AAAsB;AACpB;AACW;AACI;AAEb;AAAe;AACb;AAAwB;AAO/B;AAKH;AACW;AACO;AACuC;AAC7C;AACe;AACP;AACnB;AAIR;AAGN;;"}
@@ -29,6 +29,7 @@ const Thread = forwardRef(
29
29
  onThreadDelete,
30
30
  onAuthorClick,
31
31
  onMentionClick,
32
+ onAttachmentClick,
32
33
  overrides,
33
34
  className,
34
35
  ...props
@@ -132,6 +133,7 @@ const Thread = forwardRef(
132
133
  onCommentDelete: handleCommentDelete,
133
134
  onAuthorClick,
134
135
  onMentionClick,
136
+ onAttachmentClick,
135
137
  autoMarkReadThreadId: index === lastCommentIndex && isUnread ? thread.id : void 0,
136
138
  additionalActionsClassName: isFirstComment ? "lb-thread-actions" : void 0,
137
139
  additionalActions: isFirstComment && showResolveAction ? /* @__PURE__ */ React__default.createElement(Tooltip, {
@@ -1 +1 @@
1
- {"version":3,"file":"Thread.mjs","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkThreadAsResolved,\n useMarkThreadAsUnresolved,\n useThreadSubscription,\n} from \"@liveblocks/react\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkThreadAsResolved();\n const markThreadAsUnresolved = useMarkThreadAsUnresolved();\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const { status: subscriptionStatus, unreadSince } = useThreadSubscription(\n thread.id\n );\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n {thread.resolved ? (\n <ResolvedIcon className=\"lb-button-icon\" />\n ) : (\n <ResolveIcon className=\"lb-button-icon\" />\n )}\n </Button>\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n }}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAsIO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAoD;AAC3C;AAET;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AAEK;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEJ;AAAc;AAEX;AACA;AAGA;AACG;AACc;AACH;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACiB;AACjB;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAIL;AAAuB;AAEvB;AAAsB;AAK7B;AAKV;AAGG;AAAsB;AACpB;AACW;AACI;AAEb;AAAe;AACb;AAAwB;AAO/B;AAKH;AACW;AACO;AACuC;AAC7C;AACe;AACP;AACnB;AAIR;AAGN;;"}
1
+ {"version":3,"file":"Thread.mjs","sources":["../../src/components/Thread.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentData,\n DM,\n ThreadData,\n} from \"@liveblocks/core\";\nimport {\n useMarkThreadAsResolved,\n useMarkThreadAsUnresolved,\n useThreadSubscription,\n} from \"@liveblocks/react\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n ComponentPropsWithoutRef,\n ForwardedRef,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nimport { ArrowDownIcon } from \"../icons/ArrowDown\";\nimport { ResolveIcon } from \"../icons/Resolve\";\nimport { ResolvedIcon } from \"../icons/Resolved\";\nimport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n ThreadOverrides,\n} from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { classNames } from \"../utils/class-names\";\nimport { findLastIndex } from \"../utils/find-last-index\";\nimport type { CommentProps } from \"./Comment\";\nimport { Comment } from \"./Comment\";\nimport { Composer } from \"./Composer\";\nimport { Button } from \"./internal/Button\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\n\nexport interface ThreadProps<M extends BaseMetadata = DM>\n extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The thread to display.\n */\n thread: ThreadData<M>;\n\n /**\n * How to show or hide the composer to reply to the thread.\n */\n showComposer?: boolean | \"collapsed\";\n\n /**\n * Whether to show the action to resolve the thread.\n */\n showResolveAction?: boolean;\n\n /**\n * How to show or hide the actions.\n */\n showActions?: CommentProps[\"showActions\"];\n\n /**\n * Whether to show reactions.\n */\n showReactions?: CommentProps[\"showReactions\"];\n\n /**\n * Whether to indent the comments' content.\n */\n indentCommentContent?: CommentProps[\"indentContent\"];\n\n /**\n * Whether to show deleted comments.\n */\n showDeletedComments?: CommentProps[\"showDeleted\"];\n\n /**\n * Whether to show attachments.\n */\n showAttachments?: boolean;\n\n /**\n * The event handler called when changing the resolved status.\n */\n onResolvedChange?: (resolved: boolean) => void;\n\n /**\n * The event handler called when a comment is edited.\n */\n onCommentEdit?: CommentProps[\"onCommentEdit\"];\n\n /**\n * The event handler called when a comment is deleted.\n */\n onCommentDelete?: CommentProps[\"onCommentDelete\"];\n\n /**\n * The event handler called when the thread is deleted.\n * A thread is deleted when all its comments are deleted.\n */\n onThreadDelete?: (thread: ThreadData<M>) => void;\n\n /**\n * The event handler called when clicking on a comment's author.\n */\n onAuthorClick?: CommentProps[\"onAuthorClick\"];\n\n /**\n * The event handler called when clicking on a mention.\n */\n onMentionClick?: CommentProps[\"onMentionClick\"];\n\n /**\n * The event handler called when clicking on a comment's attachment.\n */\n onAttachmentClick?: CommentProps[\"onAttachmentClick\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<\n GlobalOverrides & ThreadOverrides & CommentOverrides & ComposerOverrides\n >;\n}\n\n/**\n * Displays a thread of comments, with a composer to reply\n * to it.\n *\n * @example\n * <>\n * {threads.map((thread) => (\n * <Thread key={thread.id} thread={thread} />\n * ))}\n * </>\n */\nexport const Thread = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n thread,\n indentCommentContent = true,\n showActions = \"hover\",\n showDeletedComments,\n showResolveAction = true,\n showReactions = true,\n showComposer = \"collapsed\",\n onResolvedChange,\n onCommentEdit,\n onCommentDelete,\n onThreadDelete,\n onAuthorClick,\n onMentionClick,\n onAttachmentClick,\n overrides,\n className,\n ...props\n }: ThreadProps<M>,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) => {\n const markThreadAsResolved = useMarkThreadAsResolved();\n const markThreadAsUnresolved = useMarkThreadAsUnresolved();\n const $ = useOverrides(overrides);\n const firstCommentIndex = useMemo(() => {\n return showDeletedComments\n ? 0\n : thread.comments.findIndex((comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const lastCommentIndex = useMemo(() => {\n return showDeletedComments\n ? thread.comments.length - 1\n : findLastIndex(thread.comments, (comment) => comment.body);\n }, [showDeletedComments, thread.comments]);\n const { status: subscriptionStatus, unreadSince } = useThreadSubscription(\n thread.id\n );\n const unreadIndex = useMemo(() => {\n // The user is not subscribed to this thread.\n if (subscriptionStatus !== \"subscribed\") {\n return;\n }\n\n // The user hasn't read the thread yet, so all comments are unread.\n if (unreadSince === null) {\n return firstCommentIndex;\n }\n\n // The user has read the thread, so we find the first unread comment.\n const unreadIndex = thread.comments.findIndex(\n (comment) =>\n (showDeletedComments ? true : comment.body) &&\n comment.createdAt > unreadSince\n );\n\n return unreadIndex >= 0 && unreadIndex < thread.comments.length\n ? unreadIndex\n : undefined;\n }, [\n firstCommentIndex,\n showDeletedComments,\n subscriptionStatus,\n thread.comments,\n unreadSince,\n ]);\n const [newIndex, setNewIndex] = useState<number>();\n const newIndicatorIndex = newIndex === undefined ? unreadIndex : newIndex;\n\n useEffect(() => {\n if (unreadIndex) {\n // Keep the \"new\" indicator at the lowest unread index.\n setNewIndex((persistedUnreadIndex) =>\n Math.min(persistedUnreadIndex ?? Infinity, unreadIndex)\n );\n }\n }, [unreadIndex]);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleResolvedChange = useCallback(\n (resolved: boolean) => {\n onResolvedChange?.(resolved);\n\n if (resolved) {\n markThreadAsResolved(thread.id);\n } else {\n markThreadAsUnresolved(thread.id);\n }\n },\n [\n markThreadAsResolved,\n markThreadAsUnresolved,\n onResolvedChange,\n thread.id,\n ]\n );\n\n const handleCommentDelete = useCallback(\n (comment: CommentData) => {\n onCommentDelete?.(comment);\n\n const filteredComments = thread.comments.filter(\n (comment) => comment.body\n );\n\n if (filteredComments.length <= 1) {\n onThreadDelete?.(thread);\n }\n },\n [onCommentDelete, onThreadDelete, thread]\n );\n\n return (\n <TooltipProvider>\n <div\n className={classNames(\n \"lb-root lb-thread\",\n showActions === \"hover\" && \"lb-thread:show-actions-hover\",\n className\n )}\n data-resolved={thread.resolved ? \"\" : undefined}\n data-unread={unreadIndex !== undefined ? \"\" : undefined}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n >\n <div className=\"lb-thread-comments\">\n {thread.comments.map((comment, index) => {\n const isFirstComment = index === firstCommentIndex;\n const isUnread =\n unreadIndex !== undefined && index >= unreadIndex;\n\n const children = (\n <Comment\n key={comment.id}\n className=\"lb-thread-comment\"\n data-unread={isUnread ? \"\" : undefined}\n comment={comment}\n indentContent={indentCommentContent}\n showDeleted={showDeletedComments}\n showActions={showActions}\n showReactions={showReactions}\n onCommentEdit={onCommentEdit}\n onCommentDelete={handleCommentDelete}\n onAuthorClick={onAuthorClick}\n onMentionClick={onMentionClick}\n onAttachmentClick={onAttachmentClick}\n autoMarkReadThreadId={\n index === lastCommentIndex && isUnread\n ? thread.id\n : undefined\n }\n additionalActionsClassName={\n isFirstComment ? \"lb-thread-actions\" : undefined\n }\n additionalActions={\n isFirstComment && showResolveAction ? (\n <Tooltip\n content={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n <TogglePrimitive.Root\n pressed={thread.resolved}\n onPressedChange={handleResolvedChange}\n asChild\n >\n <Button\n className=\"lb-comment-action\"\n onClick={stopPropagation}\n aria-label={\n thread.resolved\n ? $.THREAD_UNRESOLVE\n : $.THREAD_RESOLVE\n }\n >\n {thread.resolved ? (\n <ResolvedIcon className=\"lb-button-icon\" />\n ) : (\n <ResolveIcon className=\"lb-button-icon\" />\n )}\n </Button>\n </TogglePrimitive.Root>\n </Tooltip>\n ) : null\n }\n />\n );\n\n return index === newIndicatorIndex &&\n newIndicatorIndex !== firstCommentIndex &&\n newIndicatorIndex <= lastCommentIndex ? (\n <Fragment key={comment.id}>\n <div\n className=\"lb-thread-new-indicator\"\n aria-label={$.THREAD_NEW_INDICATOR_DESCRIPTION}\n >\n <span className=\"lb-thread-new-indicator-label\">\n <ArrowDownIcon className=\"lb-thread-new-indicator-label-icon\" />\n {$.THREAD_NEW_INDICATOR}\n </span>\n </div>\n {children}\n </Fragment>\n ) : (\n children\n );\n })}\n </div>\n {showComposer && (\n <Composer\n className=\"lb-thread-composer\"\n threadId={thread.id}\n defaultCollapsed={showComposer === \"collapsed\" ? true : undefined}\n overrides={{\n COMPOSER_PLACEHOLDER: $.THREAD_COMPOSER_PLACEHOLDER,\n COMPOSER_SEND: $.THREAD_COMPOSER_SEND,\n }}\n />\n )}\n </div>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ThreadProps<M> & RefAttributes<HTMLDivElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAgJO;AAAe;AAElB;AACE;AACuB;AACT;AACd;AACoB;AACJ;AACD;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAEuD;AAEzD;AACE;AAE4D;AAE9D;AAAoD;AAC3C;AAET;AAEE;AACE;AAAA;AAIF;AACE;AAAO;AAIT;AAAoC;AAGZ;AAGxB;AAEI;AACH;AACD;AACA;AACA;AACO;AACP;AAEF;AACA;AAEA;AACE;AAEE;AAAA;AACwD;AACxD;AACF;AAGF;AACE;AAAsB;AAGxB;AAA6B;AAEzB;AAEA;AACE;AAA8B;AAE9B;AAAgC;AAClC;AACF;AACA;AACE;AACA;AACA;AACO;AACT;AAGF;AAA4B;AAExB;AAEA;AAAyC;AAClB;AAGvB;AACE;AAAuB;AACzB;AACF;AACwC;AAG1C;AAEK;AACY;AACT;AAC2B;AAC3B;AACF;AACsC;AACQ;AACvC;AACH;AACC;AAEJ;AAAc;AAEX;AACA;AAGA;AACG;AACc;AACH;AACmB;AAC7B;AACe;AACF;AACb;AACA;AACA;AACiB;AACjB;AACA;AACA;AAIM;AAGmC;AAIpC;AAIS;AAGP;AACiB;AACC;AACV;AAEN;AACW;AACD;AAID;AAIL;AAAuB;AAEvB;AAAsB;AAK7B;AAKV;AAGG;AAAsB;AACpB;AACW;AACI;AAEb;AAAe;AACb;AAAwB;AAO/B;AAKH;AACW;AACO;AACuC;AAC7C;AACe;AACP;AACnB;AAIR;AAGN;;"}