stream-chat-react 14.0.0-beta.7 → 14.0.0-beta.8

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 (42) hide show
  1. package/dist/cjs/{WithAudioPlayback.83ba0e35.js → WithAudioPlayback.58b0b39b.js} +4 -2
  2. package/dist/cjs/WithAudioPlayback.58b0b39b.js.map +1 -0
  3. package/dist/cjs/emojis.js +1 -1
  4. package/dist/cjs/index.js +1732 -1588
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/css/index.css +1346 -1115
  7. package/dist/css/index.css.map +1 -1
  8. package/dist/es/{WithAudioPlayback.21b7f35a.mjs → WithAudioPlayback.2ffdc4c5.mjs} +169 -167
  9. package/dist/es/WithAudioPlayback.2ffdc4c5.mjs.map +1 -0
  10. package/dist/es/emojis.mjs +1 -1
  11. package/dist/es/index.mjs +1783 -1639
  12. package/dist/es/index.mjs.map +1 -1
  13. package/dist/types/components/ChannelListItem/ChannelListItemActionButtons.defaults.d.ts.map +1 -1
  14. package/dist/types/components/ChannelListItem/utils.d.ts.map +1 -1
  15. package/dist/types/components/Chat/Chat.d.ts.map +1 -1
  16. package/dist/types/components/Dialog/components/ContextMenu.d.ts +3 -1
  17. package/dist/types/components/Dialog/components/ContextMenu.d.ts.map +1 -1
  18. package/dist/types/components/Dialog/service/DialogPortal.d.ts.map +1 -1
  19. package/dist/types/components/Form/NumericInput.d.ts.map +1 -1
  20. package/dist/types/components/Form/TextInput.d.ts +15 -8
  21. package/dist/types/components/Form/TextInput.d.ts.map +1 -1
  22. package/dist/types/components/Form/index.d.ts +1 -0
  23. package/dist/types/components/Form/index.d.ts.map +1 -1
  24. package/dist/types/components/Message/MessageUI.d.ts.map +1 -1
  25. package/dist/types/components/Message/utils.d.ts +1 -0
  26. package/dist/types/components/Message/utils.d.ts.map +1 -1
  27. package/dist/types/components/MessageActions/MessageActions.d.ts.map +1 -1
  28. package/dist/types/components/MessageActions/MessageActions.defaults.d.ts.map +1 -1
  29. package/dist/types/components/MessageActions/hooks/useBaseMessageActionSetFilter.d.ts +1 -1
  30. package/dist/types/components/MessageActions/hooks/useBaseMessageActionSetFilter.d.ts.map +1 -1
  31. package/dist/types/components/MessageList/hooks/MessageList/useFloatingDateSeparatorMessageList.d.ts +2 -2
  32. package/dist/types/components/MessageList/hooks/MessageList/useFloatingDateSeparatorMessageList.d.ts.map +1 -1
  33. package/dist/types/components/MessageList/hooks/VirtualizedMessageList/useFloatingDateSeparator.d.ts.map +1 -1
  34. package/dist/types/components/Poll/PollCreationDialog/OptionFieldSet.d.ts.map +1 -1
  35. package/dist/types/components/Reactions/ReactionSelector.d.ts +2 -0
  36. package/dist/types/components/Reactions/ReactionSelector.d.ts.map +1 -1
  37. package/dist/types/components/SummarizedMessagePreview/hooks/useLatestMessagePreview.d.ts.map +1 -1
  38. package/dist/types/components/Tooltip/Tooltip.d.ts +4 -2
  39. package/dist/types/components/Tooltip/Tooltip.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/dist/cjs/WithAudioPlayback.83ba0e35.js.map +0 -1
  42. package/dist/es/WithAudioPlayback.21b7f35a.mjs.map +0 -1
package/dist/es/index.mjs CHANGED
@@ -3,8 +3,8 @@ import clsx from "clsx";
3
3
  import { nanoid } from "nanoid";
4
4
  import React, { useState, useEffect, useCallback, useLayoutEffect, useMemo, useContext, createContext, useRef, forwardRef, createElement, Component, Fragment as Fragment$1 } from "react";
5
5
  import { u as useHandleFileChangeWrapper, d as dataTransferItemsToFiles, r as renderAudio, t as toAudioBuffer, c as createFileFromBlobs, g as getExtensionFromMimeType, a as getRecordedMediaTypeFromMimeType } from "./audioProcessing.21cb49e1.mjs";
6
- import { u as useStateStore, a as useMessageComposerController, i as isMessageBounced, b as useChannelActionContext, C as ComponentContext, c as useTranslationContext, d as useChannelStateContext, e as useChatContext, f as isNotificationForPanel, B as Button, I as IconPauseFill, g as IconPlayFill, h as getDefaultExportFromCjs, j as defaultTranslatorFunction, p as predefinedFormatters, L as LocalizedFormat, k as calendar, l as IconLoading, m as useComponentContext, n as isNetworkSendFailure, o as isUserMuted, q as useThreadContext, r as usePopoverPosition, s as IconXmark, t as IconUser, v as IconExclamationMarkFill, w as IconChevronRight, x as IconChevronLeft, y as IconArrowLeft, z as IconExclamationMark, A as IconNoSign, D as isMessageErrorRetryable, E as ACTIONS_NOT_WORKING_IN_THREAD, F as useNotificationApi, G as IconArrowUpRight, H as IconPin, J as mapToUserNameOrId, K as IconClock, M as IconCheckmark1Small, N as IconChecks, O as getReadByTooltipText, P as messageHasAttachments, Q as messageTextHasEmojisOnly, R as isDate, S as getDateString, T as IconTranslate, U as useMessageComposerContext, V as useIsCooldownActive, W as IconXmarkSmall, X as IconImage, Y as IconPoll, Z as IconLocation, _ as IconFile, $ as IconLink, a0 as IconVideo, a1 as IconCamera, a2 as IconVoice, a3 as IconBookmark, a4 as IconBell, a5 as IconChevronDown, a6 as IconMinus, a7 as IconPlusSmall, a8 as IconCheckmark, a9 as DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, aa as IconTrophy, ab as IconReorder, ac as IconMinusCircle, ad as IconSend, ae as IconAudio, af as IconUserAdd, ag as IconMute, ah as IconGiphy, ai as IconFlag, aj as IconUserRemove, ak as IconAttachment, al as IconCommand, am as CHANNEL_CONTAINER_ID, an as IconPlus, ao as IconUnsupportedAttachment, ap as IconExclamationTriangleFill, aq as useAudioPlayer, ar as IconMicrophoneSolid, as as IconVideoFill, at as IconRetry, au as IconArrowDownCircle, av as IconBolt, aw as IconDelete, ax as IconUpload, ay as MessageComposerContextProvider, az as useTypingContext, aA as useChatViewContext, aB as MESSAGE_ACTIONS, aC as LegacyThreadContext, aD as IconReply, aE as IconEmoji, aF as IconMore, aG as IconUserCheck, aH as IconBookmarkRemove, aI as IconBellOff, aJ as IconNotification, aK as IconEdit, aL as IconCopy, aM as IconUnpin, aN as IconQuote, aO as IconThread, aP as IconEmojiAdd, aQ as areMessageUIPropsEqual, aR as isDateSeparatorMessage, aS as isMessageBlocked, aT as messageHasSingleAttachment, aU as messageHasGiphyAttachment, aV as messageHasReactions, aW as messageHasQuotedMessage, aX as isMessageEdited, aY as countEmojis, aZ as areMessagePropsEqual, a_ as getMessageActions, a$ as processMessages, b0 as insertIntro, b1 as getGroupStyles, b2 as getLastReceived, b3 as IconArrowUp, b4 as isIntroMessage, b5 as isLocalMessage, b6 as getIsFirstUnreadMessage, b7 as IconArrowDown, b8 as DEFAULT_NEXT_CHANNEL_PAGE_SIZE, b9 as EmptyStateIndicator, ba as useNotificationTarget, bb as getChannel, bc as IconSearch, bd as IconXCircle, be as useChannelListContext, bf as DEFAULT_JUMP_TO_PAGE_SIZE, bg as ChannelListContextProvider, bh as IconArchive, bi as IconLeave, bj as IconExclamationCircleFill, bk as useThreadsViewContext, bl as IconMessageBubbles, bm as IconRefresh, bn as hasSystemNotificationTag, bo as IconEyeFill, bp as defaultDateTimeParser, bq as isLanguageSupported, br as ChatProvider, bs as TranslationProvider } from "./WithAudioPlayback.21b7f35a.mjs";
7
- import { bx, bA, c2, c3, c4, c5, c6, c7, bC, bF, bB, bE, bG, c8, bJ, bK, bL, bM, bN, c9, bP, bV, c0, c1, ca, cb, cc, bu, b_, bI, bH, bv, bw, bQ, bR, bX, bY, bZ, bU, cd, bW, ce, bT, bS, bt, bD, by, bz, b$, bO } from "./WithAudioPlayback.21b7f35a.mjs";
6
+ import { u as useStateStore, a as useMessageComposerController, i as isMessageBounced, b as useChannelActionContext, C as ComponentContext, c as useTranslationContext, d as useChannelStateContext, e as useChatContext, f as isNotificationForPanel, B as Button, I as IconPauseFill, g as IconPlayFill, h as getDefaultExportFromCjs, j as defaultTranslatorFunction, p as predefinedFormatters, L as LocalizedFormat, k as calendar, l as IconLoading, m as useComponentContext, n as isNetworkSendFailure, o as isUserMuted, q as useThreadContext, r as usePopoverPosition, s as IconXmark, t as IconUser, v as IconExclamationMarkFill, w as IconChevronRight, x as IconChevronLeft, y as IconArrowLeft, z as IconExclamationMark, A as IconNoSign, D as isMessageDeleted, E as isMessageErrorRetryable, F as ACTIONS_NOT_WORKING_IN_THREAD, G as useNotificationApi, H as IconArrowUpRight, J as IconPin, K as mapToUserNameOrId, M as IconClock, N as IconCheckmark1Small, O as IconChecks, P as getReadByTooltipText, Q as messageHasAttachments, R as messageTextHasEmojisOnly, S as isDate, T as getDateString, U as IconTranslate, V as useMessageComposerContext, W as useIsCooldownActive, X as IconXmarkSmall, Y as IconImage, Z as IconPoll, _ as IconLocation, $ as IconFile, a0 as IconLink, a1 as IconVideo, a2 as IconCamera, a3 as IconVoice, a4 as IconBookmark, a5 as IconBell, a6 as IconChevronDown, a7 as IconMinus, a8 as IconPlusSmall, a9 as IconCheckmark, aa as DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, ab as IconTrophy, ac as IconReorder, ad as IconMinusCircle, ae as IconSend, af as IconAudio, ag as IconUserAdd, ah as IconMute, ai as IconGiphy, aj as IconFlag, ak as IconUserRemove, al as IconAttachment, am as IconCommand, an as CHANNEL_CONTAINER_ID, ao as IconPlus, ap as IconUnsupportedAttachment, aq as IconExclamationTriangleFill, ar as useAudioPlayer, as as IconMicrophoneSolid, at as IconVideoFill, au as IconRetry, av as IconArrowDownCircle, aw as IconBolt, ax as IconDelete, ay as IconUpload, az as MessageComposerContextProvider, aA as useTypingContext, aB as useChatViewContext, aC as MESSAGE_ACTIONS, aD as LegacyThreadContext, aE as IconEmojiAdd, aF as IconReply, aG as IconEmoji, aH as IconMore, aI as IconUserCheck, aJ as IconBookmarkRemove, aK as IconBellOff, aL as IconNotification, aM as IconEdit, aN as IconCopy, aO as IconUnpin, aP as IconQuote, aQ as IconThread, aR as areMessageUIPropsEqual, aS as isDateSeparatorMessage, aT as isMessageBlocked, aU as messageHasSingleAttachment, aV as messageHasGiphyAttachment, aW as messageHasReactions, aX as messageHasQuotedMessage, aY as isMessageEdited, aZ as countEmojis, a_ as areMessagePropsEqual, a$ as getMessageActions, b0 as processMessages, b1 as insertIntro, b2 as getGroupStyles, b3 as getLastReceived, b4 as IconArrowUp, b5 as isIntroMessage, b6 as isLocalMessage, b7 as getIsFirstUnreadMessage, b8 as IconArrowDown, b9 as DEFAULT_NEXT_CHANNEL_PAGE_SIZE, ba as EmptyStateIndicator, bb as useNotificationTarget, bc as getChannel, bd as IconSearch, be as IconXCircle, bf as useChannelListContext, bg as DEFAULT_JUMP_TO_PAGE_SIZE, bh as ChannelListContextProvider, bi as IconLeave, bj as IconArchive, bk as IconExclamationCircleFill, bl as useThreadsViewContext, bm as IconMessageBubbles, bn as IconRefresh, bo as hasSystemNotificationTag, bp as IconEyeFill, bq as defaultDateTimeParser, br as isLanguageSupported, bs as ChatProvider, bt as TranslationProvider } from "./WithAudioPlayback.2ffdc4c5.mjs";
7
+ import { by, bB, c3, c4, c5, c6, c7, c8, bD, bG, bC, bF, bH, c9, bK, bL, bM, bN, bO, ca, bQ, bW, c1, c2, cb, cc, cd, bv, b$, bJ, bI, bw, bx, bR, bS, bY, bZ, b_, bV, ce, bX, cf, bU, bT, bu, bE, bz, bA, c0, bP } from "./WithAudioPlayback.2ffdc4c5.mjs";
8
8
  import { StateStore, formatMessage, MessageComposer as MessageComposer$1, isGiphyAttachment, isScrapedContent, isLocalVideoAttachment, isVideoAttachment, isLocalImageAttachment, isImageAttachment, isAudioAttachment, isVoiceRecordingAttachment, isFileAttachment, isVoteAnswer, VotingVisibility, isLocalVoiceRecordingAttachment, isLocalAudioAttachment, isLocalFileAttachment, isLocalAttachment, LinkPreviewsManager, isSharedLocationResponse, LiveLocationManager, SearchController, ChannelSearchSource, UserSearchSource, MessageSearchSource, StreamChat } from "stream-chat";
9
9
  import throttle from "lodash.throttle";
10
10
  import * as linkify from "linkifyjs";
@@ -291,6 +291,10 @@ const DialogPortalDestination = () => {
291
291
  if (!destinationRoot || !dialogManager) return;
292
292
  const handleDocumentClick = (event) => {
293
293
  const target = event.target;
294
+ const clickedOverlay = target.closest?.(
295
+ "[data-str-chat__portal-id]"
296
+ );
297
+ if (clickedOverlay && clickedOverlay !== destinationRoot) return;
294
298
  if (target !== destinationRoot && destinationRoot.contains(target)) return;
295
299
  setTimeout(() => {
296
300
  Object.values(dialogManager.state.getLatestValue().dialogsById).forEach(
@@ -9842,6 +9846,7 @@ const ContextMenuContext = React.createContext(
9842
9846
  );
9843
9847
  const useContextMenuContext = () => useContext(ContextMenuContext);
9844
9848
  function ContextMenuContent({
9849
+ anchorReferenceElement,
9845
9850
  backLabel = "Back",
9846
9851
  children,
9847
9852
  className,
@@ -9905,31 +9910,37 @@ function ContextMenuContent({
9905
9910
  if (!transitionDirection) return;
9906
9911
  setMenuBodyAnimationKey((value) => value + 1);
9907
9912
  }, [transitionDirection, menuStack.length]);
9908
- return /* @__PURE__ */ jsx(ContextMenuContext.Provider, { value: { closeMenu, openSubmenu, returnToParentMenu }, children: /* @__PURE__ */ jsxs(
9909
- ContextMenuRoot,
9913
+ return /* @__PURE__ */ jsx(
9914
+ ContextMenuContext.Provider,
9910
9915
  {
9911
- className: clsx(className, activeMenu.menuClassName),
9912
- "data-str-chat-enable-animations": enableAnimations,
9913
- ...props,
9914
- children: [
9915
- activeMenu.Header ? /* @__PURE__ */ jsx(activeMenu.Header, {}) : menuStack.length > 1 ? /* @__PURE__ */ jsx(ContextMenuHeader, { children: /* @__PURE__ */ jsxs(ContextMenuBackButton, { onClick: returnToParentMenu, children: [
9916
- /* @__PURE__ */ jsx(IconChevronLeft, {}),
9917
- /* @__PURE__ */ jsx("span", { children: backLabel })
9918
- ] }) }) : null,
9919
- /* @__PURE__ */ jsx(
9920
- ContextMenuBody,
9921
- {
9922
- className: clsx({
9923
- "str-chat__context-menu__body--submenu-backward": transitionDirection === "backward",
9924
- "str-chat__context-menu__body--submenu-forward": transitionDirection === "forward"
9925
- }),
9926
- children: activeMenu.Submenu ? /* @__PURE__ */ jsx(activeMenu.Submenu, {}) : /* @__PURE__ */ jsx(ActiveMenuItemsWrapper, { children: typeof children !== "undefined" ? children : activeMenu.items?.map((Item2, index) => /* @__PURE__ */ jsx(Item2, {}, `context-menu-item-${index}`)) })
9927
- },
9928
- `context-menu-body-${menuStack.length}-${menuBodyAnimationKey}`
9929
- )
9930
- ]
9916
+ value: { anchorReferenceElement, closeMenu, openSubmenu, returnToParentMenu },
9917
+ children: /* @__PURE__ */ jsxs(
9918
+ ContextMenuRoot,
9919
+ {
9920
+ className: clsx(className, activeMenu.menuClassName),
9921
+ "data-str-chat-enable-animations": enableAnimations,
9922
+ ...props,
9923
+ children: [
9924
+ activeMenu.Header ? /* @__PURE__ */ jsx(activeMenu.Header, {}) : menuStack.length > 1 ? /* @__PURE__ */ jsx(ContextMenuHeader, { children: /* @__PURE__ */ jsxs(ContextMenuBackButton, { onClick: returnToParentMenu, children: [
9925
+ /* @__PURE__ */ jsx(IconChevronLeft, {}),
9926
+ /* @__PURE__ */ jsx("span", { children: backLabel })
9927
+ ] }) }) : null,
9928
+ /* @__PURE__ */ jsx(
9929
+ ContextMenuBody,
9930
+ {
9931
+ className: clsx({
9932
+ "str-chat__context-menu__body--submenu-backward": transitionDirection === "backward",
9933
+ "str-chat__context-menu__body--submenu-forward": transitionDirection === "forward"
9934
+ }),
9935
+ children: activeMenu.Submenu ? /* @__PURE__ */ jsx(activeMenu.Submenu, {}) : /* @__PURE__ */ jsx(ActiveMenuItemsWrapper, { children: typeof children !== "undefined" ? children : activeMenu.items?.map((Item2, index) => /* @__PURE__ */ jsx(Item2, {}, `context-menu-item-${index}`)) })
9936
+ },
9937
+ `context-menu-body-${menuStack.length}-${menuBodyAnimationKey}`
9938
+ )
9939
+ ]
9940
+ }
9941
+ )
9931
9942
  }
9932
- ) });
9943
+ );
9933
9944
  }
9934
9945
  const ContextMenu = (props) => {
9935
9946
  const { ContextMenuContent: ContextMenuContentComponent = ContextMenuContent } = useComponentContext();
@@ -10002,6 +10013,7 @@ const ContextMenu = (props) => {
10002
10013
  const content = /* @__PURE__ */ createElement(
10003
10014
  ContextMenuContentComponent,
10004
10015
  {
10016
+ anchorReferenceElement: isAnchored ? referenceElement : void 0,
10005
10017
  ...menuProps,
10006
10018
  key: `context-menu-content-${contentResetToken}`,
10007
10019
  onMenuLevelChange: handleMenuLevelChange,
@@ -10343,6 +10355,7 @@ const MessageBlocked = () => {
10343
10355
  const useBaseMessageActionSetFilter = (messageActionSet, disable = false) => {
10344
10356
  const { initialMessage: isInitialMessage, message } = useMessageContext();
10345
10357
  const { channelConfig } = useChannelStateContext();
10358
+ const messageIsDeleted = isMessageDeleted(message);
10346
10359
  const {
10347
10360
  canBlockUser,
10348
10361
  canDelete,
@@ -10370,9 +10383,9 @@ const useBaseMessageActionSetFilter = (messageActionSet, disable = false) => {
10370
10383
  if (ACTIONS_NOT_WORKING_IN_THREAD.includes(type) && isMessageThreadReply)
10371
10384
  return false;
10372
10385
  if (message.error) {
10373
- return type === "resendMessage" && canSendMessage && (allowRetry || isBounced) || type === "edit" && (isBounced && canEdit || hasNetworkSendFailure) || type === "delete" && (isBounced && canDelete || hasNetworkSendFailure);
10386
+ return type === "resendMessage" && canSendMessage && (allowRetry || isBounced) || type === "edit" && (isBounced && canEdit || hasNetworkSendFailure) || type === "delete" && !messageIsDeleted && (isBounced && canDelete || hasNetworkSendFailure);
10374
10387
  }
10375
- if (type === "resendMessage" || type === "blockUser" && !canBlockUser || type === "copyMessageText" && !message.text || type === "delete" && !canDelete || type === "edit" && !canEdit || type === "flag" && !canFlag || type === "markUnread" && !canMarkUnread || type === "mute" && !canMute || type === "quote" && !canQuote || type === "react" && !canReact || type === "reply" && !canReply || type === "remindMe" && !channelConfig?.["user_message_reminders"] || type === "saveForLater" && !channelConfig?.["user_message_reminders"])
10388
+ if (type === "resendMessage" || type === "blockUser" && !canBlockUser || type === "copyMessageText" && !message.text || type === "delete" && (!canDelete || messageIsDeleted) || type === "edit" && !canEdit || type === "flag" && !canFlag || type === "markUnread" && !canMarkUnread || type === "mute" && !canMute || type === "quote" && !canQuote || type === "react" && !canReact || type === "reply" && !canReply || type === "remindMe" && !channelConfig?.["user_message_reminders"] || type === "saveForLater" && !channelConfig?.["user_message_reminders"])
10376
10389
  return false;
10377
10390
  return true;
10378
10391
  });
@@ -10391,6 +10404,7 @@ const useBaseMessageActionSetFilter = (messageActionSet, disable = false) => {
10391
10404
  channelConfig,
10392
10405
  isBounced,
10393
10406
  isInitialMessage,
10407
+ messageIsDeleted,
10394
10408
  isMessageThreadReply,
10395
10409
  message.error,
10396
10410
  message.status,
@@ -10522,9 +10536,10 @@ const PinIndicator = ({ message }) => {
10522
10536
  /* @__PURE__ */ jsx("span", { children: label })
10523
10537
  ] }) });
10524
10538
  };
10525
- const Tooltip = ({ children, ...rest }) => /* @__PURE__ */ jsx("div", { className: "str-chat__tooltip", ...rest, children });
10539
+ const Tooltip = ({ children, className, ...rest }) => /* @__PURE__ */ jsx("div", { className: clsx("str-chat__tooltip", className), ...rest, children });
10526
10540
  const PopperTooltip = ({
10527
10541
  children,
10542
+ className,
10528
10543
  offset = [0, 10],
10529
10544
  placement = "top",
10530
10545
  referenceElement,
@@ -10551,7 +10566,7 @@ const PopperTooltip = ({
10551
10566
  return /* @__PURE__ */ jsx(
10552
10567
  "div",
10553
10568
  {
10554
- className: "str-chat__tooltip",
10569
+ className: clsx("str-chat__tooltip", className),
10555
10570
  "data-placement": resolvedPlacement,
10556
10571
  ref: setPopperElement,
10557
10572
  style: { left: x ?? 0, position: strategy, top: y ?? 0 },
@@ -14164,8 +14179,9 @@ const NumericInput = forwardRef(
14164
14179
  ),
14165
14180
  disabled: disabled || atMin,
14166
14181
  onClick: handleDecrement,
14182
+ size: "xs",
14167
14183
  variant: "secondary",
14168
- children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-numeric-input__stepper-icon", children: /* @__PURE__ */ jsx(IconMinus, {}) })
14184
+ children: /* @__PURE__ */ jsx(IconMinus, { className: "str-chat__form-numeric-input__stepper-icon" })
14169
14185
  }
14170
14186
  ),
14171
14187
  /* @__PURE__ */ jsx(
@@ -14194,7 +14210,7 @@ const NumericInput = forwardRef(
14194
14210
  ),
14195
14211
  disabled: disabled || atMax,
14196
14212
  onClick: handleIncrement,
14197
- size: "sm",
14213
+ size: "xs",
14198
14214
  variant: "secondary",
14199
14215
  children: /* @__PURE__ */ jsx(IconPlusSmall, { className: "str-chat__form-numeric-input__stepper-icon" })
14200
14216
  }
@@ -14205,12 +14221,72 @@ const NumericInput = forwardRef(
14205
14221
  );
14206
14222
  }
14207
14223
  );
14224
+ const TextInputIconMessageLine = ({ icon, text: text2 }) => /* @__PURE__ */ jsxs(Fragment, { children: [
14225
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__message-icon", children: icon }),
14226
+ /* @__PURE__ */ jsx("span", { className: "str-chat__form-text-input__message-text", children: text2 })
14227
+ ] });
14228
+ const TextInputFieldMessage = (props) => {
14229
+ if (props.kind === "neutral") {
14230
+ return /* @__PURE__ */ jsx(
14231
+ "div",
14232
+ {
14233
+ className: clsx(
14234
+ "str-chat__form-text-input__message",
14235
+ props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"
14236
+ ),
14237
+ id: props.id,
14238
+ children: props.text
14239
+ }
14240
+ );
14241
+ } else if (props.kind === "success") {
14242
+ return /* @__PURE__ */ jsx(
14243
+ "div",
14244
+ {
14245
+ className: clsx(
14246
+ "str-chat__form-text-input__message",
14247
+ "str-chat__form-text-input__message--success",
14248
+ props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"
14249
+ ),
14250
+ id: props.id,
14251
+ children: /* @__PURE__ */ jsx(
14252
+ TextInputIconMessageLine,
14253
+ {
14254
+ icon: props.successMessageIcon ?? /* @__PURE__ */ jsx(IconCheckmark, {}),
14255
+ text: props.text
14256
+ }
14257
+ )
14258
+ }
14259
+ );
14260
+ } else if (props.kind === "error") {
14261
+ return /* @__PURE__ */ jsx(
14262
+ "div",
14263
+ {
14264
+ className: clsx(
14265
+ "str-chat__form-text-input__message",
14266
+ "str-chat__form-field-error",
14267
+ props.insidePlacement && "str-chat__form-text-input__message--field-message-inside"
14268
+ ),
14269
+ id: props.id,
14270
+ role: "alert",
14271
+ children: /* @__PURE__ */ jsx(
14272
+ TextInputIconMessageLine,
14273
+ {
14274
+ icon: props.errorMessageIcon ?? /* @__PURE__ */ jsx(IconExclamationMark, {}),
14275
+ text: props.text
14276
+ }
14277
+ )
14278
+ }
14279
+ );
14280
+ }
14281
+ return null;
14282
+ };
14208
14283
  const TextInput = forwardRef(function TextInput2({
14209
14284
  className,
14210
14285
  disabled,
14211
14286
  error = false,
14212
14287
  errorMessage,
14213
14288
  errorMessageIcon,
14289
+ fieldMessagePlacement = "outside",
14214
14290
  id: idProp,
14215
14291
  label,
14216
14292
  leading,
@@ -14222,71 +14298,86 @@ const TextInput = forwardRef(function TextInput2({
14222
14298
  variant = "outline",
14223
14299
  ...inputProps
14224
14300
  }, ref) {
14225
- const generatedId = useStableId();
14226
- const id = idProp ?? generatedId;
14227
- const displayError = error && (errorMessage != null || message != null);
14228
- const displaySuccess = successMessage != null;
14229
- const displayNeutralMessage = message != null && !error;
14230
- const displayMessage = displayError || displaySuccess || displayNeutralMessage;
14231
- const messageId = displayMessage ? `${id}-message` : void 0;
14232
- const messageContent = displayError ? /* @__PURE__ */ jsxs(Fragment, { children: [
14233
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__message-icon", children: errorMessageIcon ?? /* @__PURE__ */ jsx(IconExclamationMark, {}) }),
14234
- errorMessage ?? message
14235
- ] }) : displaySuccess ? /* @__PURE__ */ jsxs(Fragment, { children: [
14236
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__message-icon", children: successMessageIcon ?? /* @__PURE__ */ jsx(IconCheckmark, {}) }),
14237
- successMessage
14238
- ] }) : displayNeutralMessage ? message : null;
14301
+ const autoId = useStableId();
14302
+ const id = idProp ?? autoId;
14303
+ const hasError = error && (errorMessage != null || message != null);
14304
+ const showSuccess = !hasError && successMessage != null;
14305
+ const showNeutral = !hasError && !showSuccess && message != null;
14306
+ const hasFeedback = hasError || showSuccess || showNeutral;
14307
+ const messageInside = fieldMessagePlacement === "inside" && hasFeedback;
14308
+ const messageId = hasError ? `${id}-field-error` : showSuccess || showNeutral ? `${id}-message` : void 0;
14309
+ const describedBy = messageId;
14310
+ const fieldMessage = hasError ? /* @__PURE__ */ jsx(
14311
+ TextInputFieldMessage,
14312
+ {
14313
+ errorMessageIcon,
14314
+ id: messageId,
14315
+ insidePlacement: messageInside,
14316
+ kind: "error",
14317
+ text: errorMessage ?? message
14318
+ }
14319
+ ) : showSuccess ? /* @__PURE__ */ jsx(
14320
+ TextInputFieldMessage,
14321
+ {
14322
+ id: messageId,
14323
+ insidePlacement: messageInside,
14324
+ kind: "success",
14325
+ successMessageIcon,
14326
+ text: successMessage
14327
+ }
14328
+ ) : showNeutral ? /* @__PURE__ */ jsx(
14329
+ TextInputFieldMessage,
14330
+ {
14331
+ id: messageId,
14332
+ insidePlacement: messageInside,
14333
+ kind: "neutral",
14334
+ text: message
14335
+ }
14336
+ ) : null;
14239
14337
  return /* @__PURE__ */ jsxs(
14240
14338
  "div",
14241
14339
  {
14242
14340
  className: clsx(
14243
14341
  "str-chat__form-text-input",
14244
14342
  error && "str-chat__form-text-input--error",
14245
- displaySuccess && "str-chat__form-text-input--success",
14343
+ showSuccess && "str-chat__form-text-input--success",
14246
14344
  disabled && "str-chat__form-text-input--disabled",
14345
+ messageInside && "str-chat__form-text-input--field-message-inside",
14247
14346
  className
14248
14347
  ),
14249
14348
  children: [
14250
- !!label && /* @__PURE__ */ jsx("label", { className: "str-chat__form-text-input__label", htmlFor: id, children: label }),
14349
+ label ? /* @__PURE__ */ jsx("label", { className: "str-chat__form-text-input__label", htmlFor: id, children: label }) : null,
14251
14350
  /* @__PURE__ */ jsxs(
14252
14351
  "div",
14253
14352
  {
14254
14353
  className: clsx(
14255
14354
  "str-chat__form-text-input__wrapper",
14256
- `str-chat__form-text-input__wrapper--${variant}`
14355
+ `str-chat__form-text-input__wrapper--${variant}`,
14356
+ messageInside && "str-chat__form-text-input__wrapper--field-message-inside"
14257
14357
  ),
14258
14358
  children: [
14259
- !!leading && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__leading", children: leading }),
14260
- /* @__PURE__ */ jsx(
14261
- "input",
14262
- {
14263
- "aria-describedby": messageId,
14264
- "aria-invalid": error,
14265
- className: "str-chat__form-text-input__input",
14266
- disabled,
14267
- id,
14268
- ref,
14269
- ...inputProps
14270
- }
14271
- ),
14272
- trailingText != null && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__suffix", children: trailingText }),
14273
- !!trailing && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__trailing", children: trailing })
14359
+ /* @__PURE__ */ jsxs("div", { className: "str-chat__form-text-input__control-row", children: [
14360
+ leading ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__leading", children: leading }) : null,
14361
+ /* @__PURE__ */ jsx(
14362
+ "input",
14363
+ {
14364
+ "aria-describedby": describedBy,
14365
+ "aria-invalid": error,
14366
+ className: "str-chat__form-text-input__input",
14367
+ disabled,
14368
+ id,
14369
+ ref,
14370
+ ...inputProps
14371
+ }
14372
+ ),
14373
+ trailingText != null ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__suffix", children: trailingText }) : null,
14374
+ trailing ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "str-chat__form-text-input__trailing", children: trailing }) : null
14375
+ ] }),
14376
+ messageInside ? fieldMessage : null
14274
14377
  ]
14275
14378
  }
14276
14379
  ),
14277
- messageContent != null && /* @__PURE__ */ jsx(
14278
- "div",
14279
- {
14280
- className: clsx(
14281
- "str-chat__form-text-input__message",
14282
- displayError && "str-chat__form-field-error",
14283
- displaySuccess && "str-chat__form-text-input__message--success"
14284
- ),
14285
- id: messageId,
14286
- role: error ? "alert" : void 0,
14287
- children: messageContent
14288
- }
14289
- )
14380
+ messageInside ? null : fieldMessage
14290
14381
  ]
14291
14382
  }
14292
14383
  );
@@ -15671,6 +15762,7 @@ const OptionFieldSet = () => {
15671
15762
  {
15672
15763
  className: "str-chat__form__input-field__value",
15673
15764
  error: !!error,
15765
+ fieldMessagePlacement: "inside",
15674
15766
  id: option.id,
15675
15767
  leading: draggable ? /* @__PURE__ */ jsx(IconReorder, { className: "str-chat__drag-handle" }) : void 0,
15676
15768
  message: error ? /* @__PURE__ */ jsx("span", { "data-testid": "poll-option-input-field-error", children: knownValidationErrors[error] ?? t("Error") }) : void 0,
@@ -15708,7 +15800,7 @@ const RemoveOptionButton = ({ className, ...props }) => /* @__PURE__ */ jsx(
15708
15800
  appearance: "ghost",
15709
15801
  circular: true,
15710
15802
  className: clsx("str-chat__form__remove-option-button", className),
15711
- size: "sm",
15803
+ size: "xs",
15712
15804
  variant: "secondary",
15713
15805
  ...props,
15714
15806
  children: /* @__PURE__ */ jsx(IconMinusCircle, {})
@@ -17324,7 +17416,7 @@ const GalleryUI = () => {
17324
17416
  children: /* @__PURE__ */ jsx(
17325
17417
  "div",
17326
17418
  {
17327
- className: clsx({
17419
+ className: clsx("str-chat__gallery__media-container", {
17328
17420
  "str-chat__gallery__media--dragging": isDragging,
17329
17421
  "str-chat__gallery__media--slide-backward": !isDragging && slideDirection === "backward",
17330
17422
  "str-chat__gallery__media--slide-forward": !isDragging && slideDirection === "forward"
@@ -18229,7 +18321,7 @@ const textComposerStateSelector$3 = ({ selection, suggestions }) => ({
18229
18321
  const searchSourceStateSelector$5 = (nextValue) => ({
18230
18322
  items: nextValue.items ?? []
18231
18323
  });
18232
- const defaultComponents = {
18324
+ const defaultComponents$1 = {
18233
18325
  "/": (props) => /* @__PURE__ */ jsx(CommandItem, { ...props, entity: props.entity }),
18234
18326
  ":": (props) => /* @__PURE__ */ jsx(EmoticonItem, { ...props, entity: props.entity }),
18235
18327
  "@": (props) => /* @__PURE__ */ jsx(UserItem, { ...props, entity: props.entity })
@@ -18240,7 +18332,7 @@ const SuggestionList = ({
18240
18332
  containerClassName,
18241
18333
  focusedItemIndex,
18242
18334
  setFocusedItemIndex,
18243
- suggestionItemComponents = defaultComponents
18335
+ suggestionItemComponents = defaultComponents$1
18244
18336
  }) => {
18245
18337
  const {
18246
18338
  AutocompleteSuggestionItem = SuggestionListItem,
@@ -19094,7 +19186,7 @@ const getLatestMessagePreview = (channel, t, userLanguage = "en", isMessageAIGen
19094
19186
  if (!latestMessage) {
19095
19187
  return t("Nothing yet...");
19096
19188
  }
19097
- if (latestMessage.deleted_at) {
19189
+ if (isMessageDeleted(latestMessage)) {
19098
19190
  return t("Message deleted");
19099
19191
  }
19100
19192
  if (poll) {
@@ -19632,7 +19724,11 @@ const defaultReactionOptions = {
19632
19724
  };
19633
19725
  const stableOwnReactions = [];
19634
19726
  const ReactionSelector = (props) => {
19635
- const { handleReaction: propHandleReaction, own_reactions: propOwnReactions } = props;
19727
+ const {
19728
+ dialogId: propDialogId,
19729
+ handleReaction: propHandleReaction,
19730
+ own_reactions: propOwnReactions
19731
+ } = props;
19636
19732
  const [extendedListOpen, setExtendedListOpen] = useState(false);
19637
19733
  const {
19638
19734
  reactionOptions = defaultReactionOptions,
@@ -19644,7 +19740,7 @@ const ReactionSelector = (props) => {
19644
19740
  message,
19645
19741
  threadList
19646
19742
  } = useMessageContext();
19647
- const dialogId2 = ReactionSelector.getDialogId({
19743
+ const dialogId2 = propDialogId ?? ReactionSelector.getDialogId({
19648
19744
  messageId: message.id,
19649
19745
  threadList
19650
19746
  });
@@ -19670,50 +19766,61 @@ const ReactionSelector = (props) => {
19670
19766
  })
19671
19767
  );
19672
19768
  }, [reactionOptions]);
19673
- return /* @__PURE__ */ jsxs("div", { className: "str-chat__reaction-selector", "data-testid": "reaction-selector", children: [
19674
- !extendedListOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
19675
- /* @__PURE__ */ jsx(
19676
- "ul",
19677
- {
19678
- className: "str-chat__reaction-selector-list",
19679
- "data-testid": "reaction-selector-list",
19680
- children: adjustedQuickReactionOptions.map(
19681
- ({ Component: Component2, name: reactionName, type: reactionType }) => /* @__PURE__ */ jsx("li", { className: "str-chat__reaction-list-selector__item", children: /* @__PURE__ */ jsx(
19682
- "button",
19683
- {
19684
- "aria-label": `Select Reaction: ${reactionName || reactionType}`,
19685
- "aria-pressed": typeof ownReactionByType[reactionType] !== "undefined",
19686
- className: clsx("str-chat__reaction-selector-list__item-button"),
19687
- "data-testid": "select-reaction-button",
19688
- "data-text": reactionType,
19689
- onClick: (event) => {
19690
- handleReaction(reactionType, event);
19691
- if (closeReactionSelectorOnClick) {
19692
- dialog.close();
19693
- }
19694
- },
19695
- children: /* @__PURE__ */ jsx("span", { className: "str-chat__reaction-icon", children: /* @__PURE__ */ jsx(Component2, {}) })
19696
- }
19697
- ) }, reactionType)
19698
- )
19699
- }
19700
- ),
19701
- /* @__PURE__ */ jsx(
19702
- Button,
19703
- {
19704
- appearance: "outline",
19705
- circular: true,
19706
- className: "str-chat__reaction-selector__add-button",
19707
- "data-testid": "reaction-selector-add-button",
19708
- onClick: () => setExtendedListOpen(true),
19709
- size: "sm",
19710
- variant: "secondary",
19711
- children: /* @__PURE__ */ jsx(IconPlus, {})
19769
+ return /* @__PURE__ */ jsx("div", { className: "str-chat__reaction-selector", "data-testid": "reaction-selector", children: !extendedListOpen ? /* @__PURE__ */ jsxs(Fragment, { children: [
19770
+ /* @__PURE__ */ jsx(
19771
+ "ul",
19772
+ {
19773
+ className: "str-chat__reaction-selector-list",
19774
+ "data-testid": "reaction-selector-list",
19775
+ children: adjustedQuickReactionOptions.map(
19776
+ ({ Component: Component2, name: reactionName, type: reactionType }) => /* @__PURE__ */ jsx("li", { className: "str-chat__reaction-list-selector__item", children: /* @__PURE__ */ jsx(
19777
+ "button",
19778
+ {
19779
+ "aria-label": `Select Reaction: ${reactionName || reactionType}`,
19780
+ "aria-pressed": typeof ownReactionByType[reactionType] !== "undefined",
19781
+ className: clsx("str-chat__reaction-selector-list__item-button"),
19782
+ "data-testid": "select-reaction-button",
19783
+ "data-text": reactionType,
19784
+ onClick: (event) => {
19785
+ handleReaction(reactionType, event);
19786
+ if (closeReactionSelectorOnClick) {
19787
+ dialog.close();
19788
+ }
19789
+ },
19790
+ children: /* @__PURE__ */ jsx("span", { className: "str-chat__reaction-icon", children: /* @__PURE__ */ jsx(Component2, {}) })
19791
+ }
19792
+ ) }, reactionType)
19793
+ )
19794
+ }
19795
+ ),
19796
+ /* @__PURE__ */ jsx(
19797
+ Button,
19798
+ {
19799
+ appearance: "outline",
19800
+ circular: true,
19801
+ className: "str-chat__reaction-selector__add-button",
19802
+ "data-testid": "reaction-selector-add-button",
19803
+ onClick: () => {
19804
+ setExtendedListOpen(true);
19805
+ },
19806
+ size: "sm",
19807
+ variant: "secondary",
19808
+ children: /* @__PURE__ */ jsx(IconPlus, {})
19809
+ }
19810
+ )
19811
+ ] }) : /* @__PURE__ */ jsx(
19812
+ ReactionSelectorExtendedList2,
19813
+ {
19814
+ ...props,
19815
+ dialogId: dialogId2,
19816
+ handleReaction: async (reactionType, event) => {
19817
+ await handleReaction(reactionType, event);
19818
+ if (closeReactionSelectorOnClick) {
19819
+ dialog.close();
19712
19820
  }
19713
- )
19714
- ] }),
19715
- extendedListOpen && /* @__PURE__ */ jsx(ReactionSelectorExtendedList2, { ...props, dialogId: dialogId2 })
19716
- ] });
19821
+ }
19822
+ }
19823
+ ) });
19717
19824
  };
19718
19825
  ReactionSelector.getDialogId = ({ messageId, threadList }) => {
19719
19826
  const dialogIdNamespace = threadList ? "-thread" : "";
@@ -19773,521 +19880,682 @@ ReactionSelector.ExtendedList = function ReactionSelectorExtendedList({
19773
19880
  }
19774
19881
  );
19775
19882
  };
19776
- const ReactionSelectorWithButton = ({
19777
- ReactionIcon
19883
+ function useFetchReactions(options) {
19884
+ const { addNotification } = useNotificationApi();
19885
+ const { handleFetchReactions: contextHandleFetchReactions } = useMessageContext();
19886
+ const { t } = useTranslationContext("useFetchReactions");
19887
+ const [reactions, setReactions] = useState([]);
19888
+ const {
19889
+ handleFetchReactions: propHandleFetchReactions,
19890
+ reactionType,
19891
+ shouldFetch,
19892
+ sort
19893
+ } = options;
19894
+ const [isLoading, setIsLoading] = useState(shouldFetch);
19895
+ const handleFetchReactions = propHandleFetchReactions ?? contextHandleFetchReactions;
19896
+ const [refetchNonce, setRefetchNonce] = useState(null);
19897
+ useEffect(() => {
19898
+ if (!shouldFetch) {
19899
+ return;
19900
+ }
19901
+ let cancel = false;
19902
+ (async () => {
19903
+ try {
19904
+ setIsLoading(true);
19905
+ const reactions2 = await handleFetchReactions(reactionType ?? void 0, sort);
19906
+ if (!cancel) {
19907
+ setReactions(reactions2);
19908
+ }
19909
+ } catch (e) {
19910
+ if (!cancel) {
19911
+ setReactions([]);
19912
+ addNotification({
19913
+ emitter: "Reactions",
19914
+ error: e instanceof Error ? e : void 0,
19915
+ message: t("Error fetching reactions"),
19916
+ severity: "error",
19917
+ type: "api:message:reactions:fetch:failed"
19918
+ });
19919
+ }
19920
+ } finally {
19921
+ if (!cancel) {
19922
+ setIsLoading(false);
19923
+ }
19924
+ }
19925
+ })();
19926
+ return () => {
19927
+ cancel = true;
19928
+ };
19929
+ }, [
19930
+ addNotification,
19931
+ handleFetchReactions,
19932
+ reactionType,
19933
+ refetchNonce,
19934
+ shouldFetch,
19935
+ sort,
19936
+ t
19937
+ ]);
19938
+ const refetch = useCallback(() => {
19939
+ setRefetchNonce(Math.random());
19940
+ }, []);
19941
+ return { isLoading, reactions, refetch };
19942
+ }
19943
+ const defaultReactionDetailsSort = { created_at: -1 };
19944
+ const MessageReactionsDetailLoadingIndicator = () => {
19945
+ const elements = useMemo(
19946
+ () => Array.from({ length: 3 }, (_, index) => /* @__PURE__ */ jsxs("div", { className: "str-chat__message-reactions-detail__skeleton-item", children: [
19947
+ /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__skeleton-avatar" }),
19948
+ /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__skeleton-line" })
19949
+ ] }, index)),
19950
+ []
19951
+ );
19952
+ return /* @__PURE__ */ jsx(Fragment, { children: elements });
19953
+ };
19954
+ const MessageReactionsDetail = ({
19955
+ handleFetchReactions,
19956
+ handleReaction,
19957
+ onSelectedReactionTypeChange,
19958
+ own_reactions,
19959
+ reactionDetailsSort: propReactionDetailsSort,
19960
+ reactionGroups,
19961
+ reactions,
19962
+ selectedReactionType,
19963
+ totalReactionCount
19778
19964
  }) => {
19779
- const { t } = useTranslationContext("ReactionSelectorWithButton");
19780
- const { isMyMessage, message, threadList } = useMessageContext();
19781
- const { ReactionSelector: ReactionSelector$1 = ReactionSelector } = useComponentContext();
19782
- const buttonRef = useRef(null);
19783
- const dialogId2 = ReactionSelector.getDialogId({
19784
- messageId: message.id,
19785
- threadList
19965
+ const [extendedReactionListOpen, setExtendedReactionListOpen] = useState(false);
19966
+ const { client } = useChatContext();
19967
+ const {
19968
+ Avatar: Avatar$1 = Avatar,
19969
+ LoadingIndicator: LoadingIndicator2 = MessageReactionsDetailLoadingIndicator,
19970
+ reactionOptions = defaultReactionOptions,
19971
+ ReactionSelectorExtendedList: ReactionSelectorExtendedList2 = ReactionSelector.ExtendedList
19972
+ } = useComponentContext(MessageReactionsDetail.name);
19973
+ const { t } = useTranslationContext();
19974
+ const {
19975
+ handleReaction: contextHandleReaction,
19976
+ message,
19977
+ reactionDetailsSort: contextReactionDetailsSort
19978
+ } = useMessageContext(MessageReactionsDetail.name);
19979
+ const reactionDetailsSort = propReactionDetailsSort ?? contextReactionDetailsSort ?? defaultReactionDetailsSort;
19980
+ const {
19981
+ isLoading: areReactionsLoading,
19982
+ reactions: reactionDetails,
19983
+ refetch
19984
+ } = useFetchReactions({
19985
+ handleFetchReactions,
19986
+ reactionType: selectedReactionType,
19987
+ shouldFetch: true,
19988
+ sort: reactionDetailsSort
19786
19989
  });
19787
- const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId2 });
19788
- const dialogIsOpen = useDialogIsOpen(dialogId2, dialogManager?.id);
19789
- return /* @__PURE__ */ jsxs(Fragment, { children: [
19790
- /* @__PURE__ */ jsx(
19791
- DialogAnchor,
19990
+ if (extendedReactionListOpen) {
19991
+ return /* @__PURE__ */ jsx(
19992
+ "div",
19792
19993
  {
19793
- dialogManagerId: dialogManager?.id,
19794
- id: dialogId2,
19795
- placement: isMyMessage() ? "top-end" : "top-start",
19796
- referenceElement: buttonRef.current,
19797
- trapFocus: true,
19798
- updatePositionOnContentResize: true,
19799
- children: /* @__PURE__ */ jsx(ReactionSelector$1, {})
19800
- }
19801
- ),
19802
- /* @__PURE__ */ jsx(
19803
- QuickMessageActionsButton,
19804
- {
19805
- "aria-expanded": dialogIsOpen,
19806
- "aria-label": t("aria/Open Reaction Selector"),
19807
- className: "str-chat__message-reactions-button",
19808
- "data-testid": "message-reaction-action",
19809
- onClick: () => dialog?.toggle(),
19810
- ref: buttonRef,
19811
- children: /* @__PURE__ */ jsx(ReactionIcon, { className: "str-chat__message-action-icon" })
19994
+ className: "str-chat__message-reactions-detail",
19995
+ "data-testid": "message-reactions-detail",
19996
+ children: /* @__PURE__ */ jsx(
19997
+ ReactionSelectorExtendedList2,
19998
+ {
19999
+ dialogId: MessageReactionsDetail.getDialogId({ messageId: message.id }),
20000
+ handleReaction,
20001
+ own_reactions
20002
+ }
20003
+ )
19812
20004
  }
19813
- )
19814
- ] });
19815
- };
19816
- const getErrorMessage$1 = (error, fallback) => error instanceof Error && error.message ? error.message : fallback;
19817
- const getNotificationError$1 = (error) => {
19818
- if (error instanceof Error) return error;
19819
- if (typeof error === "string") return new Error(error);
19820
- if (error && typeof error === "object" && "message" in error) {
19821
- const message = error.message;
19822
- if (typeof message === "string") return new Error(message);
20005
+ );
19823
20006
  }
19824
- return void 0;
19825
- };
19826
- const RemindMeSubmenuHeader = () => {
19827
- const { t } = useTranslationContext();
19828
- const { returnToParentMenu } = useContextMenuContext();
19829
- return /* @__PURE__ */ jsx(ContextMenuHeader, { children: /* @__PURE__ */ jsxs(ContextMenuBackButton, { onClick: returnToParentMenu, children: [
19830
- /* @__PURE__ */ jsx(IconChevronLeft, {}),
19831
- /* @__PURE__ */ jsx("span", { children: t("Remind Me") })
19832
- ] }) });
19833
- };
19834
- const RemindMeSubmenu = () => {
19835
- const { t } = useTranslationContext();
19836
- const { client } = useChatContext();
19837
- const { message } = useMessageContext();
19838
- const { closeMenu } = useContextMenuContext();
19839
- const { addNotification } = useNotificationApi();
19840
- return /* @__PURE__ */ jsx(
20007
+ return /* @__PURE__ */ jsxs(
19841
20008
  "div",
19842
20009
  {
19843
- "aria-label": t("aria/Remind Me Options"),
19844
- className: "str-chat__message-actions-box__submenu",
19845
- role: "listbox",
19846
- children: client.reminders.scheduledOffsetsMs.map((offsetMs) => /* @__PURE__ */ jsx(
19847
- ContextMenuButton,
19848
- {
19849
- className: "str-chat__message-actions-list-item-button",
19850
- onClick: async () => {
19851
- try {
19852
- await client.reminders.upsertReminder({
19853
- messageId: message.id,
19854
- remind_at: new Date((/* @__PURE__ */ new Date()).getTime() + offsetMs).toISOString()
19855
- });
19856
- addNotification({
19857
- context: {
19858
- message
19859
- },
19860
- emitter: "MessageActions",
19861
- message: t("Reminder set"),
19862
- severity: "success",
19863
- type: "api:message:reminder:set:success"
19864
- });
19865
- } catch (error) {
19866
- addNotification({
19867
- context: {
19868
- message
19869
- },
19870
- emitter: "MessageActions",
19871
- error: getNotificationError$1(error),
19872
- message: getErrorMessage$1(error, "Error setting reminder"),
19873
- severity: "error",
19874
- type: "api:message:reminder:set:failed"
19875
- });
19876
- } finally {
19877
- closeMenu();
19878
- }
19879
- },
19880
- children: t("duration/Remind Me", { milliseconds: offsetMs })
19881
- },
19882
- `reminder-offset-option--${offsetMs}`
19883
- ))
20010
+ className: "str-chat__message-reactions-detail",
20011
+ "data-testid": "message-reactions-detail",
20012
+ children: [
20013
+ typeof totalReactionCount === "number" && /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__total-count", children: t("{{ count }} reactions", { count: totalReactionCount }) }),
20014
+ /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__reaction-type-list-container", children: /* @__PURE__ */ jsxs(
20015
+ "ul",
20016
+ {
20017
+ className: "str-chat__message-reactions-detail__reaction-type-list",
20018
+ "data-testid": "reaction-type-list",
20019
+ children: [
20020
+ /* @__PURE__ */ jsx("li", { className: "str-chat__message-reactions-detail__reaction-type-list-item", children: /* @__PURE__ */ jsx(
20021
+ "button",
20022
+ {
20023
+ "aria-label": t("Add reaction"),
20024
+ className: "str-chat__message-reactions-detail__reaction-type-list-item-button",
20025
+ "data-testid": "add-reaction-button",
20026
+ onClick: () => setExtendedReactionListOpen(true),
20027
+ children: /* @__PURE__ */ jsx("span", { className: "str-chat__message-reactions-detail__reaction-type-list-item-icon", children: /* @__PURE__ */ jsx(IconEmojiAdd, {}) })
20028
+ }
20029
+ ) }),
20030
+ reactions.map(
20031
+ ({ EmojiComponent, reactionCount, reactionType }) => EmojiComponent && /* @__PURE__ */ jsx(
20032
+ "li",
20033
+ {
20034
+ className: "str-chat__message-reactions-detail__reaction-type-list-item",
20035
+ children: /* @__PURE__ */ jsxs(
20036
+ "button",
20037
+ {
20038
+ "aria-pressed": reactionType === selectedReactionType,
20039
+ className: "str-chat__message-reactions-detail__reaction-type-list-item-button",
20040
+ onClick: () => onSelectedReactionTypeChange?.(
20041
+ selectedReactionType === reactionType ? null : reactionType
20042
+ ),
20043
+ children: [
20044
+ /* @__PURE__ */ jsx("span", { className: "str-chat__message-reactions-detail__reaction-type-list-item-icon", children: /* @__PURE__ */ jsx(EmojiComponent, {}) }),
20045
+ /* @__PURE__ */ jsx(
20046
+ "span",
20047
+ {
20048
+ className: "str-chat__message-reactions-detail__reaction-type-list-item-count",
20049
+ "data-testid": "reaction-type-count",
20050
+ children: reactionCount
20051
+ }
20052
+ )
20053
+ ]
20054
+ }
20055
+ )
20056
+ },
20057
+ reactionType
20058
+ )
20059
+ )
20060
+ ]
20061
+ }
20062
+ ) }),
20063
+ /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__user-list-container", children: /* @__PURE__ */ jsxs(
20064
+ "div",
20065
+ {
20066
+ className: "str-chat__message-reactions-detail__user-list",
20067
+ "data-testid": "all-reacting-users",
20068
+ children: [
20069
+ areReactionsLoading && /* @__PURE__ */ jsx(LoadingIndicator2, {}),
20070
+ !areReactionsLoading && /* @__PURE__ */ jsx(Fragment, { children: reactionDetails.map(({ type, user }) => {
20071
+ const belongsToCurrentUser = client.user?.id === user?.id;
20072
+ const EmojiComponent = Array.isArray(reactionOptions) ? void 0 : reactionOptions.quick[type]?.Component ?? reactionOptions.extended?.[type]?.Component;
20073
+ return /* @__PURE__ */ jsxs(
20074
+ "div",
20075
+ {
20076
+ className: "str-chat__message-reactions-detail__user-list-item",
20077
+ children: [
20078
+ /* @__PURE__ */ jsx(
20079
+ Avatar$1,
20080
+ {
20081
+ className: "str-chat__avatar--with-border",
20082
+ "data-testid": "avatar",
20083
+ imageUrl: user?.image,
20084
+ size: "md",
20085
+ userName: user?.name || user?.id
20086
+ }
20087
+ ),
20088
+ /* @__PURE__ */ jsxs("div", { className: "str-chat__message-reactions-detail__user-list-item-info", children: [
20089
+ /* @__PURE__ */ jsx(
20090
+ "span",
20091
+ {
20092
+ className: "str-chat__message-reactions-detail__user-list-item-username",
20093
+ "data-testid": "reaction-user-username",
20094
+ children: belongsToCurrentUser ? t("You") : user?.name || user?.id
20095
+ }
20096
+ ),
20097
+ belongsToCurrentUser && /* @__PURE__ */ jsx(
20098
+ "button",
20099
+ {
20100
+ className: "str-chat__message-reactions-detail__user-list-item-button",
20101
+ "data-testid": "remove-reaction-button",
20102
+ onClick: async (e) => {
20103
+ const reactionCountBeforeRemoval = reactionGroups?.[type]?.count ?? 0;
20104
+ await contextHandleReaction(type, e);
20105
+ if (selectedReactionType !== null && reactionCountBeforeRemoval <= 1) {
20106
+ onSelectedReactionTypeChange?.(null);
20107
+ } else {
20108
+ refetch();
20109
+ }
20110
+ },
20111
+ children: t("Tap to remove")
20112
+ }
20113
+ )
20114
+ ] }),
20115
+ /* @__PURE__ */ jsx("span", { className: "str-chat__message-reactions-detail__user-list-item-icon", children: !selectedReactionType && EmojiComponent && /* @__PURE__ */ jsx(EmojiComponent, {}) })
20116
+ ]
20117
+ },
20118
+ `${user?.id}-${type}`
20119
+ );
20120
+ }) })
20121
+ ]
20122
+ }
20123
+ ) })
20124
+ ]
19884
20125
  }
19885
20126
  );
19886
20127
  };
19887
- const QuickMessageActionsButton = ({ className, ...props }) => /* @__PURE__ */ jsx(
19888
- Button,
19889
- {
19890
- appearance: "ghost",
19891
- circular: true,
19892
- className: clsx("str-chat__message-actions-box-button", className),
19893
- size: "sm",
19894
- variant: "secondary",
19895
- ...props
20128
+ MessageReactionsDetail.displayName = "MessageReactionsDetail";
20129
+ MessageReactionsDetail.getDialogId = ({ messageId }) => `message-reactions-detail-${messageId}`;
20130
+ const defaultReactionsSort = (a, b) => {
20131
+ if (a.firstReactionAt && b.firstReactionAt) {
20132
+ return +a.firstReactionAt - +b.firstReactionAt;
19896
20133
  }
19897
- );
19898
- const DeleteMessageAlert = ({ onCancel, onDelete }) => {
19899
- const { t } = useTranslationContext();
19900
- const { close } = useModalContext();
19901
- return /* @__PURE__ */ jsxs(
19902
- Alert.Root,
19903
- {
19904
- className: "str-chat__delete-message-alert",
19905
- "data-testid": "message-delete-alert",
19906
- children: [
19907
- /* @__PURE__ */ jsx(
19908
- Alert.Header,
19909
- {
19910
- description: t("Are you sure you want to delete this message?"),
19911
- title: t("Delete message")
19912
- }
19913
- ),
19914
- /* @__PURE__ */ jsxs(Alert.Actions, { children: [
19915
- /* @__PURE__ */ jsx(
19916
- Button,
19917
- {
19918
- appearance: "outline",
19919
- className: "str-chat__delete-message-alert__delete-button",
19920
- "data-testid": "delete-message-alert-delete-button",
19921
- onClick: onDelete,
19922
- size: "md",
19923
- variant: "danger",
19924
- children: t("Delete message")
19925
- }
19926
- ),
19927
- /* @__PURE__ */ jsx(
19928
- Button,
19929
- {
19930
- appearance: "outline",
19931
- className: "str-chat__delete-message-alert__cancel-button",
19932
- "data-testid": "delete-message-alert-cancel-button",
19933
- onClick: () => {
19934
- onCancel();
19935
- close();
19936
- },
19937
- size: "md",
19938
- variant: "secondary",
19939
- children: t("Cancel")
19940
- }
19941
- )
19942
- ] })
19943
- ]
20134
+ return a.reactionType.localeCompare(b.reactionType, "en");
20135
+ };
20136
+ const useProcessReactions = (params) => {
20137
+ const {
20138
+ own_reactions: propOwnReactions,
20139
+ reaction_groups: propReactionGroups,
20140
+ reactions: propReactions,
20141
+ sortReactions: propSortReactions
20142
+ } = params;
20143
+ const { message, sortReactions: contextSortReactions } = useMessageContext();
20144
+ const { reactionOptions = defaultReactionOptions } = useComponentContext();
20145
+ const sortReactions = propSortReactions ?? contextSortReactions ?? defaultReactionsSort;
20146
+ const latestReactions = propReactions ?? message.latest_reactions;
20147
+ const ownReactions = propOwnReactions ?? message?.own_reactions;
20148
+ const reactionGroups = propReactionGroups ?? message?.reaction_groups ?? void 0;
20149
+ const isOwnReaction = useCallback(
20150
+ (reactionType) => ownReactions?.some((reaction) => reaction.type === reactionType) ?? false,
20151
+ [ownReactions]
20152
+ );
20153
+ const getEmojiByReactionType = useCallback(
20154
+ (reactionType) => {
20155
+ if (Array.isArray(reactionOptions)) {
20156
+ return reactionOptions.find(({ type }) => type === reactionType)?.Component ?? null;
20157
+ }
20158
+ return reactionOptions.quick[reactionType]?.Component ?? reactionOptions.extended?.[reactionType]?.Component ?? null;
20159
+ },
20160
+ [reactionOptions]
20161
+ );
20162
+ const isSupportedReaction = useCallback(
20163
+ (reactionType) => {
20164
+ if (Array.isArray(reactionOptions)) {
20165
+ return reactionOptions.some(
20166
+ (reactionOption) => reactionOption.type === reactionType
20167
+ );
20168
+ }
20169
+ return typeof reactionOptions.quick[reactionType] !== "undefined" || typeof reactionOptions.extended?.[reactionType] !== "undefined";
20170
+ },
20171
+ [reactionOptions]
20172
+ );
20173
+ const uniqueReactionTypeCount = useMemo(() => {
20174
+ if (!reactionGroups) {
20175
+ return 0;
20176
+ }
20177
+ return Object.keys(reactionGroups).filter(
20178
+ (reactionType) => isSupportedReaction(reactionType)
20179
+ ).length;
20180
+ }, [isSupportedReaction, reactionGroups]);
20181
+ const getLatestReactedUserNames = useCallback(
20182
+ (reactionType) => latestReactions?.flatMap((reaction) => {
20183
+ if (reactionType && reactionType === reaction.type) {
20184
+ const username = reaction.user?.name || reaction.user?.id;
20185
+ return username ? [username] : [];
20186
+ }
20187
+ return [];
20188
+ }) ?? [],
20189
+ [latestReactions]
20190
+ );
20191
+ const existingReactions = useMemo(() => {
20192
+ if (!reactionGroups) {
20193
+ return [];
19944
20194
  }
20195
+ const unsortedReactions = Object.entries(reactionGroups).flatMap(
20196
+ ([reactionType, { count, first_reaction_at, last_reaction_at }]) => {
20197
+ if (count === 0 || !isSupportedReaction(reactionType)) {
20198
+ return [];
20199
+ }
20200
+ const latestReactedUserNames = getLatestReactedUserNames(reactionType);
20201
+ return [
20202
+ {
20203
+ EmojiComponent: getEmojiByReactionType(reactionType),
20204
+ firstReactionAt: first_reaction_at ? new Date(first_reaction_at) : null,
20205
+ isOwnReaction: isOwnReaction(reactionType),
20206
+ lastReactionAt: last_reaction_at ? new Date(last_reaction_at) : null,
20207
+ latestReactedUserNames,
20208
+ reactionCount: count,
20209
+ reactionType,
20210
+ unlistedReactedUserCount: count - latestReactedUserNames.length
20211
+ }
20212
+ ];
20213
+ }
20214
+ );
20215
+ return unsortedReactions.sort(sortReactions);
20216
+ }, [
20217
+ getEmojiByReactionType,
20218
+ getLatestReactedUserNames,
20219
+ isOwnReaction,
20220
+ isSupportedReaction,
20221
+ reactionGroups,
20222
+ sortReactions
20223
+ ]);
20224
+ const hasReactions = existingReactions.length > 0;
20225
+ const totalReactionCount = useMemo(
20226
+ () => Object.values(reactionGroups ?? {}).reduce((total, { count }) => total + count, 0),
20227
+ [reactionGroups]
19945
20228
  );
20229
+ return {
20230
+ existingReactions,
20231
+ hasReactions,
20232
+ reactionGroups,
20233
+ totalReactionCount,
20234
+ uniqueReactionTypeCount
20235
+ };
19946
20236
  };
19947
- const msgActionsBoxButtonClassName = "str-chat__message-actions-list-item-button";
19948
- const getErrorMessage = (error, fallback) => error instanceof Error && error.message ? error.message : fallback;
19949
- const getNotificationError = (error) => {
19950
- if (error instanceof Error) return error;
19951
- if (typeof error === "string") return new Error(error);
19952
- if (error && typeof error === "object" && "message" in error) {
19953
- const message = error.message;
19954
- if (typeof message === "string") return new Error(message);
20237
+ const FragmentOrButton = ({
20238
+ buttonIf: renderButton = false,
20239
+ children,
20240
+ ...props
20241
+ }) => {
20242
+ if (renderButton) {
20243
+ return /* @__PURE__ */ jsx("button", { ...props, children });
19955
20244
  }
19956
- return void 0;
20245
+ return /* @__PURE__ */ jsx(Fragment, { children });
19957
20246
  };
19958
- const DefaultMessageActionComponents = {
19959
- dropdown: {
19960
- ThreadReply() {
19961
- const { closeMenu } = useContextMenuContext();
19962
- const { handleOpenThread } = useMessageContext();
19963
- const { t } = useTranslationContext();
19964
- return /* @__PURE__ */ jsx(
19965
- ContextMenuButton,
19966
- {
19967
- "aria-label": t("aria/Open Thread"),
19968
- className: msgActionsBoxButtonClassName,
19969
- "data-testid": "thread-action",
19970
- Icon: IconThread,
19971
- onClick: (e) => {
19972
- handleOpenThread(e);
19973
- closeMenu();
19974
- },
19975
- children: t("Thread Reply")
19976
- }
19977
- );
19978
- },
19979
- Quote() {
19980
- const { closeMenu } = useContextMenuContext();
19981
- const { message } = useMessageContext();
19982
- const { t } = useTranslationContext();
19983
- const messageComposer = useMessageComposerController();
19984
- const handleQuote = () => {
19985
- messageComposer.setQuotedMessage(message);
19986
- const elements = message.parent_id ? document.querySelectorAll(".str-chat__thread .str-chat__textarea__textarea") : document.getElementsByClassName("str-chat__textarea__textarea");
19987
- const textarea = elements.item(0);
19988
- if (textarea instanceof HTMLTextAreaElement) {
19989
- textarea.focus();
19990
- }
19991
- };
19992
- return /* @__PURE__ */ jsx(
19993
- ContextMenuButton,
19994
- {
19995
- "aria-label": t("aria/Quote Message"),
19996
- className: msgActionsBoxButtonClassName,
19997
- Icon: IconQuote,
19998
- onClick: () => {
19999
- handleQuote();
20000
- closeMenu();
20001
- },
20002
- children: t("Quote Reply")
20003
- }
20004
- );
20005
- },
20006
- Pin() {
20007
- const { closeMenu } = useContextMenuContext();
20008
- const { handlePin, message } = useMessageContext();
20009
- const { addNotification } = useNotificationApi();
20010
- const { t } = useTranslationContext();
20011
- const isPinned = !!message.pinned;
20012
- return /* @__PURE__ */ jsx(
20013
- ContextMenuButton,
20014
- {
20015
- "aria-label": isPinned ? t("aria/Unpin Message") : t("aria/Pin Message"),
20016
- className: msgActionsBoxButtonClassName,
20017
- Icon: isPinned ? IconUnpin : IconPin,
20018
- onClick: async (event) => {
20019
- try {
20020
- await handlePin(event);
20021
- addNotification({
20022
- context: {
20023
- message
20024
- },
20025
- emitter: "MessageActions",
20026
- message: isPinned ? t("Message unpinned") : t("Message pinned"),
20027
- severity: "success",
20028
- type: isPinned ? "api:message:unpin:success" : "api:message:pin:success"
20029
- });
20030
- } catch (error) {
20031
- addNotification({
20032
- context: {
20033
- message
20034
- },
20035
- emitter: "MessageActions",
20036
- error: getNotificationError(error),
20037
- message: getErrorMessage(
20038
- error,
20039
- isPinned ? t("Error removing message pin") : t("Error pinning message")
20040
- ),
20041
- severity: "error",
20042
- type: isPinned ? "api:message:unpin:failed" : "api:message:pin:failed"
20043
- });
20044
- }
20045
- closeMenu();
20046
- },
20047
- children: isPinned ? t("Unpin") : t("Pin")
20048
- }
20049
- );
20050
- },
20051
- CopyMessageText() {
20052
- const { closeMenu } = useContextMenuContext();
20053
- const { message } = useMessageContext();
20054
- const { t } = useTranslationContext();
20055
- return /* @__PURE__ */ jsx(
20056
- ContextMenuButton,
20057
- {
20058
- "aria-label": t("aria/Copy Message Text"),
20059
- className: msgActionsBoxButtonClassName,
20060
- Icon: IconCopy,
20061
- onClick: () => {
20062
- if (message.text) navigator.clipboard.writeText(message.text);
20063
- closeMenu();
20064
- },
20065
- children: t("Copy Message")
20066
- }
20067
- );
20068
- },
20069
- Resend() {
20070
- const { closeMenu } = useContextMenuContext();
20071
- const { handleRetry, message } = useMessageContext();
20072
- const { t } = useTranslationContext();
20073
- return /* @__PURE__ */ jsx(
20074
- ContextMenuButton,
20075
- {
20076
- "aria-label": t("aria/Resend Message"),
20077
- className: msgActionsBoxButtonClassName,
20078
- Icon: IconRetry,
20079
- onClick: () => {
20080
- handleRetry(message);
20081
- closeMenu();
20082
- },
20083
- children: t("Resend")
20084
- }
20085
- );
20086
- },
20087
- Edit() {
20088
- const messageComposer = useMessageComposerController();
20089
- const { message } = useMessageContext();
20090
- const { t } = useTranslationContext();
20091
- const { closeMenu } = useContextMenuContext();
20092
- return /* @__PURE__ */ jsx(
20093
- ContextMenuButton,
20094
- {
20095
- "aria-label": t("aria/Edit Message"),
20096
- className: msgActionsBoxButtonClassName,
20097
- Icon: IconEdit,
20098
- onClick: () => {
20099
- savePreEditSnapshot(messageComposer);
20100
- messageComposer.initState({ composition: message });
20101
- closeMenu();
20102
- },
20103
- children: t("Edit Message")
20104
- }
20105
- );
20106
- },
20107
- MarkUnread() {
20108
- const { closeMenu } = useContextMenuContext();
20109
- const { handleMarkUnread, message } = useMessageContext();
20110
- const { addNotification } = useNotificationApi();
20111
- const { t } = useTranslationContext();
20112
- return /* @__PURE__ */ jsx(
20113
- ContextMenuButton,
20114
- {
20115
- "aria-label": t("aria/Mark Message Unread"),
20116
- className: msgActionsBoxButtonClassName,
20117
- Icon: IconNotification,
20118
- onClick: async (event) => {
20119
- try {
20120
- await handleMarkUnread(event);
20121
- addNotification({
20122
- context: {
20123
- message
20124
- },
20125
- emitter: "MessageActions",
20126
- message: t("Message marked as unread"),
20127
- severity: "success",
20128
- type: "api:message:markUnread:success"
20129
- });
20130
- } catch (error) {
20131
- addNotification({
20132
- context: {
20133
- message
20134
- },
20135
- emitter: "MessageActions",
20136
- error: getNotificationError(error),
20137
- message: getErrorMessage(
20138
- error,
20139
- t(
20140
- "Error marking message unread. Cannot mark unread messages older than the newest 100 channel messages."
20247
+ const UnMemoizedMessageReactions = (props) => {
20248
+ const {
20249
+ capLimit: { clustered: capLimitClustered = 5, segmented: capLimitSegmented = 4 } = {},
20250
+ flipHorizontalPosition = false,
20251
+ handleFetchReactions,
20252
+ reactionDetailsSort,
20253
+ verticalPosition = "top",
20254
+ visualStyle = "clustered",
20255
+ ...rest
20256
+ } = props;
20257
+ const {
20258
+ existingReactions,
20259
+ hasReactions,
20260
+ reactionGroups,
20261
+ totalReactionCount,
20262
+ uniqueReactionTypeCount
20263
+ } = useProcessReactions(rest);
20264
+ const [selectedReactionType, setSelectedReactionType] = useState(
20265
+ null
20266
+ );
20267
+ const { t } = useTranslationContext("MessageReactions");
20268
+ const { MessageReactionsDetail: MessageReactionsDetail$1 = MessageReactionsDetail } = useComponentContext();
20269
+ const { isMyMessage, message } = useMessageContext();
20270
+ const divRef = useRef(null);
20271
+ const dialogId2 = MessageReactionsDetail.getDialogId({
20272
+ messageId: message.id
20273
+ });
20274
+ const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId2 });
20275
+ const isDialogOpen = useDialogIsOpen(dialogId2, dialogManager?.id);
20276
+ const handleReactionButtonClick = (reactionType) => {
20277
+ if (totalReactionCount > MAX_MESSAGE_REACTIONS_TO_FETCH) {
20278
+ return;
20279
+ }
20280
+ setSelectedReactionType(reactionType);
20281
+ dialog.open();
20282
+ };
20283
+ const cappedExistingReactions = useMemo(() => {
20284
+ if (visualStyle === "segmented" && verticalPosition !== "top") return null;
20285
+ const capLimit = visualStyle === "segmented" ? capLimitSegmented : capLimitClustered;
20286
+ const sliced = existingReactions.slice(0, capLimit);
20287
+ return {
20288
+ /**
20289
+ * Accumulated reaction count of capped reaction types, first four in case of
20290
+ * segmented(top) and first five in case of clustered(top/bottom) variations.
20291
+ */
20292
+ reactionCountToDisplay: sliced.reduce(
20293
+ (accumulatedCount, { reactionCount }) => accumulatedCount + reactionCount,
20294
+ 0
20295
+ ),
20296
+ reactionsToDisplay: sliced
20297
+ };
20298
+ }, [
20299
+ capLimitClustered,
20300
+ capLimitSegmented,
20301
+ existingReactions,
20302
+ verticalPosition,
20303
+ visualStyle
20304
+ ]);
20305
+ if (!hasReactions) return null;
20306
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
20307
+ /* @__PURE__ */ jsx(
20308
+ "div",
20309
+ {
20310
+ "aria-label": t("aria/Reaction list"),
20311
+ className: clsx("str-chat__message-reactions", {
20312
+ [`str-chat__message-reactions--flipped-horizontally`]: flipHorizontalPosition,
20313
+ [`str-chat__message-reactions--${verticalPosition}`]: typeof verticalPosition === "string",
20314
+ [`str-chat__message-reactions--${visualStyle}`]: typeof visualStyle === "string"
20315
+ }),
20316
+ ref: divRef,
20317
+ role: "figure",
20318
+ children: /* @__PURE__ */ jsxs(
20319
+ FragmentOrButton,
20320
+ {
20321
+ "aria-expanded": isDialogOpen,
20322
+ "aria-pressed": isDialogOpen,
20323
+ buttonIf: visualStyle === "clustered",
20324
+ className: "str-chat__message-reactions__list-button",
20325
+ "data-testid": "message-reactions-list-button",
20326
+ onClick: () => handleReactionButtonClick(null),
20327
+ children: [
20328
+ /* @__PURE__ */ jsxs("ul", { className: "str-chat__message-reactions__list", children: [
20329
+ (cappedExistingReactions?.reactionsToDisplay ?? existingReactions).map(
20330
+ ({ EmojiComponent, reactionCount, reactionType }) => EmojiComponent && /* @__PURE__ */ jsx(
20331
+ "li",
20332
+ {
20333
+ className: "str-chat__message-reactions__list-item",
20334
+ "data-testid": "message-reactions-list-item",
20335
+ children: /* @__PURE__ */ jsxs(
20336
+ FragmentOrButton,
20337
+ {
20338
+ buttonIf: visualStyle === "segmented",
20339
+ className: "str-chat__message-reactions__list-item-button",
20340
+ onClick: () => handleReactionButtonClick(reactionType),
20341
+ children: [
20342
+ /* @__PURE__ */ jsx(
20343
+ "span",
20344
+ {
20345
+ className: "str-chat__message-reactions__list-item-icon",
20346
+ "data-testid": "message-reactions-list-item-icon",
20347
+ children: /* @__PURE__ */ jsx(EmojiComponent, {})
20348
+ }
20349
+ ),
20350
+ visualStyle === "segmented" && reactionCount > 1 && /* @__PURE__ */ jsx(
20351
+ "span",
20352
+ {
20353
+ className: "str-chat__message-reactions__list-item-count",
20354
+ "data-testclass": "message-reactions-item-count",
20355
+ children: reactionCount
20356
+ }
20357
+ )
20358
+ ]
20359
+ }
20360
+ )
20361
+ },
20362
+ reactionType
20141
20363
  )
20142
20364
  ),
20143
- severity: "error",
20144
- type: "api:message:markUnread:failed"
20145
- });
20146
- }
20147
- closeMenu();
20148
- },
20149
- children: t("Mark as unread")
20150
- }
20151
- );
20365
+ uniqueReactionTypeCount > 4 && cappedExistingReactions && visualStyle === "segmented" && /* @__PURE__ */ jsx("li", { className: "str-chat__message-reactions__list-item str-chat__message-reactions__list-item--more", children: /* @__PURE__ */ jsx(
20366
+ "button",
20367
+ {
20368
+ className: "str-chat__message-reactions__list-item-button",
20369
+ onClick: () => handleReactionButtonClick(null),
20370
+ children: /* @__PURE__ */ jsxs("span", { className: "str-chat__message-reactions__overflow-count", children: [
20371
+ "+",
20372
+ totalReactionCount - cappedExistingReactions.reactionCountToDisplay
20373
+ ] })
20374
+ }
20375
+ ) })
20376
+ ] }),
20377
+ visualStyle === "clustered" && /* @__PURE__ */ jsx(
20378
+ "span",
20379
+ {
20380
+ className: "str-chat__message-reactions__total-count",
20381
+ "data-testid": "message-reactions-total-count",
20382
+ children: totalReactionCount
20383
+ }
20384
+ )
20385
+ ]
20386
+ }
20387
+ )
20388
+ }
20389
+ ),
20390
+ /* @__PURE__ */ jsx(
20391
+ DialogAnchor,
20392
+ {
20393
+ dialogManagerId: dialogManager?.id,
20394
+ id: dialogId2,
20395
+ offset: 8,
20396
+ placement: isMyMessage() ? "bottom-end" : "bottom-start",
20397
+ referenceElement: divRef.current,
20398
+ trapFocus: true,
20399
+ updatePositionOnContentResize: true,
20400
+ children: /* @__PURE__ */ jsx(
20401
+ MessageReactionsDetail$1,
20402
+ {
20403
+ handleFetchReactions,
20404
+ onSelectedReactionTypeChange: setSelectedReactionType,
20405
+ reactionDetailsSort,
20406
+ reactionGroups,
20407
+ reactions: existingReactions,
20408
+ selectedReactionType,
20409
+ totalReactionCount
20410
+ }
20411
+ )
20412
+ }
20413
+ )
20414
+ ] });
20415
+ };
20416
+ const MessageReactions = React.memo(
20417
+ UnMemoizedMessageReactions
20418
+ );
20419
+ const getImageDimensions = (source) => new Promise((resolve, reject) => {
20420
+ const image = new Image();
20421
+ image.addEventListener(
20422
+ "load",
20423
+ () => {
20424
+ resolve([image.width, image.height]);
20152
20425
  },
20153
- RemindMe() {
20154
- const { closeMenu, openSubmenu } = useContextMenuContext();
20155
- const { client } = useChatContext();
20156
- const { addNotification } = useNotificationApi();
20157
- const { t } = useTranslationContext();
20158
- const { message } = useMessageContext();
20159
- const reminder = useMessageReminder(message.id);
20160
- const messageAlreadyBookmarked = reminder && !reminder?.remindAt;
20161
- if (messageAlreadyBookmarked) return null;
20162
- return /* @__PURE__ */ jsx(
20163
- ContextMenuButton,
20164
- {
20165
- "aria-label": reminder ? t("aria/Remind Me Message") : t("aria/Remove Reminder"),
20166
- className: msgActionsBoxButtonClassName,
20167
- hasSubMenu: !reminder,
20168
- Icon: reminder ? IconBellOff : IconBell,
20169
- onClick: async () => {
20170
- if (reminder) {
20171
- try {
20172
- await client.reminders.deleteReminder(reminder.id);
20173
- addNotification({
20174
- context: {
20175
- message
20176
- },
20177
- emitter: "MessageActions",
20178
- message: t("Remove reminder"),
20179
- severity: "success",
20180
- type: "api:message:reminder:delete:success"
20181
- });
20182
- } catch (error) {
20183
- addNotification({
20184
- context: {
20185
- message
20186
- },
20187
- emitter: "MessageActions",
20188
- error: getNotificationError(error),
20189
- message: getErrorMessage(error, "Error removing reminder"),
20190
- severity: "error",
20191
- type: "api:message:reminder:delete:failed"
20192
- });
20193
- } finally {
20194
- closeMenu();
20195
- }
20196
- } else {
20197
- openSubmenu({
20198
- Header: RemindMeSubmenuHeader,
20199
- Submenu: RemindMeSubmenu
20200
- });
20201
- }
20202
- },
20203
- children: reminder ? t("Remove reminder") : t("Remind me")
20204
- }
20205
- );
20206
- },
20207
- SaveForLater() {
20208
- const { closeMenu } = useContextMenuContext();
20209
- const { client } = useChatContext();
20210
- const { addNotification } = useNotificationApi();
20211
- const { message } = useMessageContext();
20212
- const { t } = useTranslationContext();
20213
- const reminder = useMessageReminder(message.id);
20214
- const messageAlreadyHasReminderScheduled = Boolean(reminder && reminder?.remindAt);
20215
- if (messageAlreadyHasReminderScheduled) return null;
20216
- return /* @__PURE__ */ jsx(
20426
+ { once: true }
20427
+ );
20428
+ image.addEventListener("error", () => reject(`Couldn't load image from ${source}`), {
20429
+ once: true
20430
+ });
20431
+ image.src = source;
20432
+ });
20433
+ const SpriteImage = ({
20434
+ columns,
20435
+ fallback,
20436
+ height,
20437
+ position,
20438
+ rows,
20439
+ spriteUrl,
20440
+ style,
20441
+ width
20442
+ }) => {
20443
+ const [[spriteWidth, spriteHeight], setSpriteDimensions] = useState([0, 0]);
20444
+ useEffect(() => {
20445
+ getImageDimensions(spriteUrl).then(setSpriteDimensions).catch(console.error);
20446
+ }, [spriteUrl]);
20447
+ const [x, y] = position;
20448
+ if (!spriteHeight || !spriteWidth) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
20449
+ return /* @__PURE__ */ jsx(
20450
+ "div",
20451
+ {
20452
+ "data-testid": "sprite-image",
20453
+ style: {
20454
+ ...style,
20455
+ "--str-chat__sprite-image-resize-ratio": "var(--str-chat__sprite-image-resize-ratio-x, var(--str-chat__sprite-image-resize-ratio-y, 1))",
20456
+ "--str-chat__sprite-image-resize-ratio-x": "calc(var(--str-chat__sprite-image-width) / var(--str-chat__sprite-item-width))",
20457
+ "--str-chat__sprite-image-resize-ratio-y": "calc(var(--str-chat__sprite-image-height) / var(--str-chat__sprite-item-height))",
20458
+ "--str-chat__sprite-item-height": `${spriteHeight / rows}`,
20459
+ "--str-chat__sprite-item-width": `${spriteWidth / columns}`,
20460
+ ...Number.isFinite(height) ? { "--str-chat__sprite-image-height": `${height}px` } : {},
20461
+ ...Number.isFinite(width) ? { "--str-chat__sprite-image-width": `${width}px` } : {},
20462
+ backgroundImage: `url('${spriteUrl}')`,
20463
+ backgroundPosition: `${x * (100 / (columns - 1))}% ${y * (100 / (rows - 1))}%`,
20464
+ backgroundSize: `${columns * 100}% ${rows * 100}%`,
20465
+ height: "var(--str-chat__sprite-image-height, calc(var(--str-chat__sprite-item-height) * var(--str-chat__sprite-image-resize-ratio)))",
20466
+ width: "var(--str-chat__sprite-image-width, calc(var(--str-chat__sprite-item-width) * var(--str-chat__sprite-image-resize-ratio)))"
20467
+ }
20468
+ }
20469
+ );
20470
+ };
20471
+ const ReactionSelectorWithButton = ({
20472
+ ReactionIcon
20473
+ }) => {
20474
+ const { t } = useTranslationContext("ReactionSelectorWithButton");
20475
+ const { isMyMessage, message, threadList } = useMessageContext();
20476
+ const { ReactionSelector: ReactionSelector$1 = ReactionSelector } = useComponentContext();
20477
+ const buttonRef = useRef(null);
20478
+ const dialogId2 = ReactionSelector.getDialogId({
20479
+ messageId: message.id,
20480
+ threadList
20481
+ });
20482
+ const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId2 });
20483
+ const dialogIsOpen = useDialogIsOpen(dialogId2, dialogManager?.id);
20484
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
20485
+ /* @__PURE__ */ jsx(
20486
+ DialogAnchor,
20487
+ {
20488
+ dialogManagerId: dialogManager?.id,
20489
+ id: dialogId2,
20490
+ placement: isMyMessage() ? "top-end" : "top-start",
20491
+ referenceElement: buttonRef.current,
20492
+ trapFocus: true,
20493
+ updatePositionOnContentResize: true,
20494
+ children: /* @__PURE__ */ jsx(ReactionSelector$1, {})
20495
+ }
20496
+ ),
20497
+ /* @__PURE__ */ jsx(
20498
+ QuickMessageActionsButton,
20499
+ {
20500
+ "aria-expanded": dialogIsOpen,
20501
+ "aria-label": t("aria/Open Reaction Selector"),
20502
+ className: "str-chat__message-reactions-button",
20503
+ "data-testid": "message-reaction-action",
20504
+ onClick: () => dialog?.toggle(),
20505
+ ref: buttonRef,
20506
+ children: /* @__PURE__ */ jsx(ReactionIcon, { className: "str-chat__message-action-icon" })
20507
+ }
20508
+ )
20509
+ ] });
20510
+ };
20511
+ const getErrorMessage$1 = (error, fallback) => error instanceof Error && error.message ? error.message : fallback;
20512
+ const getNotificationError$1 = (error) => {
20513
+ if (error instanceof Error) return error;
20514
+ if (typeof error === "string") return new Error(error);
20515
+ if (error && typeof error === "object" && "message" in error) {
20516
+ const message = error.message;
20517
+ if (typeof message === "string") return new Error(message);
20518
+ }
20519
+ return void 0;
20520
+ };
20521
+ const RemindMeSubmenuHeader = () => {
20522
+ const { t } = useTranslationContext();
20523
+ const { returnToParentMenu } = useContextMenuContext();
20524
+ return /* @__PURE__ */ jsx(ContextMenuHeader, { children: /* @__PURE__ */ jsxs(ContextMenuBackButton, { onClick: returnToParentMenu, children: [
20525
+ /* @__PURE__ */ jsx(IconChevronLeft, {}),
20526
+ /* @__PURE__ */ jsx("span", { children: t("Remind Me") })
20527
+ ] }) });
20528
+ };
20529
+ const RemindMeSubmenu = () => {
20530
+ const { t } = useTranslationContext();
20531
+ const { client } = useChatContext();
20532
+ const { message } = useMessageContext();
20533
+ const { closeMenu } = useContextMenuContext();
20534
+ const { addNotification } = useNotificationApi();
20535
+ return /* @__PURE__ */ jsx(
20536
+ "div",
20537
+ {
20538
+ "aria-label": t("aria/Remind Me Options"),
20539
+ className: "str-chat__message-actions-box__submenu",
20540
+ role: "listbox",
20541
+ children: client.reminders.scheduledOffsetsMs.map((offsetMs) => /* @__PURE__ */ jsx(
20217
20542
  ContextMenuButton,
20218
20543
  {
20219
- "aria-label": reminder ? t("aria/Remove Save For Later") : t("aria/Bookmark Message"),
20220
- className: msgActionsBoxButtonClassName,
20221
- Icon: reminder ? IconBookmarkRemove : IconBookmark,
20544
+ className: "str-chat__message-actions-list-item-button",
20222
20545
  onClick: async () => {
20223
20546
  try {
20224
- if (reminder) {
20225
- await client.reminders.deleteReminder(reminder.id);
20226
- addNotification({
20227
- context: {
20228
- message
20229
- },
20230
- emitter: "MessageActions",
20231
- message: t("Remove save for later"),
20232
- severity: "success",
20233
- type: "api:message:saveForLater:delete:success"
20234
- });
20235
- } else {
20236
- await client.reminders.createReminder({ messageId: message.id });
20237
- addNotification({
20238
- context: {
20239
- message
20240
- },
20241
- emitter: "MessageActions",
20242
- message: t("Saved for later"),
20243
- severity: "success",
20244
- type: "api:message:saveForLater:create:success"
20245
- });
20246
- }
20247
- } catch (error) {
20248
- addNotification({
20249
- context: {
20250
- message
20251
- },
20252
- emitter: "MessageActions",
20253
- error: getNotificationError(error),
20254
- message: getErrorMessage(
20255
- error,
20256
- reminder ? "Error removing message from saved for later" : "Error saving message for later"
20257
- ),
20258
- severity: "error",
20259
- type: reminder ? "api:message:saveForLater:delete:failed" : "api:message:saveForLater:create:failed"
20547
+ await client.reminders.upsertReminder({
20548
+ messageId: message.id,
20549
+ remind_at: new Date((/* @__PURE__ */ new Date()).getTime() + offsetMs).toISOString()
20260
20550
  });
20261
- } finally {
20262
- closeMenu();
20263
- }
20264
- },
20265
- children: reminder ? t("Remove save for later") : t("Save for later")
20266
- }
20267
- );
20268
- },
20269
- Flag() {
20270
- const { closeMenu } = useContextMenuContext();
20271
- const { handleFlag, message } = useMessageContext();
20272
- const { addNotification } = useNotificationApi();
20273
- const { t } = useTranslationContext();
20274
- return /* @__PURE__ */ jsx(
20275
- ContextMenuButton,
20276
- {
20277
- "aria-label": t("aria/Flag Message"),
20278
- className: msgActionsBoxButtonClassName,
20279
- Icon: IconFlag,
20280
- onClick: async (event) => {
20281
- try {
20282
- await handleFlag(event);
20283
20551
  addNotification({
20284
20552
  context: {
20285
20553
  message
20286
20554
  },
20287
20555
  emitter: "MessageActions",
20288
- message: t("Message has been successfully flagged"),
20556
+ message: t("Reminder set"),
20289
20557
  severity: "success",
20290
- type: "api:message:flag:success"
20558
+ type: "api:message:reminder:set:success"
20291
20559
  });
20292
20560
  } catch (error) {
20293
20561
  addNotification({
@@ -20295,46 +20563,218 @@ const DefaultMessageActionComponents = {
20295
20563
  message
20296
20564
  },
20297
20565
  emitter: "MessageActions",
20298
- error: getNotificationError(error),
20299
- message: getErrorMessage(error, t("Error adding flag")),
20566
+ error: getNotificationError$1(error),
20567
+ message: getErrorMessage$1(error, "Error setting reminder"),
20300
20568
  severity: "error",
20301
- type: "api:message:flag:failed"
20569
+ type: "api:message:reminder:set:failed"
20302
20570
  });
20571
+ } finally {
20572
+ closeMenu();
20303
20573
  }
20304
- closeMenu();
20305
20574
  },
20306
- children: t("Flag")
20307
- }
20308
- );
20309
- },
20310
- Mute() {
20311
- const { closeMenu } = useContextMenuContext();
20312
- const { handleMute, message } = useMessageContext();
20313
- const { addNotification } = useNotificationApi();
20314
- const { mutes } = useChatContext();
20315
- const { t } = useTranslationContext();
20316
- const isMuted = isUserMuted(message, mutes);
20575
+ children: t("duration/Remind Me", { milliseconds: offsetMs })
20576
+ },
20577
+ `reminder-offset-option--${offsetMs}`
20578
+ ))
20579
+ }
20580
+ );
20581
+ };
20582
+ const QuickMessageActionsButton = ({ className, ...props }) => /* @__PURE__ */ jsx(
20583
+ Button,
20584
+ {
20585
+ appearance: "ghost",
20586
+ circular: true,
20587
+ className: clsx("str-chat__message-actions-box-button", className),
20588
+ size: "sm",
20589
+ variant: "secondary",
20590
+ ...props
20591
+ }
20592
+ );
20593
+ const DeleteMessageAlert = ({ onCancel, onDelete }) => {
20594
+ const { t } = useTranslationContext();
20595
+ const { close } = useModalContext();
20596
+ return /* @__PURE__ */ jsxs(
20597
+ Alert.Root,
20598
+ {
20599
+ className: "str-chat__delete-message-alert",
20600
+ "data-testid": "message-delete-alert",
20601
+ children: [
20602
+ /* @__PURE__ */ jsx(
20603
+ Alert.Header,
20604
+ {
20605
+ description: t("Are you sure you want to delete this message?"),
20606
+ title: t("Delete message")
20607
+ }
20608
+ ),
20609
+ /* @__PURE__ */ jsxs(Alert.Actions, { children: [
20610
+ /* @__PURE__ */ jsx(
20611
+ Button,
20612
+ {
20613
+ appearance: "outline",
20614
+ className: "str-chat__delete-message-alert__delete-button",
20615
+ "data-testid": "delete-message-alert-delete-button",
20616
+ onClick: onDelete,
20617
+ size: "md",
20618
+ variant: "danger",
20619
+ children: t("Delete message")
20620
+ }
20621
+ ),
20622
+ /* @__PURE__ */ jsx(
20623
+ Button,
20624
+ {
20625
+ appearance: "outline",
20626
+ className: "str-chat__delete-message-alert__cancel-button",
20627
+ "data-testid": "delete-message-alert-cancel-button",
20628
+ onClick: () => {
20629
+ onCancel();
20630
+ close();
20631
+ },
20632
+ size: "md",
20633
+ variant: "secondary",
20634
+ children: t("Cancel")
20635
+ }
20636
+ )
20637
+ ] })
20638
+ ]
20639
+ }
20640
+ );
20641
+ };
20642
+ const msgActionsBoxButtonClassName = "str-chat__message-actions-list-item-button";
20643
+ const getErrorMessage = (error, fallback) => error instanceof Error && error.message ? error.message : fallback;
20644
+ const getNotificationError = (error) => {
20645
+ if (error instanceof Error) return error;
20646
+ if (typeof error === "string") return new Error(error);
20647
+ if (error && typeof error === "object" && "message" in error) {
20648
+ const message = error.message;
20649
+ if (typeof message === "string") return new Error(message);
20650
+ }
20651
+ return void 0;
20652
+ };
20653
+ const DefaultMessageActionComponents = {
20654
+ dropdown: {
20655
+ React() {
20656
+ const { ReactionSelector: ReactionSelector$1 = ReactionSelector } = useComponentContext();
20657
+ const { anchorReferenceElement } = useContextMenuContext();
20658
+ const { isMyMessage, message, threadList } = useMessageContext();
20659
+ const { t } = useTranslationContext();
20660
+ const [referenceElement, setReferenceElement] = useState(null);
20661
+ const dialogId2 = `${ReactionSelector.getDialogId({
20662
+ messageId: message.id,
20663
+ threadList
20664
+ })}-dropdown`;
20665
+ const { dialog, dialogManager } = useDialogOnNearestManager({
20666
+ id: dialogId2
20667
+ });
20668
+ const dialogIsOpen = useDialogIsOpen(dialogId2, dialogManager?.id);
20669
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
20670
+ /* @__PURE__ */ jsx(
20671
+ DialogAnchor,
20672
+ {
20673
+ dialogManagerId: dialogManager?.id,
20674
+ id: dialogId2,
20675
+ offset: 8,
20676
+ placement: isMyMessage() ? "top-end" : "top-start",
20677
+ referenceElement,
20678
+ trapFocus: true,
20679
+ updatePositionOnContentResize: true,
20680
+ children: /* @__PURE__ */ jsx(ReactionSelector$1, { dialogId: dialogId2 })
20681
+ }
20682
+ ),
20683
+ /* @__PURE__ */ jsx(
20684
+ ContextMenuButton,
20685
+ {
20686
+ "aria-expanded": dialogIsOpen,
20687
+ "aria-label": t("aria/Open Reaction Selector"),
20688
+ className: clsx(
20689
+ msgActionsBoxButtonClassName,
20690
+ "str-chat__message-actions-list-item-button--react"
20691
+ ),
20692
+ "data-testid": "dropdown-react-action",
20693
+ Icon: IconEmoji,
20694
+ onClick: (event) => {
20695
+ if (dialogIsOpen) {
20696
+ dialog.close();
20697
+ return;
20698
+ }
20699
+ setReferenceElement(
20700
+ anchorReferenceElement instanceof HTMLElement ? anchorReferenceElement : event.currentTarget
20701
+ );
20702
+ dialog.open();
20703
+ },
20704
+ children: t("Add reaction")
20705
+ }
20706
+ )
20707
+ ] });
20708
+ },
20709
+ ThreadReply() {
20710
+ const { closeMenu } = useContextMenuContext();
20711
+ const { handleOpenThread } = useMessageContext();
20712
+ const { t } = useTranslationContext();
20317
20713
  return /* @__PURE__ */ jsx(
20318
20714
  ContextMenuButton,
20319
20715
  {
20320
- "aria-label": isMuted ? t("aria/Unmute User") : t("aria/Mute User"),
20716
+ "aria-label": t("aria/Open Thread"),
20321
20717
  className: msgActionsBoxButtonClassName,
20322
- Icon: isMuted ? IconAudio : IconMute,
20718
+ "data-testid": "thread-action",
20719
+ Icon: IconThread,
20720
+ onClick: (e) => {
20721
+ handleOpenThread(e);
20722
+ closeMenu();
20723
+ },
20724
+ children: t("Thread Reply")
20725
+ }
20726
+ );
20727
+ },
20728
+ Quote() {
20729
+ const { closeMenu } = useContextMenuContext();
20730
+ const { message } = useMessageContext();
20731
+ const { t } = useTranslationContext();
20732
+ const messageComposer = useMessageComposerController();
20733
+ const handleQuote = () => {
20734
+ messageComposer.setQuotedMessage(message);
20735
+ const elements = message.parent_id ? document.querySelectorAll(".str-chat__thread .str-chat__textarea__textarea") : document.getElementsByClassName("str-chat__textarea__textarea");
20736
+ const textarea = elements.item(0);
20737
+ if (textarea instanceof HTMLTextAreaElement) {
20738
+ textarea.focus();
20739
+ }
20740
+ };
20741
+ return /* @__PURE__ */ jsx(
20742
+ ContextMenuButton,
20743
+ {
20744
+ "aria-label": t("aria/Quote Message"),
20745
+ className: msgActionsBoxButtonClassName,
20746
+ Icon: IconQuote,
20747
+ onClick: () => {
20748
+ handleQuote();
20749
+ closeMenu();
20750
+ },
20751
+ children: t("Quote Reply")
20752
+ }
20753
+ );
20754
+ },
20755
+ Pin() {
20756
+ const { closeMenu } = useContextMenuContext();
20757
+ const { handlePin, message } = useMessageContext();
20758
+ const { addNotification } = useNotificationApi();
20759
+ const { t } = useTranslationContext();
20760
+ const isPinned = !!message.pinned;
20761
+ return /* @__PURE__ */ jsx(
20762
+ ContextMenuButton,
20763
+ {
20764
+ "aria-label": isPinned ? t("aria/Unpin Message") : t("aria/Pin Message"),
20765
+ className: msgActionsBoxButtonClassName,
20766
+ Icon: isPinned ? IconUnpin : IconPin,
20323
20767
  onClick: async (event) => {
20324
20768
  try {
20325
- await handleMute(event);
20769
+ await handlePin(event);
20326
20770
  addNotification({
20327
20771
  context: {
20328
20772
  message
20329
20773
  },
20330
20774
  emitter: "MessageActions",
20331
- message: isMuted ? t("{{ user }} has been unmuted", {
20332
- user: message.user?.name || message.user?.id
20333
- }) : t("{{ user }} has been muted", {
20334
- user: message.user?.name || message.user?.id
20335
- }),
20775
+ message: isPinned ? t("Message unpinned") : t("Message pinned"),
20336
20776
  severity: "success",
20337
- type: isMuted ? "api:user:unmute:success" : "api:user:mute:success"
20777
+ type: isPinned ? "api:message:unpin:success" : "api:message:pin:success"
20338
20778
  });
20339
20779
  } catch (error) {
20340
20780
  addNotification({
@@ -20345,818 +20785,553 @@ const DefaultMessageActionComponents = {
20345
20785
  error: getNotificationError(error),
20346
20786
  message: getErrorMessage(
20347
20787
  error,
20348
- isMuted ? t("Error unmuting a user ...") : t("Error muting a user ...")
20788
+ isPinned ? t("Error removing message pin") : t("Error pinning message")
20349
20789
  ),
20350
20790
  severity: "error",
20351
- type: isMuted ? "api:user:unmute:failed" : "api:user:mute:failed"
20791
+ type: isPinned ? "api:message:unpin:failed" : "api:message:pin:failed"
20352
20792
  });
20353
20793
  }
20354
20794
  closeMenu();
20355
20795
  },
20356
- children: isMuted ? t("Unmute") : t("Mute")
20796
+ children: isPinned ? t("Unpin") : t("Pin")
20357
20797
  }
20358
20798
  );
20359
20799
  },
20360
- Delete() {
20361
- const { closeMenu } = useContextMenuContext();
20362
- const { addNotification } = useNotificationApi();
20363
- const { Modal = GlobalModal } = useComponentContext();
20364
- const { handleDelete, message } = useMessageContext();
20365
- const { t } = useTranslationContext();
20366
- const [openModal, setOpenModal] = useState(false);
20367
- return /* @__PURE__ */ jsxs(Fragment, { children: [
20368
- /* @__PURE__ */ jsx(
20369
- ContextMenuButton,
20370
- {
20371
- "aria-label": t("aria/Delete Message"),
20372
- className: msgActionsBoxButtonClassName,
20373
- Icon: IconDelete,
20374
- onClick: () => {
20375
- setOpenModal(true);
20376
- },
20377
- variant: "destructive",
20378
- children: t("Delete message")
20379
- }
20380
- ),
20381
- /* @__PURE__ */ jsx(Modal, { open: openModal, children: /* @__PURE__ */ jsx(
20382
- DeleteMessageAlert,
20383
- {
20384
- onCancel: () => {
20385
- setOpenModal(false);
20386
- closeMenu();
20387
- },
20388
- onDelete: async () => {
20389
- try {
20390
- await handleDelete();
20391
- addNotification({
20392
- context: {
20393
- message
20394
- },
20395
- emitter: "MessageActions",
20396
- message: t("Message deleted"),
20397
- severity: "success",
20398
- type: "api:message:delete:success"
20399
- });
20400
- } catch (error) {
20401
- addNotification({
20402
- context: {
20403
- message
20404
- },
20405
- emitter: "MessageActions",
20406
- error: getNotificationError(error),
20407
- message: getErrorMessage(error, t("Error deleting message")),
20408
- severity: "error",
20409
- type: "api:message:delete:failed"
20410
- });
20411
- } finally {
20412
- setOpenModal(false);
20413
- closeMenu();
20414
- }
20415
- }
20416
- }
20417
- ) })
20418
- ] });
20419
- },
20420
- BlockUser() {
20800
+ CopyMessageText() {
20421
20801
  const { closeMenu } = useContextMenuContext();
20422
- const { client } = useChatContext();
20423
20802
  const { message } = useMessageContext();
20424
20803
  const { t } = useTranslationContext();
20425
- const isBlocked = !message.user?.id || new Set(client.blockedUsers.getLatestValue().userIds).has(message.user?.id);
20426
20804
  return /* @__PURE__ */ jsx(
20427
20805
  ContextMenuButton,
20428
20806
  {
20429
- "aria-label": isBlocked ? t("aria/Unblock User") : t("aria/Block User"),
20430
- className: clsx(msgActionsBoxButtonClassName),
20431
- Icon: isBlocked ? IconUserCheck : IconNoSign,
20807
+ "aria-label": t("aria/Copy Message Text"),
20808
+ className: msgActionsBoxButtonClassName,
20809
+ Icon: IconCopy,
20432
20810
  onClick: () => {
20433
- const targetId = message.user?.id;
20434
- if (targetId) {
20435
- if (isBlocked) client.unBlockUser(targetId);
20436
- else client.blockUser(targetId);
20437
- }
20811
+ if (message.text) navigator.clipboard.writeText(message.text);
20438
20812
  closeMenu();
20439
20813
  },
20440
- children: isBlocked ? t("Unblock User") : t("Block User")
20814
+ children: t("Copy Message")
20441
20815
  }
20442
20816
  );
20443
- }
20444
- },
20445
- quick: {
20446
- // eslint-disable-next-line react/display-name
20447
- DropdownToggle: forwardRef((_, ref) => {
20817
+ },
20818
+ Resend() {
20819
+ const { closeMenu } = useContextMenuContext();
20820
+ const { handleRetry, message } = useMessageContext();
20448
20821
  const { t } = useTranslationContext();
20449
- const { message } = useMessageContext();
20450
- const dropdownDialogIsOpen = useDialogIsOpen(
20451
- MessageActions.getDialogId({ messageId: message.id })
20452
- );
20453
- const { dialog } = useDialogOnNearestManager({
20454
- id: MessageActions.getDialogId({ messageId: message.id })
20455
- });
20456
20822
  return /* @__PURE__ */ jsx(
20457
- QuickMessageActionsButton,
20823
+ ContextMenuButton,
20458
20824
  {
20459
- "aria-expanded": dropdownDialogIsOpen,
20460
- "aria-haspopup": "true",
20461
- "aria-label": t("aria/Open Message Actions Menu"),
20462
- className: "str-chat__message-actions-box-button",
20463
- "data-testid": "message-actions-toggle-button",
20825
+ "aria-label": t("aria/Resend Message"),
20826
+ className: msgActionsBoxButtonClassName,
20827
+ Icon: IconRetry,
20464
20828
  onClick: () => {
20465
- dialog?.toggle();
20829
+ handleRetry(message);
20830
+ closeMenu();
20466
20831
  },
20467
- ref,
20468
- children: /* @__PURE__ */ jsx(IconMore, { className: "str-chat__message-action-icon" })
20832
+ children: t("Resend")
20469
20833
  }
20470
20834
  );
20471
- }),
20472
- React() {
20473
- return /* @__PURE__ */ jsx(ReactionSelectorWithButton, { ReactionIcon: IconEmoji });
20474
20835
  },
20475
- Reply() {
20476
- const { handleOpenThread } = useMessageContext();
20836
+ Edit() {
20837
+ const messageComposer = useMessageComposerController();
20838
+ const { message } = useMessageContext();
20477
20839
  const { t } = useTranslationContext();
20840
+ const { closeMenu } = useContextMenuContext();
20478
20841
  return /* @__PURE__ */ jsx(
20479
- QuickMessageActionsButton,
20842
+ ContextMenuButton,
20480
20843
  {
20481
- "aria-label": t("aria/Open Thread"),
20482
- className: "str-chat__message-reply-in-thread-button",
20483
- "data-testid": "thread-action",
20484
- onClick: handleOpenThread,
20485
- children: /* @__PURE__ */ jsx(IconReply, { className: "str-chat__message-action-icon" })
20844
+ "aria-label": t("aria/Edit Message"),
20845
+ className: msgActionsBoxButtonClassName,
20846
+ Icon: IconEdit,
20847
+ onClick: () => {
20848
+ savePreEditSnapshot(messageComposer);
20849
+ messageComposer.initState({ composition: message });
20850
+ closeMenu();
20851
+ },
20852
+ children: t("Edit Message")
20486
20853
  }
20487
20854
  );
20488
- }
20489
- }
20490
- };
20491
- const defaultMessageActionSet = [
20492
- {
20493
- Component: DefaultMessageActionComponents.quick.DropdownToggle,
20494
- placement: "quick-dropdown-toggle"
20495
- },
20496
- {
20497
- Component: DefaultMessageActionComponents.quick.Reply,
20498
- placement: "quick",
20499
- type: "reply"
20500
- },
20501
- {
20502
- Component: DefaultMessageActionComponents.quick.React,
20503
- placement: "quick",
20504
- type: "react"
20505
- },
20506
- {
20507
- Component: DefaultMessageActionComponents.dropdown.ThreadReply,
20508
- placement: "dropdown",
20509
- type: "reply"
20510
- },
20511
- {
20512
- Component: DefaultMessageActionComponents.dropdown.Quote,
20513
- placement: "dropdown",
20514
- type: "quote"
20515
- },
20516
- {
20517
- Component: DefaultMessageActionComponents.dropdown.Pin,
20518
- placement: "dropdown",
20519
- type: "pin"
20520
- },
20521
- {
20522
- Component: DefaultMessageActionComponents.dropdown.CopyMessageText,
20523
- placement: "dropdown",
20524
- type: "copyMessageText"
20525
- },
20526
- {
20527
- Component: DefaultMessageActionComponents.dropdown.Resend,
20528
- placement: "dropdown",
20529
- type: "resendMessage"
20530
- },
20531
- {
20532
- Component: DefaultMessageActionComponents.dropdown.Edit,
20533
- placement: "dropdown",
20534
- type: "edit"
20535
- },
20536
- {
20537
- Component: DefaultMessageActionComponents.dropdown.MarkUnread,
20538
- placement: "dropdown",
20539
- type: "markUnread"
20540
- },
20541
- {
20542
- Component: DefaultMessageActionComponents.dropdown.RemindMe,
20543
- placement: "dropdown",
20544
- type: "remindMe"
20545
- },
20546
- {
20547
- Component: DefaultMessageActionComponents.dropdown.SaveForLater,
20548
- placement: "dropdown",
20549
- type: "saveForLater"
20550
- },
20551
- {
20552
- Component: DefaultMessageActionComponents.dropdown.Flag,
20553
- placement: "dropdown",
20554
- type: "flag"
20555
- },
20556
- {
20557
- Component: DefaultMessageActionComponents.dropdown.Mute,
20558
- placement: "dropdown",
20559
- type: "mute"
20560
- },
20561
- {
20562
- Component: DefaultMessageActionComponents.dropdown.Delete,
20563
- placement: "dropdown",
20564
- type: "delete"
20565
- },
20566
- {
20567
- Component: DefaultMessageActionComponents.dropdown.BlockUser,
20568
- placement: "dropdown",
20569
- type: "blockUser"
20570
- }
20571
- ];
20572
- function useFetchReactions(options) {
20573
- const { addNotification } = useNotificationApi();
20574
- const { handleFetchReactions: contextHandleFetchReactions } = useMessageContext();
20575
- const { t } = useTranslationContext("useFetchReactions");
20576
- const [reactions, setReactions] = useState([]);
20577
- const {
20578
- handleFetchReactions: propHandleFetchReactions,
20579
- reactionType,
20580
- shouldFetch,
20581
- sort
20582
- } = options;
20583
- const [isLoading, setIsLoading] = useState(shouldFetch);
20584
- const handleFetchReactions = propHandleFetchReactions ?? contextHandleFetchReactions;
20585
- const [refetchNonce, setRefetchNonce] = useState(null);
20586
- useEffect(() => {
20587
- if (!shouldFetch) {
20588
- return;
20589
- }
20590
- let cancel = false;
20591
- (async () => {
20592
- try {
20593
- setIsLoading(true);
20594
- const reactions2 = await handleFetchReactions(reactionType ?? void 0, sort);
20595
- if (!cancel) {
20596
- setReactions(reactions2);
20597
- }
20598
- } catch (e) {
20599
- if (!cancel) {
20600
- setReactions([]);
20601
- addNotification({
20602
- emitter: "Reactions",
20603
- error: e instanceof Error ? e : void 0,
20604
- message: t("Error fetching reactions"),
20605
- severity: "error",
20606
- type: "api:message:reactions:fetch:failed"
20607
- });
20608
- }
20609
- } finally {
20610
- if (!cancel) {
20611
- setIsLoading(false);
20855
+ },
20856
+ MarkUnread() {
20857
+ const { closeMenu } = useContextMenuContext();
20858
+ const { handleMarkUnread, message } = useMessageContext();
20859
+ const { addNotification } = useNotificationApi();
20860
+ const { t } = useTranslationContext();
20861
+ return /* @__PURE__ */ jsx(
20862
+ ContextMenuButton,
20863
+ {
20864
+ "aria-label": t("aria/Mark Message Unread"),
20865
+ className: msgActionsBoxButtonClassName,
20866
+ Icon: IconNotification,
20867
+ onClick: async (event) => {
20868
+ try {
20869
+ await handleMarkUnread(event);
20870
+ addNotification({
20871
+ context: {
20872
+ message
20873
+ },
20874
+ emitter: "MessageActions",
20875
+ message: t("Message marked as unread"),
20876
+ severity: "success",
20877
+ type: "api:message:markUnread:success"
20878
+ });
20879
+ } catch (error) {
20880
+ addNotification({
20881
+ context: {
20882
+ message
20883
+ },
20884
+ emitter: "MessageActions",
20885
+ error: getNotificationError(error),
20886
+ message: getErrorMessage(
20887
+ error,
20888
+ t(
20889
+ "Error marking message unread. Cannot mark unread messages older than the newest 100 channel messages."
20890
+ )
20891
+ ),
20892
+ severity: "error",
20893
+ type: "api:message:markUnread:failed"
20894
+ });
20895
+ }
20896
+ closeMenu();
20897
+ },
20898
+ children: t("Mark as unread")
20612
20899
  }
20613
- }
20614
- })();
20615
- return () => {
20616
- cancel = true;
20617
- };
20618
- }, [
20619
- addNotification,
20620
- handleFetchReactions,
20621
- reactionType,
20622
- refetchNonce,
20623
- shouldFetch,
20624
- sort,
20625
- t
20626
- ]);
20627
- const refetch = useCallback(() => {
20628
- setRefetchNonce(Math.random());
20629
- }, []);
20630
- return { isLoading, reactions, refetch };
20631
- }
20632
- const defaultReactionDetailsSort = { created_at: -1 };
20633
- const MessageReactionsDetailLoadingIndicator = () => {
20634
- const elements = useMemo(
20635
- () => Array.from({ length: 3 }, (_, index) => /* @__PURE__ */ jsxs("div", { className: "str-chat__message-reactions-detail__skeleton-item", children: [
20636
- /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__skeleton-avatar" }),
20637
- /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__skeleton-line" })
20638
- ] }, index)),
20639
- []
20640
- );
20641
- return /* @__PURE__ */ jsx(Fragment, { children: elements });
20642
- };
20643
- const MessageReactionsDetail = ({
20644
- handleFetchReactions,
20645
- handleReaction,
20646
- onSelectedReactionTypeChange,
20647
- own_reactions,
20648
- reactionDetailsSort: propReactionDetailsSort,
20649
- reactionGroups,
20650
- reactions,
20651
- selectedReactionType,
20652
- totalReactionCount
20653
- }) => {
20654
- const [extendedReactionListOpen, setExtendedReactionListOpen] = useState(false);
20655
- const { client } = useChatContext();
20656
- const {
20657
- Avatar: Avatar$1 = Avatar,
20658
- LoadingIndicator: LoadingIndicator2 = MessageReactionsDetailLoadingIndicator,
20659
- reactionOptions = defaultReactionOptions,
20660
- ReactionSelectorExtendedList: ReactionSelectorExtendedList2 = ReactionSelector.ExtendedList
20661
- } = useComponentContext(MessageReactionsDetail.name);
20662
- const { t } = useTranslationContext();
20663
- const {
20664
- handleReaction: contextHandleReaction,
20665
- message,
20666
- reactionDetailsSort: contextReactionDetailsSort
20667
- } = useMessageContext(MessageReactionsDetail.name);
20668
- const reactionDetailsSort = propReactionDetailsSort ?? contextReactionDetailsSort ?? defaultReactionDetailsSort;
20669
- const {
20670
- isLoading: areReactionsLoading,
20671
- reactions: reactionDetails,
20672
- refetch
20673
- } = useFetchReactions({
20674
- handleFetchReactions,
20675
- reactionType: selectedReactionType,
20676
- shouldFetch: true,
20677
- sort: reactionDetailsSort
20678
- });
20679
- if (extendedReactionListOpen) {
20680
- return /* @__PURE__ */ jsx(
20681
- "div",
20682
- {
20683
- className: "str-chat__message-reactions-detail",
20684
- "data-testid": "message-reactions-detail",
20685
- children: /* @__PURE__ */ jsx(
20686
- ReactionSelectorExtendedList2,
20687
- {
20688
- dialogId: MessageReactionsDetail.getDialogId({ messageId: message.id }),
20689
- handleReaction,
20690
- own_reactions
20691
- }
20692
- )
20693
- }
20694
- );
20695
- }
20696
- return /* @__PURE__ */ jsxs(
20697
- "div",
20698
- {
20699
- className: "str-chat__message-reactions-detail",
20700
- "data-testid": "message-reactions-detail",
20701
- children: [
20702
- typeof totalReactionCount === "number" && /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__total-count", children: t("{{ count }} reactions", { count: totalReactionCount }) }),
20703
- /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__reaction-type-list-container", children: /* @__PURE__ */ jsxs(
20704
- "ul",
20705
- {
20706
- className: "str-chat__message-reactions-detail__reaction-type-list",
20707
- "data-testid": "reaction-type-list",
20708
- children: [
20709
- /* @__PURE__ */ jsx("li", { className: "str-chat__message-reactions-detail__reaction-type-list-item", children: /* @__PURE__ */ jsx(
20710
- "button",
20711
- {
20712
- "aria-label": t("Add reaction"),
20713
- className: "str-chat__message-reactions-detail__reaction-type-list-item-button",
20714
- "data-testid": "add-reaction-button",
20715
- onClick: () => setExtendedReactionListOpen(true),
20716
- children: /* @__PURE__ */ jsx("span", { className: "str-chat__message-reactions-detail__reaction-type-list-item-icon", children: /* @__PURE__ */ jsx(IconEmojiAdd, {}) })
20717
- }
20718
- ) }),
20719
- reactions.map(
20720
- ({ EmojiComponent, reactionCount, reactionType }) => EmojiComponent && /* @__PURE__ */ jsx(
20721
- "li",
20722
- {
20723
- className: "str-chat__message-reactions-detail__reaction-type-list-item",
20724
- children: /* @__PURE__ */ jsxs(
20725
- "button",
20726
- {
20727
- "aria-pressed": reactionType === selectedReactionType,
20728
- className: "str-chat__message-reactions-detail__reaction-type-list-item-button",
20729
- onClick: () => onSelectedReactionTypeChange?.(
20730
- selectedReactionType === reactionType ? null : reactionType
20731
- ),
20732
- children: [
20733
- /* @__PURE__ */ jsx("span", { className: "str-chat__message-reactions-detail__reaction-type-list-item-icon", children: /* @__PURE__ */ jsx(EmojiComponent, {}) }),
20734
- /* @__PURE__ */ jsx(
20735
- "span",
20736
- {
20737
- className: "str-chat__message-reactions-detail__reaction-type-list-item-count",
20738
- "data-testid": "reaction-type-count",
20739
- children: reactionCount
20740
- }
20741
- )
20742
- ]
20743
- }
20744
- )
20900
+ );
20901
+ },
20902
+ RemindMe() {
20903
+ const { closeMenu, openSubmenu } = useContextMenuContext();
20904
+ const { client } = useChatContext();
20905
+ const { addNotification } = useNotificationApi();
20906
+ const { t } = useTranslationContext();
20907
+ const { message } = useMessageContext();
20908
+ const reminder = useMessageReminder(message.id);
20909
+ const messageAlreadyBookmarked = reminder && !reminder?.remindAt;
20910
+ if (messageAlreadyBookmarked) return null;
20911
+ return /* @__PURE__ */ jsx(
20912
+ ContextMenuButton,
20913
+ {
20914
+ "aria-label": reminder ? t("aria/Remind Me Message") : t("aria/Remove Reminder"),
20915
+ className: msgActionsBoxButtonClassName,
20916
+ hasSubMenu: !reminder,
20917
+ Icon: reminder ? IconBellOff : IconBell,
20918
+ onClick: async () => {
20919
+ if (reminder) {
20920
+ try {
20921
+ await client.reminders.deleteReminder(reminder.id);
20922
+ addNotification({
20923
+ context: {
20924
+ message
20745
20925
  },
20746
- reactionType
20747
- )
20748
- )
20749
- ]
20750
- }
20751
- ) }),
20752
- /* @__PURE__ */ jsx("div", { className: "str-chat__message-reactions-detail__user-list-container", children: /* @__PURE__ */ jsxs(
20753
- "div",
20754
- {
20755
- className: "str-chat__message-reactions-detail__user-list",
20756
- "data-testid": "all-reacting-users",
20757
- children: [
20758
- areReactionsLoading && /* @__PURE__ */ jsx(LoadingIndicator2, {}),
20759
- !areReactionsLoading && /* @__PURE__ */ jsx(Fragment, { children: reactionDetails.map(({ type, user }) => {
20760
- const belongsToCurrentUser = client.user?.id === user?.id;
20761
- const EmojiComponent = Array.isArray(reactionOptions) ? void 0 : reactionOptions.quick[type]?.Component ?? reactionOptions.extended?.[type]?.Component;
20762
- return /* @__PURE__ */ jsxs(
20763
- "div",
20764
- {
20765
- className: "str-chat__message-reactions-detail__user-list-item",
20766
- children: [
20767
- /* @__PURE__ */ jsx(
20768
- Avatar$1,
20769
- {
20770
- className: "str-chat__avatar--with-border",
20771
- "data-testid": "avatar",
20772
- imageUrl: user?.image,
20773
- size: "md",
20774
- userName: user?.name || user?.id
20775
- }
20776
- ),
20777
- /* @__PURE__ */ jsxs("div", { className: "str-chat__message-reactions-detail__user-list-item-info", children: [
20778
- /* @__PURE__ */ jsx(
20779
- "span",
20780
- {
20781
- className: "str-chat__message-reactions-detail__user-list-item-username",
20782
- "data-testid": "reaction-user-username",
20783
- children: belongsToCurrentUser ? t("You") : user?.name || user?.id
20784
- }
20785
- ),
20786
- belongsToCurrentUser && /* @__PURE__ */ jsx(
20787
- "button",
20788
- {
20789
- className: "str-chat__message-reactions-detail__user-list-item-button",
20790
- "data-testid": "remove-reaction-button",
20791
- onClick: async (e) => {
20792
- const reactionCountBeforeRemoval = reactionGroups?.[type]?.count ?? 0;
20793
- await contextHandleReaction(type, e);
20794
- if (selectedReactionType !== null && reactionCountBeforeRemoval <= 1) {
20795
- onSelectedReactionTypeChange?.(null);
20796
- } else {
20797
- refetch();
20798
- }
20799
- },
20800
- children: t("Tap to remove")
20801
- }
20802
- )
20803
- ] }),
20804
- /* @__PURE__ */ jsx("span", { className: "str-chat__message-reactions-detail__user-list-item-icon", children: !selectedReactionType && EmojiComponent && /* @__PURE__ */ jsx(EmojiComponent, {}) })
20805
- ]
20926
+ emitter: "MessageActions",
20927
+ message: t("Remove reminder"),
20928
+ severity: "success",
20929
+ type: "api:message:reminder:delete:success"
20930
+ });
20931
+ } catch (error) {
20932
+ addNotification({
20933
+ context: {
20934
+ message
20806
20935
  },
20807
- `${user?.id}-${type}`
20808
- );
20809
- }) })
20810
- ]
20811
- }
20812
- ) })
20813
- ]
20814
- }
20815
- );
20816
- };
20817
- MessageReactionsDetail.displayName = "MessageReactionsDetail";
20818
- MessageReactionsDetail.getDialogId = ({ messageId }) => `message-reactions-detail-${messageId}`;
20819
- const defaultReactionsSort = (a, b) => {
20820
- if (a.firstReactionAt && b.firstReactionAt) {
20821
- return +a.firstReactionAt - +b.firstReactionAt;
20822
- }
20823
- return a.reactionType.localeCompare(b.reactionType, "en");
20824
- };
20825
- const useProcessReactions = (params) => {
20826
- const {
20827
- own_reactions: propOwnReactions,
20828
- reaction_groups: propReactionGroups,
20829
- reactions: propReactions,
20830
- sortReactions: propSortReactions
20831
- } = params;
20832
- const { message, sortReactions: contextSortReactions } = useMessageContext();
20833
- const { reactionOptions = defaultReactionOptions } = useComponentContext();
20834
- const sortReactions = propSortReactions ?? contextSortReactions ?? defaultReactionsSort;
20835
- const latestReactions = propReactions ?? message.latest_reactions;
20836
- const ownReactions = propOwnReactions ?? message?.own_reactions;
20837
- const reactionGroups = propReactionGroups ?? message?.reaction_groups ?? void 0;
20838
- const isOwnReaction = useCallback(
20839
- (reactionType) => ownReactions?.some((reaction) => reaction.type === reactionType) ?? false,
20840
- [ownReactions]
20841
- );
20842
- const getEmojiByReactionType = useCallback(
20843
- (reactionType) => {
20844
- if (Array.isArray(reactionOptions)) {
20845
- return reactionOptions.find(({ type }) => type === reactionType)?.Component ?? null;
20846
- }
20847
- return reactionOptions.quick[reactionType]?.Component ?? reactionOptions.extended?.[reactionType]?.Component ?? null;
20848
- },
20849
- [reactionOptions]
20850
- );
20851
- const isSupportedReaction = useCallback(
20852
- (reactionType) => {
20853
- if (Array.isArray(reactionOptions)) {
20854
- return reactionOptions.some(
20855
- (reactionOption) => reactionOption.type === reactionType
20856
- );
20857
- }
20858
- return typeof reactionOptions.quick[reactionType] !== "undefined" || typeof reactionOptions.extended?.[reactionType] !== "undefined";
20859
- },
20860
- [reactionOptions]
20861
- );
20862
- const uniqueReactionTypeCount = useMemo(() => {
20863
- if (!reactionGroups) {
20864
- return 0;
20865
- }
20866
- return Object.keys(reactionGroups).filter(
20867
- (reactionType) => isSupportedReaction(reactionType)
20868
- ).length;
20869
- }, [isSupportedReaction, reactionGroups]);
20870
- const getLatestReactedUserNames = useCallback(
20871
- (reactionType) => latestReactions?.flatMap((reaction) => {
20872
- if (reactionType && reactionType === reaction.type) {
20873
- const username = reaction.user?.name || reaction.user?.id;
20874
- return username ? [username] : [];
20875
- }
20876
- return [];
20877
- }) ?? [],
20878
- [latestReactions]
20879
- );
20880
- const existingReactions = useMemo(() => {
20881
- if (!reactionGroups) {
20882
- return [];
20883
- }
20884
- const unsortedReactions = Object.entries(reactionGroups).flatMap(
20885
- ([reactionType, { count, first_reaction_at, last_reaction_at }]) => {
20886
- if (count === 0 || !isSupportedReaction(reactionType)) {
20887
- return [];
20936
+ emitter: "MessageActions",
20937
+ error: getNotificationError(error),
20938
+ message: getErrorMessage(error, "Error removing reminder"),
20939
+ severity: "error",
20940
+ type: "api:message:reminder:delete:failed"
20941
+ });
20942
+ } finally {
20943
+ closeMenu();
20944
+ }
20945
+ } else {
20946
+ openSubmenu({
20947
+ Header: RemindMeSubmenuHeader,
20948
+ Submenu: RemindMeSubmenu
20949
+ });
20950
+ }
20951
+ },
20952
+ children: reminder ? t("Remove reminder") : t("Remind me")
20888
20953
  }
20889
- const latestReactedUserNames = getLatestReactedUserNames(reactionType);
20890
- return [
20891
- {
20892
- EmojiComponent: getEmojiByReactionType(reactionType),
20893
- firstReactionAt: first_reaction_at ? new Date(first_reaction_at) : null,
20894
- isOwnReaction: isOwnReaction(reactionType),
20895
- lastReactionAt: last_reaction_at ? new Date(last_reaction_at) : null,
20896
- latestReactedUserNames,
20897
- reactionCount: count,
20898
- reactionType,
20899
- unlistedReactedUserCount: count - latestReactedUserNames.length
20900
- }
20901
- ];
20902
- }
20903
- );
20904
- return unsortedReactions.sort(sortReactions);
20905
- }, [
20906
- getEmojiByReactionType,
20907
- getLatestReactedUserNames,
20908
- isOwnReaction,
20909
- isSupportedReaction,
20910
- reactionGroups,
20911
- sortReactions
20912
- ]);
20913
- const hasReactions = existingReactions.length > 0;
20914
- const totalReactionCount = useMemo(
20915
- () => Object.values(reactionGroups ?? {}).reduce((total, { count }) => total + count, 0),
20916
- [reactionGroups]
20917
- );
20918
- return {
20919
- existingReactions,
20920
- hasReactions,
20921
- reactionGroups,
20922
- totalReactionCount,
20923
- uniqueReactionTypeCount
20924
- };
20925
- };
20926
- const FragmentOrButton = ({
20927
- buttonIf: renderButton = false,
20928
- children,
20929
- ...props
20930
- }) => {
20931
- if (renderButton) {
20932
- return /* @__PURE__ */ jsx("button", { ...props, children });
20933
- }
20934
- return /* @__PURE__ */ jsx(Fragment, { children });
20935
- };
20936
- const UnMemoizedMessageReactions = (props) => {
20937
- const {
20938
- capLimit: { clustered: capLimitClustered = 5, segmented: capLimitSegmented = 4 } = {},
20939
- flipHorizontalPosition = false,
20940
- handleFetchReactions,
20941
- reactionDetailsSort,
20942
- verticalPosition = "top",
20943
- visualStyle = "clustered",
20944
- ...rest
20945
- } = props;
20946
- const {
20947
- existingReactions,
20948
- hasReactions,
20949
- reactionGroups,
20950
- totalReactionCount,
20951
- uniqueReactionTypeCount
20952
- } = useProcessReactions(rest);
20953
- const [selectedReactionType, setSelectedReactionType] = useState(
20954
- null
20955
- );
20956
- const { t } = useTranslationContext("MessageReactions");
20957
- const { MessageReactionsDetail: MessageReactionsDetail$1 = MessageReactionsDetail } = useComponentContext();
20958
- const { isMyMessage, message } = useMessageContext();
20959
- const divRef = useRef(null);
20960
- const dialogId2 = MessageReactionsDetail.getDialogId({
20961
- messageId: message.id
20962
- });
20963
- const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId2 });
20964
- const isDialogOpen = useDialogIsOpen(dialogId2, dialogManager?.id);
20965
- const handleReactionButtonClick = (reactionType) => {
20966
- if (totalReactionCount > MAX_MESSAGE_REACTIONS_TO_FETCH) {
20967
- return;
20968
- }
20969
- setSelectedReactionType(reactionType);
20970
- dialog.open();
20971
- };
20972
- const cappedExistingReactions = useMemo(() => {
20973
- if (visualStyle === "segmented" && verticalPosition !== "top") return null;
20974
- const capLimit = visualStyle === "segmented" ? capLimitSegmented : capLimitClustered;
20975
- const sliced = existingReactions.slice(0, capLimit);
20976
- return {
20977
- /**
20978
- * Accumulated reaction count of capped reaction types, first four in case of
20979
- * segmented(top) and first five in case of clustered(top/bottom) variations.
20980
- */
20981
- reactionCountToDisplay: sliced.reduce(
20982
- (accumulatedCount, { reactionCount }) => accumulatedCount + reactionCount,
20983
- 0
20984
- ),
20985
- reactionsToDisplay: sliced
20986
- };
20987
- }, [
20988
- capLimitClustered,
20989
- capLimitSegmented,
20990
- existingReactions,
20991
- verticalPosition,
20992
- visualStyle
20993
- ]);
20994
- if (!hasReactions) return null;
20995
- return /* @__PURE__ */ jsxs(Fragment, { children: [
20996
- /* @__PURE__ */ jsx(
20997
- "div",
20998
- {
20999
- "aria-label": t("aria/Reaction list"),
21000
- className: clsx("str-chat__message-reactions", {
21001
- [`str-chat__message-reactions--flipped-horizontally`]: flipHorizontalPosition,
21002
- [`str-chat__message-reactions--${verticalPosition}`]: typeof verticalPosition === "string",
21003
- [`str-chat__message-reactions--${visualStyle}`]: typeof visualStyle === "string"
21004
- }),
21005
- ref: divRef,
21006
- role: "figure",
21007
- children: /* @__PURE__ */ jsxs(
21008
- FragmentOrButton,
21009
- {
21010
- "aria-expanded": isDialogOpen,
21011
- "aria-pressed": isDialogOpen,
21012
- buttonIf: visualStyle === "clustered",
21013
- className: "str-chat__message-reactions__list-button",
21014
- "data-testid": "message-reactions-list-button",
21015
- onClick: () => handleReactionButtonClick(null),
21016
- children: [
21017
- /* @__PURE__ */ jsxs("ul", { className: "str-chat__message-reactions__list", children: [
21018
- (cappedExistingReactions?.reactionsToDisplay ?? existingReactions).map(
21019
- ({ EmojiComponent, reactionCount, reactionType }) => EmojiComponent && /* @__PURE__ */ jsx(
21020
- "li",
21021
- {
21022
- className: "str-chat__message-reactions__list-item",
21023
- "data-testid": "message-reactions-list-item",
21024
- children: /* @__PURE__ */ jsxs(
21025
- FragmentOrButton,
21026
- {
21027
- buttonIf: visualStyle === "segmented",
21028
- className: "str-chat__message-reactions__list-item-button",
21029
- onClick: () => handleReactionButtonClick(reactionType),
21030
- children: [
21031
- /* @__PURE__ */ jsx(
21032
- "span",
21033
- {
21034
- className: "str-chat__message-reactions__list-item-icon",
21035
- "data-testid": "message-reactions-list-item-icon",
21036
- children: /* @__PURE__ */ jsx(EmojiComponent, {})
21037
- }
21038
- ),
21039
- visualStyle === "segmented" && reactionCount > 1 && /* @__PURE__ */ jsx(
21040
- "span",
21041
- {
21042
- className: "str-chat__message-reactions__list-item-count",
21043
- "data-testclass": "message-reactions-item-count",
21044
- children: reactionCount
21045
- }
21046
- )
21047
- ]
21048
- }
21049
- )
21050
- },
21051
- reactionType
21052
- )
20954
+ );
20955
+ },
20956
+ SaveForLater() {
20957
+ const { closeMenu } = useContextMenuContext();
20958
+ const { client } = useChatContext();
20959
+ const { addNotification } = useNotificationApi();
20960
+ const { message } = useMessageContext();
20961
+ const { t } = useTranslationContext();
20962
+ const reminder = useMessageReminder(message.id);
20963
+ const messageAlreadyHasReminderScheduled = Boolean(reminder && reminder?.remindAt);
20964
+ if (messageAlreadyHasReminderScheduled) return null;
20965
+ return /* @__PURE__ */ jsx(
20966
+ ContextMenuButton,
20967
+ {
20968
+ "aria-label": reminder ? t("aria/Remove Save For Later") : t("aria/Bookmark Message"),
20969
+ className: msgActionsBoxButtonClassName,
20970
+ Icon: reminder ? IconBookmarkRemove : IconBookmark,
20971
+ onClick: async () => {
20972
+ try {
20973
+ if (reminder) {
20974
+ await client.reminders.deleteReminder(reminder.id);
20975
+ addNotification({
20976
+ context: {
20977
+ message
20978
+ },
20979
+ emitter: "MessageActions",
20980
+ message: t("Remove save for later"),
20981
+ severity: "success",
20982
+ type: "api:message:saveForLater:delete:success"
20983
+ });
20984
+ } else {
20985
+ await client.reminders.createReminder({ messageId: message.id });
20986
+ addNotification({
20987
+ context: {
20988
+ message
20989
+ },
20990
+ emitter: "MessageActions",
20991
+ message: t("Saved for later"),
20992
+ severity: "success",
20993
+ type: "api:message:saveForLater:create:success"
20994
+ });
20995
+ }
20996
+ } catch (error) {
20997
+ addNotification({
20998
+ context: {
20999
+ message
21000
+ },
21001
+ emitter: "MessageActions",
21002
+ error: getNotificationError(error),
21003
+ message: getErrorMessage(
21004
+ error,
21005
+ reminder ? "Error removing message from saved for later" : "Error saving message for later"
21053
21006
  ),
21054
- uniqueReactionTypeCount > 4 && cappedExistingReactions && visualStyle === "segmented" && /* @__PURE__ */ jsx("li", { className: "str-chat__message-reactions__list-item str-chat__message-reactions__list-item--more", children: /* @__PURE__ */ jsx(
21055
- "button",
21056
- {
21057
- className: "str-chat__message-reactions__list-item-button",
21058
- onClick: () => handleReactionButtonClick(null),
21059
- children: /* @__PURE__ */ jsxs("span", { className: "str-chat__message-reactions__overflow-count", children: [
21060
- "+",
21061
- totalReactionCount - cappedExistingReactions.reactionCountToDisplay
21062
- ] })
21063
- }
21064
- ) })
21065
- ] }),
21066
- visualStyle === "clustered" && /* @__PURE__ */ jsx(
21067
- "span",
21068
- {
21069
- className: "str-chat__message-reactions__total-count",
21070
- "data-testid": "message-reactions-total-count",
21071
- children: totalReactionCount
21072
- }
21073
- )
21074
- ]
21075
- }
21076
- )
21077
- }
21078
- ),
21079
- /* @__PURE__ */ jsx(
21080
- DialogAnchor,
21081
- {
21082
- dialogManagerId: dialogManager?.id,
21083
- id: dialogId2,
21084
- offset: 8,
21085
- placement: isMyMessage() ? "bottom-end" : "bottom-start",
21086
- referenceElement: divRef.current,
21087
- trapFocus: true,
21088
- updatePositionOnContentResize: true,
21089
- children: /* @__PURE__ */ jsx(
21090
- MessageReactionsDetail$1,
21007
+ severity: "error",
21008
+ type: reminder ? "api:message:saveForLater:delete:failed" : "api:message:saveForLater:create:failed"
21009
+ });
21010
+ } finally {
21011
+ closeMenu();
21012
+ }
21013
+ },
21014
+ children: reminder ? t("Remove save for later") : t("Save for later")
21015
+ }
21016
+ );
21017
+ },
21018
+ Flag() {
21019
+ const { closeMenu } = useContextMenuContext();
21020
+ const { handleFlag, message } = useMessageContext();
21021
+ const { addNotification } = useNotificationApi();
21022
+ const { t } = useTranslationContext();
21023
+ return /* @__PURE__ */ jsx(
21024
+ ContextMenuButton,
21025
+ {
21026
+ "aria-label": t("aria/Flag Message"),
21027
+ className: msgActionsBoxButtonClassName,
21028
+ Icon: IconFlag,
21029
+ onClick: async (event) => {
21030
+ try {
21031
+ await handleFlag(event);
21032
+ addNotification({
21033
+ context: {
21034
+ message
21035
+ },
21036
+ emitter: "MessageActions",
21037
+ message: t("Message has been successfully flagged"),
21038
+ severity: "success",
21039
+ type: "api:message:flag:success"
21040
+ });
21041
+ } catch (error) {
21042
+ addNotification({
21043
+ context: {
21044
+ message
21045
+ },
21046
+ emitter: "MessageActions",
21047
+ error: getNotificationError(error),
21048
+ message: getErrorMessage(error, t("Error adding flag")),
21049
+ severity: "error",
21050
+ type: "api:message:flag:failed"
21051
+ });
21052
+ }
21053
+ closeMenu();
21054
+ },
21055
+ children: t("Flag")
21056
+ }
21057
+ );
21058
+ },
21059
+ Mute() {
21060
+ const { closeMenu } = useContextMenuContext();
21061
+ const { handleMute, message } = useMessageContext();
21062
+ const { addNotification } = useNotificationApi();
21063
+ const { mutes } = useChatContext();
21064
+ const { t } = useTranslationContext();
21065
+ const isMuted = isUserMuted(message, mutes);
21066
+ return /* @__PURE__ */ jsx(
21067
+ ContextMenuButton,
21068
+ {
21069
+ "aria-label": isMuted ? t("aria/Unmute User") : t("aria/Mute User"),
21070
+ className: msgActionsBoxButtonClassName,
21071
+ Icon: isMuted ? IconAudio : IconMute,
21072
+ onClick: async (event) => {
21073
+ try {
21074
+ await handleMute(event);
21075
+ addNotification({
21076
+ context: {
21077
+ message
21078
+ },
21079
+ emitter: "MessageActions",
21080
+ message: isMuted ? t("{{ user }} has been unmuted", {
21081
+ user: message.user?.name || message.user?.id
21082
+ }) : t("{{ user }} has been muted", {
21083
+ user: message.user?.name || message.user?.id
21084
+ }),
21085
+ severity: "success",
21086
+ type: isMuted ? "api:user:unmute:success" : "api:user:mute:success"
21087
+ });
21088
+ } catch (error) {
21089
+ addNotification({
21090
+ context: {
21091
+ message
21092
+ },
21093
+ emitter: "MessageActions",
21094
+ error: getNotificationError(error),
21095
+ message: getErrorMessage(
21096
+ error,
21097
+ isMuted ? t("Error unmuting a user ...") : t("Error muting a user ...")
21098
+ ),
21099
+ severity: "error",
21100
+ type: isMuted ? "api:user:unmute:failed" : "api:user:mute:failed"
21101
+ });
21102
+ }
21103
+ closeMenu();
21104
+ },
21105
+ children: isMuted ? t("Unmute") : t("Mute")
21106
+ }
21107
+ );
21108
+ },
21109
+ Delete() {
21110
+ const { closeMenu } = useContextMenuContext();
21111
+ const { addNotification } = useNotificationApi();
21112
+ const { Modal = GlobalModal } = useComponentContext();
21113
+ const { handleDelete, message } = useMessageContext();
21114
+ const { t } = useTranslationContext();
21115
+ const [openModal, setOpenModal] = useState(false);
21116
+ if (isMessageDeleted(message)) return null;
21117
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
21118
+ /* @__PURE__ */ jsx(
21119
+ ContextMenuButton,
21091
21120
  {
21092
- handleFetchReactions,
21093
- onSelectedReactionTypeChange: setSelectedReactionType,
21094
- reactionDetailsSort,
21095
- reactionGroups,
21096
- reactions: existingReactions,
21097
- selectedReactionType,
21098
- totalReactionCount
21121
+ "aria-label": t("aria/Delete Message"),
21122
+ className: msgActionsBoxButtonClassName,
21123
+ Icon: IconDelete,
21124
+ onClick: () => {
21125
+ setOpenModal(true);
21126
+ },
21127
+ variant: "destructive",
21128
+ children: t("Delete message")
21099
21129
  }
21100
- )
21101
- }
21102
- )
21103
- ] });
21104
- };
21105
- const MessageReactions = React.memo(
21106
- UnMemoizedMessageReactions
21107
- );
21108
- const getImageDimensions = (source) => new Promise((resolve, reject) => {
21109
- const image = new Image();
21110
- image.addEventListener(
21111
- "load",
21112
- () => {
21113
- resolve([image.width, image.height]);
21130
+ ),
21131
+ /* @__PURE__ */ jsx(Modal, { open: openModal, children: /* @__PURE__ */ jsx(
21132
+ DeleteMessageAlert,
21133
+ {
21134
+ onCancel: () => {
21135
+ setOpenModal(false);
21136
+ closeMenu();
21137
+ },
21138
+ onDelete: async () => {
21139
+ try {
21140
+ await handleDelete();
21141
+ addNotification({
21142
+ context: {
21143
+ message
21144
+ },
21145
+ emitter: "MessageActions",
21146
+ message: t("Message deleted"),
21147
+ severity: "success",
21148
+ type: "api:message:delete:success"
21149
+ });
21150
+ } catch (error) {
21151
+ addNotification({
21152
+ context: {
21153
+ message
21154
+ },
21155
+ emitter: "MessageActions",
21156
+ error: getNotificationError(error),
21157
+ message: getErrorMessage(error, t("Error deleting message")),
21158
+ severity: "error",
21159
+ type: "api:message:delete:failed"
21160
+ });
21161
+ } finally {
21162
+ setOpenModal(false);
21163
+ closeMenu();
21164
+ }
21165
+ }
21166
+ }
21167
+ ) })
21168
+ ] });
21114
21169
  },
21115
- { once: true }
21116
- );
21117
- image.addEventListener("error", () => reject(`Couldn't load image from ${source}`), {
21118
- once: true
21119
- });
21120
- image.src = source;
21121
- });
21122
- const SpriteImage = ({
21123
- columns,
21124
- fallback,
21125
- height,
21126
- position,
21127
- rows,
21128
- spriteUrl,
21129
- style,
21130
- width
21131
- }) => {
21132
- const [[spriteWidth, spriteHeight], setSpriteDimensions] = useState([0, 0]);
21133
- useEffect(() => {
21134
- getImageDimensions(spriteUrl).then(setSpriteDimensions).catch(console.error);
21135
- }, [spriteUrl]);
21136
- const [x, y] = position;
21137
- if (!spriteHeight || !spriteWidth) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
21138
- return /* @__PURE__ */ jsx(
21139
- "div",
21140
- {
21141
- "data-testid": "sprite-image",
21142
- style: {
21143
- ...style,
21144
- "--str-chat__sprite-image-resize-ratio": "var(--str-chat__sprite-image-resize-ratio-x, var(--str-chat__sprite-image-resize-ratio-y, 1))",
21145
- "--str-chat__sprite-image-resize-ratio-x": "calc(var(--str-chat__sprite-image-width) / var(--str-chat__sprite-item-width))",
21146
- "--str-chat__sprite-image-resize-ratio-y": "calc(var(--str-chat__sprite-image-height) / var(--str-chat__sprite-item-height))",
21147
- "--str-chat__sprite-item-height": `${spriteHeight / rows}`,
21148
- "--str-chat__sprite-item-width": `${spriteWidth / columns}`,
21149
- ...Number.isFinite(height) ? { "--str-chat__sprite-image-height": `${height}px` } : {},
21150
- ...Number.isFinite(width) ? { "--str-chat__sprite-image-width": `${width}px` } : {},
21151
- backgroundImage: `url('${spriteUrl}')`,
21152
- backgroundPosition: `${x * (100 / (columns - 1))}% ${y * (100 / (rows - 1))}%`,
21153
- backgroundSize: `${columns * 100}% ${rows * 100}%`,
21154
- height: "var(--str-chat__sprite-image-height, calc(var(--str-chat__sprite-item-height) * var(--str-chat__sprite-image-resize-ratio)))",
21155
- width: "var(--str-chat__sprite-image-width, calc(var(--str-chat__sprite-item-width) * var(--str-chat__sprite-image-resize-ratio)))"
21156
- }
21170
+ BlockUser() {
21171
+ const { closeMenu } = useContextMenuContext();
21172
+ const { client } = useChatContext();
21173
+ const { message } = useMessageContext();
21174
+ const { t } = useTranslationContext();
21175
+ const isBlocked = !message.user?.id || new Set(client.blockedUsers.getLatestValue().userIds).has(message.user?.id);
21176
+ return /* @__PURE__ */ jsx(
21177
+ ContextMenuButton,
21178
+ {
21179
+ "aria-label": isBlocked ? t("aria/Unblock User") : t("aria/Block User"),
21180
+ className: clsx(msgActionsBoxButtonClassName),
21181
+ Icon: isBlocked ? IconUserCheck : IconNoSign,
21182
+ onClick: () => {
21183
+ const targetId = message.user?.id;
21184
+ if (targetId) {
21185
+ if (isBlocked) client.unBlockUser(targetId);
21186
+ else client.blockUser(targetId);
21187
+ }
21188
+ closeMenu();
21189
+ },
21190
+ children: isBlocked ? t("Unblock User") : t("Block User")
21191
+ }
21192
+ );
21157
21193
  }
21158
- );
21194
+ },
21195
+ quick: {
21196
+ // eslint-disable-next-line react/display-name
21197
+ DropdownToggle: forwardRef((_, ref) => {
21198
+ const { t } = useTranslationContext();
21199
+ const { message, threadList } = useMessageContext();
21200
+ const dropdownDialogIsOpen = useDialogIsOpen(
21201
+ MessageActions.getDialogId({ messageId: message.id })
21202
+ );
21203
+ const { dialog } = useDialogOnNearestManager({
21204
+ id: MessageActions.getDialogId({ messageId: message.id })
21205
+ });
21206
+ const reactionSelectorDialogId = ReactionSelector.getDialogId({
21207
+ messageId: message.id,
21208
+ threadList
21209
+ });
21210
+ const { dialog: dropdownReactionSelectorDialog } = useDialogOnNearestManager({
21211
+ id: `${reactionSelectorDialogId}-dropdown`
21212
+ });
21213
+ return /* @__PURE__ */ jsx(
21214
+ QuickMessageActionsButton,
21215
+ {
21216
+ "aria-expanded": dropdownDialogIsOpen,
21217
+ "aria-haspopup": "true",
21218
+ "aria-label": t("aria/Open Message Actions Menu"),
21219
+ className: "str-chat__message-actions-box-button",
21220
+ "data-testid": "message-actions-toggle-button",
21221
+ onClick: () => {
21222
+ dropdownReactionSelectorDialog?.close();
21223
+ dialog?.toggle();
21224
+ },
21225
+ ref,
21226
+ children: /* @__PURE__ */ jsx(IconMore, { className: "str-chat__message-action-icon" })
21227
+ }
21228
+ );
21229
+ }),
21230
+ React() {
21231
+ return /* @__PURE__ */ jsx(ReactionSelectorWithButton, { ReactionIcon: IconEmoji });
21232
+ },
21233
+ Reply() {
21234
+ const { handleOpenThread } = useMessageContext();
21235
+ const { t } = useTranslationContext();
21236
+ return /* @__PURE__ */ jsx(
21237
+ QuickMessageActionsButton,
21238
+ {
21239
+ "aria-label": t("aria/Open Thread"),
21240
+ className: "str-chat__message-reply-in-thread-button",
21241
+ "data-testid": "thread-action",
21242
+ onClick: handleOpenThread,
21243
+ children: /* @__PURE__ */ jsx(IconReply, { className: "str-chat__message-action-icon" })
21244
+ }
21245
+ );
21246
+ }
21247
+ }
21159
21248
  };
21249
+ const defaultMessageActionSet = [
21250
+ {
21251
+ Component: DefaultMessageActionComponents.quick.DropdownToggle,
21252
+ placement: "quick-dropdown-toggle"
21253
+ },
21254
+ {
21255
+ Component: DefaultMessageActionComponents.quick.Reply,
21256
+ placement: "quick",
21257
+ type: "reply"
21258
+ },
21259
+ {
21260
+ Component: DefaultMessageActionComponents.quick.React,
21261
+ placement: "quick",
21262
+ type: "react"
21263
+ },
21264
+ {
21265
+ Component: DefaultMessageActionComponents.dropdown.React,
21266
+ placement: "dropdown",
21267
+ type: "react"
21268
+ },
21269
+ {
21270
+ Component: DefaultMessageActionComponents.dropdown.ThreadReply,
21271
+ placement: "dropdown",
21272
+ type: "reply"
21273
+ },
21274
+ {
21275
+ Component: DefaultMessageActionComponents.dropdown.Quote,
21276
+ placement: "dropdown",
21277
+ type: "quote"
21278
+ },
21279
+ {
21280
+ Component: DefaultMessageActionComponents.dropdown.Pin,
21281
+ placement: "dropdown",
21282
+ type: "pin"
21283
+ },
21284
+ {
21285
+ Component: DefaultMessageActionComponents.dropdown.CopyMessageText,
21286
+ placement: "dropdown",
21287
+ type: "copyMessageText"
21288
+ },
21289
+ {
21290
+ Component: DefaultMessageActionComponents.dropdown.Resend,
21291
+ placement: "dropdown",
21292
+ type: "resendMessage"
21293
+ },
21294
+ {
21295
+ Component: DefaultMessageActionComponents.dropdown.Edit,
21296
+ placement: "dropdown",
21297
+ type: "edit"
21298
+ },
21299
+ {
21300
+ Component: DefaultMessageActionComponents.dropdown.MarkUnread,
21301
+ placement: "dropdown",
21302
+ type: "markUnread"
21303
+ },
21304
+ {
21305
+ Component: DefaultMessageActionComponents.dropdown.RemindMe,
21306
+ placement: "dropdown",
21307
+ type: "remindMe"
21308
+ },
21309
+ {
21310
+ Component: DefaultMessageActionComponents.dropdown.SaveForLater,
21311
+ placement: "dropdown",
21312
+ type: "saveForLater"
21313
+ },
21314
+ {
21315
+ Component: DefaultMessageActionComponents.dropdown.Flag,
21316
+ placement: "dropdown",
21317
+ type: "flag"
21318
+ },
21319
+ {
21320
+ Component: DefaultMessageActionComponents.dropdown.Mute,
21321
+ placement: "dropdown",
21322
+ type: "mute"
21323
+ },
21324
+ {
21325
+ Component: DefaultMessageActionComponents.dropdown.Delete,
21326
+ placement: "dropdown",
21327
+ type: "delete"
21328
+ },
21329
+ {
21330
+ Component: DefaultMessageActionComponents.dropdown.BlockUser,
21331
+ placement: "dropdown",
21332
+ type: "blockUser"
21333
+ }
21334
+ ];
21160
21335
  const useSplitActionSet = (actionSet) => useMemo(() => {
21161
21336
  const quickActionSet = [];
21162
21337
  const dropdownActionSet = [];
@@ -21190,6 +21365,7 @@ const MessageActions = ({
21190
21365
  messageId: message.id,
21191
21366
  threadList
21192
21367
  });
21368
+ const dropdownReactionSelectorDialogId = `${reactionSelectorDialogId}-dropdown`;
21193
21369
  const { dialog, dialogManager } = useDialogOnNearestManager({
21194
21370
  id: messageActionsDialogId
21195
21371
  });
@@ -21201,6 +21377,10 @@ const MessageActions = ({
21201
21377
  reactionSelectorDialogId,
21202
21378
  dialogManager?.id
21203
21379
  );
21380
+ const dropdownReactionSelectorDialogIsOpen = useDialogIsOpen(
21381
+ dropdownReactionSelectorDialogId,
21382
+ dialogManager?.id
21383
+ );
21204
21384
  if (dropdownActionSet.length + quickActionSet.length === 0) {
21205
21385
  return null;
21206
21386
  }
@@ -21208,7 +21388,7 @@ const MessageActions = ({
21208
21388
  "div",
21209
21389
  {
21210
21390
  className: clsx("str-chat__message-options", {
21211
- "str-chat__message-options--active": messageActionsDialogIsOpen || reactionSelectorDialogIsOpen
21391
+ "str-chat__message-options--active": messageActionsDialogIsOpen || reactionSelectorDialogIsOpen || dropdownReactionSelectorDialogIsOpen
21212
21392
  }),
21213
21393
  children: [
21214
21394
  quickDropdownToggleAction && dropdownActionSet.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -21218,6 +21398,7 @@ const MessageActions = ({
21218
21398
  {
21219
21399
  backLabel: t("Back"),
21220
21400
  className: clsx("str-chat__message-actions-box", {
21401
+ "str-chat__message-actions-box--hidden": dropdownReactionSelectorDialogIsOpen,
21221
21402
  "str-chat__message-actions-box--open": messageActionsDialogIsOpen
21222
21403
  }),
21223
21404
  dialogManagerId: dialogManager?.id,
@@ -21282,6 +21463,7 @@ const MessageUIWithContext = ({
21282
21463
  () => isMessageAIGenerated?.(message),
21283
21464
  [isMessageAIGenerated, message]
21284
21465
  );
21466
+ const isDeleted = isMessageDeleted(message);
21285
21467
  const finalAttachments = useMemo(
21286
21468
  () => !message.shared_location && !message.attachments ? [] : !message.shared_location ? message.attachments : [message.shared_location, ...message.attachments ?? []],
21287
21469
  [message]
@@ -21289,7 +21471,7 @@ const MessageUIWithContext = ({
21289
21471
  if (isDateSeparatorMessage(message)) {
21290
21472
  return null;
21291
21473
  }
21292
- if (MessageDeleted && (message.deleted_at || message.type === "deleted")) {
21474
+ if (MessageDeleted && isDeleted) {
21293
21475
  return /* @__PURE__ */ jsx(MessageDeleted, { message });
21294
21476
  }
21295
21477
  if (isMessageBlocked(message)) {
@@ -21297,7 +21479,6 @@ const MessageUIWithContext = ({
21297
21479
  }
21298
21480
  const poll = message.poll_id && client.polls.fromState(message.poll_id);
21299
21481
  const memberCount = Object.keys(channel?.state?.members ?? {}).length;
21300
- const isDeleted = !!message.deleted_at;
21301
21482
  const hasAttachment = !isDeleted && messageHasAttachments(message);
21302
21483
  const hasSingleAttachment = !isDeleted && messageHasSingleAttachment(message);
21303
21484
  const hasGiphyAttachment = !isDeleted && messageHasGiphyAttachment(message);
@@ -21388,7 +21569,7 @@ const MessageUIWithContext = ({
21388
21569
  thread_participants: message.thread_participants
21389
21570
  }
21390
21571
  ),
21391
- message.deleted_at ? /* @__PURE__ */ jsx(MessageDeletedBubble$1, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
21572
+ isDeleted ? /* @__PURE__ */ jsx(MessageDeletedBubble$1, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
21392
21573
  /* @__PURE__ */ jsxs(MessageBubble, { children: [
21393
21574
  poll && /* @__PURE__ */ jsx(Poll, { poll }),
21394
21575
  message.quoted_message && /* @__PURE__ */ jsx(QuotedMessage$1, {}),
@@ -21673,26 +21854,21 @@ const useFloatingDateSeparatorMessageList = ({
21673
21854
  }
21674
21855
  const containerRect = listElement.getBoundingClientRect();
21675
21856
  let bestDate = null;
21676
- let bestBottom = -Infinity;
21677
- let anyVisible = false;
21857
+ let bestTop = -Infinity;
21678
21858
  for (const el of separators) {
21679
21859
  const rect = el.getBoundingClientRect();
21680
21860
  const dataDate = el.getAttribute("data-date");
21681
21861
  if (!dataDate) continue;
21682
- const isAboveViewport = rect.bottom < containerRect.top;
21683
- const isVisible = rect.top < containerRect.bottom && rect.bottom > containerRect.top;
21684
- if (isVisible) {
21685
- anyVisible = true;
21686
- }
21687
- if (isAboveViewport && rect.bottom > bestBottom) {
21688
- bestBottom = rect.bottom;
21862
+ const isAtOrAboveTopBoundary = rect.top <= containerRect.top;
21863
+ if (isAtOrAboveTopBoundary && rect.top > bestTop) {
21864
+ bestTop = rect.top;
21689
21865
  const d = new Date(dataDate);
21690
21866
  if (!isNaN(d.getTime())) bestDate = d;
21691
21867
  }
21692
21868
  }
21693
21869
  setState({
21694
- date: anyVisible ? null : bestDate,
21695
- visible: !anyVisible && bestDate !== null
21870
+ date: bestDate,
21871
+ visible: bestDate !== null
21696
21872
  });
21697
21873
  }, [disableDateSeparator, listElement, processedMessages]);
21698
21874
  useEffect(() => {
@@ -22689,6 +22865,10 @@ function getFloatingDateForFirstMessage(firstMessage, processedMessages, firstMe
22689
22865
  }
22690
22866
  return null;
22691
22867
  }
22868
+ function getFloatingDateForFirstItem(firstItem, processedMessages, firstItemIndex) {
22869
+ if (isDateSeparatorMessage(firstItem)) return firstItem.date;
22870
+ return getFloatingDateForFirstMessage(firstItem, processedMessages, firstItemIndex);
22871
+ }
22692
22872
  const HIDDEN_STATE = { date: null, visible: false };
22693
22873
  const useFloatingDateSeparator = ({
22694
22874
  disableDateSeparator,
@@ -22707,17 +22887,8 @@ const useFloatingDateSeparator = ({
22707
22887
  return;
22708
22888
  }
22709
22889
  const first = valid[0];
22710
- if (isDateSeparatorMessage(first)) {
22711
- setState(HIDDEN_STATE);
22712
- return;
22713
- }
22714
- const hasVisibleDateSeparator = valid.some(isDateSeparatorMessage);
22715
- if (hasVisibleDateSeparator) {
22716
- setState(HIDDEN_STATE);
22717
- return;
22718
- }
22719
22890
  const firstIndex = processedMessages.findIndex((m) => m.id === first.id);
22720
- const date = firstIndex >= 0 ? getFloatingDateForFirstMessage(first, processedMessages, firstIndex) : null;
22891
+ const date = firstIndex >= 0 ? getFloatingDateForFirstItem(first, processedMessages, firstIndex) : null;
22721
22892
  const visible = date !== null;
22722
22893
  setState((prev) => {
22723
22894
  const prevTime = prev.date?.getTime() ?? null;
@@ -25645,77 +25816,9 @@ const useArchiveActionButtonBehavior = () => {
25645
25816
  title: membership.archived_at ? t("Unarchive") : t("Archive")
25646
25817
  };
25647
25818
  };
25648
- const defaultChannelActionSet = [
25649
- {
25650
- // eslint-disable-next-line react/display-name
25651
- Component: forwardRef((_, ref) => {
25652
- const { channel } = useChannelListItemContext();
25653
- const dialogId2 = ChannelListItemActionButtons.getDialogId({
25654
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
25655
- channelId: channel.id
25656
- });
25657
- const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId2 });
25658
- const dialogIsOpen = useDialogIsOpen(dialogId2, dialogManager?.id);
25659
- return /* @__PURE__ */ jsx(
25660
- Button,
25661
- {
25662
- appearance: "ghost",
25663
- "aria-expanded": dialogIsOpen,
25664
- "aria-pressed": dialogIsOpen,
25665
- circular: true,
25666
- onClick: (e) => {
25667
- e.stopPropagation();
25668
- dialog.toggle();
25669
- },
25670
- ref,
25671
- size: "sm",
25672
- variant: "secondary",
25673
- children: /* @__PURE__ */ jsx(IconMore, {})
25674
- }
25675
- );
25676
- }),
25677
- placement: "quick-dropdown-toggle"
25678
- },
25679
- {
25680
- Component() {
25681
- const behaviorProps = useArchiveActionButtonBehavior();
25682
- return /* @__PURE__ */ jsx(
25683
- Button,
25684
- {
25685
- appearance: "ghost",
25686
- "aria-label": behaviorProps.title,
25687
- circular: true,
25688
- size: "sm",
25689
- variant: "secondary",
25690
- ...behaviorProps,
25691
- children: /* @__PURE__ */ jsx(IconArchive, {})
25692
- }
25693
- );
25694
- },
25695
- placement: "quick",
25696
- type: "archive"
25697
- },
25698
- {
25699
- Component() {
25700
- const behaviorProps = useMuteActionButtonBehavior();
25701
- return /* @__PURE__ */ jsx(
25702
- Button,
25703
- {
25704
- appearance: "ghost",
25705
- "aria-label": behaviorProps.title,
25706
- circular: true,
25707
- size: "sm",
25708
- variant: "secondary",
25709
- ...behaviorProps,
25710
- children: /* @__PURE__ */ jsx(IconMute, {})
25711
- }
25712
- );
25713
- },
25714
- placement: "quick",
25715
- type: "mute"
25716
- },
25717
- {
25718
- Component() {
25819
+ const defaultComponents = {
25820
+ dropdown: {
25821
+ Archive() {
25719
25822
  const behaviorProps = useArchiveActionButtonBehavior();
25720
25823
  return /* @__PURE__ */ jsx(
25721
25824
  ContextMenuButton,
@@ -25727,27 +25830,7 @@ const defaultChannelActionSet = [
25727
25830
  }
25728
25831
  );
25729
25832
  },
25730
- placement: "dropdown",
25731
- type: "archive"
25732
- },
25733
- {
25734
- Component() {
25735
- const behaviorProps = useMuteActionButtonBehavior();
25736
- return /* @__PURE__ */ jsx(
25737
- ContextMenuButton,
25738
- {
25739
- "aria-label": behaviorProps.title,
25740
- Icon: IconMute,
25741
- ...behaviorProps,
25742
- children: behaviorProps.title
25743
- }
25744
- );
25745
- },
25746
- placement: "dropdown",
25747
- type: "mute"
25748
- },
25749
- {
25750
- Component() {
25833
+ Ban() {
25751
25834
  const { client } = useChatContext();
25752
25835
  const { addNotification } = useNotificationApi();
25753
25836
  const { t } = useTranslationContext();
@@ -25814,11 +25897,55 @@ const defaultChannelActionSet = [
25814
25897
  }
25815
25898
  );
25816
25899
  },
25817
- placement: "dropdown",
25818
- type: "ban"
25819
- },
25820
- {
25821
- Component() {
25900
+ Leave() {
25901
+ const { t } = useTranslationContext();
25902
+ const { channel } = useChannelListItemContext();
25903
+ const { client } = useChatContext();
25904
+ const { addNotification } = useNotificationApi();
25905
+ const [inProgress, setInProgress] = useState(false);
25906
+ const title = t("Leave Channel");
25907
+ return /* @__PURE__ */ jsx(
25908
+ ContextMenuButton,
25909
+ {
25910
+ "aria-label": title,
25911
+ disabled: inProgress,
25912
+ Icon: IconLeave,
25913
+ onClick: async (e) => {
25914
+ e.stopPropagation();
25915
+ try {
25916
+ setInProgress(true);
25917
+ await channel.removeMembers([client.userID]);
25918
+ addNotification({
25919
+ context: {
25920
+ channel
25921
+ },
25922
+ emitter: ChannelListItemActionButtons.name,
25923
+ message: t("Left channel"),
25924
+ severity: "success",
25925
+ type: "api:channel:leave:success"
25926
+ });
25927
+ } catch (error) {
25928
+ addNotification({
25929
+ context: {
25930
+ channel
25931
+ },
25932
+ emitter: ChannelListItemActionButtons.name,
25933
+ error: error instanceof Error ? error : new Error("An unknown error occurred"),
25934
+ message: t("Failed to leave channel"),
25935
+ severity: "error",
25936
+ type: "api:channel:leave:failed"
25937
+ });
25938
+ } finally {
25939
+ setInProgress(false);
25940
+ }
25941
+ },
25942
+ title,
25943
+ variant: "destructive",
25944
+ children: title
25945
+ }
25946
+ );
25947
+ },
25948
+ Pin() {
25822
25949
  const { t } = useTranslationContext();
25823
25950
  const { addNotification } = useNotificationApi();
25824
25951
  const { channel } = useChannelListItemContext();
@@ -25885,59 +26012,80 @@ const defaultChannelActionSet = [
25885
26012
  children: title
25886
26013
  }
25887
26014
  );
25888
- },
25889
- placement: "dropdown",
25890
- type: "pin"
26015
+ }
25891
26016
  },
25892
- {
25893
- Component() {
25894
- const { t } = useTranslationContext();
25895
- const { channel } = useChannelListItemContext();
25896
- const { client } = useChatContext();
25897
- const { addNotification } = useNotificationApi();
25898
- const [inProgress, setInProgress] = useState(false);
25899
- const title = t("Leave Channel");
26017
+ quick: {
26018
+ Mute() {
26019
+ const behaviorProps = useMuteActionButtonBehavior();
25900
26020
  return /* @__PURE__ */ jsx(
25901
- ContextMenuButton,
26021
+ Button,
25902
26022
  {
25903
- "aria-label": title,
25904
- disabled: inProgress,
25905
- Icon: IconLeave,
25906
- onClick: async (e) => {
25907
- e.stopPropagation();
25908
- try {
25909
- setInProgress(true);
25910
- await channel.removeMembers([client.userID]);
25911
- addNotification({
25912
- context: {
25913
- channel
25914
- },
25915
- emitter: ChannelListItemActionButtons.name,
25916
- message: t("Left channel"),
25917
- severity: "success",
25918
- type: "api:channel:leave:success"
25919
- });
25920
- } catch (error) {
25921
- addNotification({
25922
- context: {
25923
- channel
25924
- },
25925
- emitter: ChannelListItemActionButtons.name,
25926
- error: error instanceof Error ? error : new Error("An unknown error occurred"),
25927
- message: t("Failed to leave channel"),
25928
- severity: "error",
25929
- type: "api:channel:leave:failed"
25930
- });
25931
- } finally {
25932
- setInProgress(false);
25933
- }
25934
- },
25935
- title,
25936
- variant: "destructive",
25937
- children: title
26023
+ appearance: "ghost",
26024
+ "aria-label": behaviorProps.title,
26025
+ circular: true,
26026
+ size: "sm",
26027
+ variant: "secondary",
26028
+ ...behaviorProps,
26029
+ children: /* @__PURE__ */ jsx(IconMute, {})
25938
26030
  }
25939
26031
  );
25940
- },
26032
+ }
26033
+ },
26034
+ QuickDropdownToggle: forwardRef((_, ref) => {
26035
+ const { channel } = useChannelListItemContext();
26036
+ const dialogId2 = ChannelListItemActionButtons.getDialogId({
26037
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
26038
+ channelId: channel.id
26039
+ });
26040
+ const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId2 });
26041
+ const dialogIsOpen = useDialogIsOpen(dialogId2, dialogManager?.id);
26042
+ return /* @__PURE__ */ jsx(
26043
+ Button,
26044
+ {
26045
+ appearance: "ghost",
26046
+ "aria-expanded": dialogIsOpen,
26047
+ "aria-pressed": dialogIsOpen,
26048
+ circular: true,
26049
+ onClick: (e) => {
26050
+ e.stopPropagation();
26051
+ dialog.toggle();
26052
+ },
26053
+ ref,
26054
+ size: "sm",
26055
+ variant: "secondary",
26056
+ children: /* @__PURE__ */ jsx(IconMore, {})
26057
+ }
26058
+ );
26059
+ })
26060
+ };
26061
+ defaultComponents.QuickDropdownToggle.displayName = "QuickDropdownToggle";
26062
+ const defaultChannelActionSet = [
26063
+ {
26064
+ Component: defaultComponents.QuickDropdownToggle,
26065
+ placement: "quick-dropdown-toggle"
26066
+ },
26067
+ {
26068
+ Component: defaultComponents.quick.Mute,
26069
+ placement: "quick",
26070
+ type: "mute"
26071
+ },
26072
+ {
26073
+ Component: defaultComponents.dropdown.Archive,
26074
+ placement: "dropdown",
26075
+ type: "archive"
26076
+ },
26077
+ {
26078
+ Component: defaultComponents.dropdown.Ban,
26079
+ placement: "dropdown",
26080
+ type: "ban"
26081
+ },
26082
+ {
26083
+ Component: defaultComponents.dropdown.Pin,
26084
+ placement: "dropdown",
26085
+ type: "pin"
26086
+ },
26087
+ {
26088
+ Component: defaultComponents.dropdown.Leave,
25941
26089
  placement: "dropdown",
25942
26090
  type: "leave"
25943
26091
  }
@@ -25945,8 +26093,6 @@ const defaultChannelActionSet = [
25945
26093
  const useBaseChannelActionSetFilter = (channelActionSet) => {
25946
26094
  const { channel } = useChannelListItemContext();
25947
26095
  const membership = useChannelMembershipState(channel);
25948
- const isDirectMessageChannel = channel.type === "messaging" && // assuming one of the users is current user
25949
- channel.data?.member_count === 2 && channel.id?.startsWith("!members-");
25950
26096
  const memberCount = channel.data?.member_count ?? 0;
25951
26097
  const connectedUserIsMember = typeof membership.user !== "undefined";
25952
26098
  const ownCapabilities = channel.data?.own_capabilities;
@@ -25955,9 +26101,9 @@ const useBaseChannelActionSetFilter = (channelActionSet) => {
25955
26101
  if (action.placement === "quick-dropdown-toggle") return true;
25956
26102
  switch (action.type) {
25957
26103
  case "archive":
25958
- return connectedUserIsMember && (action.placement === "quick" && isDirectMessageChannel || action.placement === "dropdown" && !isDirectMessageChannel);
26104
+ return connectedUserIsMember;
25959
26105
  case "mute":
25960
- return ownCapabilities?.includes("mute-channel") && (action.placement === "dropdown" && isDirectMessageChannel || action.placement === "quick" && !isDirectMessageChannel);
26106
+ return ownCapabilities?.includes("mute-channel");
25961
26107
  case "ban":
25962
26108
  return memberCount > 0 && memberCount <= 2 && ownCapabilities?.includes("ban-channel-members");
25963
26109
  case "leave":
@@ -25969,13 +26115,7 @@ const useBaseChannelActionSetFilter = (channelActionSet) => {
25969
26115
  }
25970
26116
  });
25971
26117
  return filtered;
25972
- }, [
25973
- channelActionSet,
25974
- isDirectMessageChannel,
25975
- memberCount,
25976
- ownCapabilities,
25977
- connectedUserIsMember
25978
- ]);
26118
+ }, [channelActionSet, memberCount, ownCapabilities, connectedUserIsMember]);
25979
26119
  };
25980
26120
  const ChannelListItemActionButtons = () => {
25981
26121
  const { ContextMenu: ContextMenuComponent = ContextMenu } = useComponentContext();
@@ -26105,7 +26245,7 @@ const useLatestMessagePreview = ({
26105
26245
  } else if (!isOwnMessage && participantCount !== void 0 && participantCount > 2) {
26106
26246
  senderName = latestMessage.user?.name || latestMessage.user?.id;
26107
26247
  }
26108
- if (latestMessage.deleted_at) {
26248
+ if (isMessageDeleted(latestMessage)) {
26109
26249
  return {
26110
26250
  deliveryStatus,
26111
26251
  senderName,
@@ -28230,7 +28370,7 @@ const useChat = ({
28230
28370
  };
28231
28371
  useEffect(() => {
28232
28372
  if (!client) return;
28233
- const version = "14.0.0-beta.7";
28373
+ const version = "14.0.0-beta.8";
28234
28374
  const userAgent = client.getUserAgent();
28235
28375
  if (!userAgent.includes("stream-chat-react")) {
28236
28376
  client.setUserAgent(`stream-chat-react-${version}-${userAgent}`);
@@ -28414,7 +28554,6 @@ const Chat = (props) => {
28414
28554
  defaultLanguage,
28415
28555
  i18nInstance
28416
28556
  });
28417
- useReportLostConnectionSystemNotification();
28418
28557
  const channelsQueryState = useChannelsQueryState();
28419
28558
  const searchController = useMemo(
28420
28559
  () => customChannelSearchController ?? new SearchController({
@@ -28505,7 +28644,7 @@ export {
28505
28644
  AttachmentWithinContainer,
28506
28645
  Audio,
28507
28646
  AudioContainer,
28508
- bx as AudioPlayer,
28647
+ by as AudioPlayer,
28509
28648
  AudioRecorder,
28510
28649
  Avatar,
28511
28650
  AvatarStack,
@@ -28517,13 +28656,13 @@ export {
28517
28656
  Callout,
28518
28657
  Card,
28519
28658
  CardContainer,
28520
- bA as Channel,
28521
- c2 as ChannelActionContext,
28522
- c3 as ChannelActionProvider,
28659
+ bB as Channel,
28660
+ c3 as ChannelActionContext,
28661
+ c4 as ChannelActionProvider,
28523
28662
  ChannelAvatar,
28524
28663
  ChannelHeader,
28525
28664
  ChannelList,
28526
- c4 as ChannelListContext,
28665
+ c5 as ChannelListContext,
28527
28666
  ChannelListContextProvider,
28528
28667
  ChannelListItem,
28529
28668
  ChannelListItemActionButtons,
@@ -28531,23 +28670,23 @@ export {
28531
28670
  ChannelListItemUI,
28532
28671
  ChannelListUI,
28533
28672
  ChannelSearchResultItem,
28534
- c5 as ChannelStateContext,
28535
- c6 as ChannelStateProvider,
28673
+ c6 as ChannelStateContext,
28674
+ c7 as ChannelStateProvider,
28536
28675
  Chat,
28537
- c7 as ChatContext,
28676
+ c8 as ChatContext,
28538
28677
  ChatProvider,
28539
- bC as ChatView,
28540
- bF as ChatViewChannelsSelectorButton,
28541
- bB as ChatViewContext,
28542
- bE as ChatViewSelectorButton,
28543
- bG as ChatViewThreadsSelectorButton,
28678
+ bD as ChatView,
28679
+ bG as ChatViewChannelsSelectorButton,
28680
+ bC as ChatViewContext,
28681
+ bF as ChatViewSelectorButton,
28682
+ bH as ChatViewThreadsSelectorButton,
28544
28683
  CheckSignIcon,
28545
28684
  Checkmark,
28546
28685
  CircularProgressIndicator,
28547
28686
  CommandChip,
28548
28687
  CommandItem,
28549
28688
  ComponentContext,
28550
- c8 as ComponentProvider,
28689
+ c9 as ComponentProvider,
28551
28690
  ContextMenu,
28552
28691
  ContextMenuBackButton,
28553
28692
  ContextMenuBody,
@@ -28633,8 +28772,8 @@ export {
28633
28772
  IconLink,
28634
28773
  IconLoading,
28635
28774
  IconLocation,
28636
- bJ as IconMessageBubble,
28637
- bK as IconMessageBubbleFill,
28775
+ bK as IconMessageBubble,
28776
+ bL as IconMessageBubbleFill,
28638
28777
  IconMessageBubbles,
28639
28778
  IconMicrophoneSolid,
28640
28779
  IconMinus,
@@ -28657,7 +28796,7 @@ export {
28657
28796
  IconSearch,
28658
28797
  IconSend,
28659
28798
  IconThread,
28660
- bL as IconThreadFill,
28799
+ bM as IconThreadFill,
28661
28800
  IconTranslate,
28662
28801
  IconTrophy,
28663
28802
  IconUnpin,
@@ -28681,9 +28820,9 @@ export {
28681
28820
  LinkPreviewList,
28682
28821
  LoadMoreButton,
28683
28822
  LoadMorePaginator,
28684
- bM as LoadingChannel,
28823
+ bN as LoadingChannel,
28685
28824
  LoadingChannels,
28686
- bN as LoadingErrorIndicator,
28825
+ bO as LoadingErrorIndicator,
28687
28826
  LoadingIndicator,
28688
28827
  MAX_MESSAGE_REACTIONS_TO_FETCH,
28689
28828
  MESSAGE_ACTIONS,
@@ -28697,7 +28836,7 @@ export {
28697
28836
  MessageBouncePrompt,
28698
28837
  MessageBounceProvider,
28699
28838
  MessageComposer,
28700
- c9 as MessageComposerContext,
28839
+ ca as MessageComposerContext,
28701
28840
  MessageComposerContextProvider,
28702
28841
  MessageComposerUI,
28703
28842
  MessageContext,
@@ -28729,7 +28868,7 @@ export {
28729
28868
  NotificationList,
28730
28869
  NotificationTranslationTopic,
28731
28870
  NumericInput,
28732
- bP as OPTIONAL_MESSAGE_ACTIONS,
28871
+ bQ as OPTIONAL_MESSAGE_ACTIONS,
28733
28872
  OtherFilesContainer,
28734
28873
  PauseIcon,
28735
28874
  PinIndicator,
@@ -28765,7 +28904,7 @@ export {
28765
28904
  ReminderNotification,
28766
28905
  Root,
28767
28906
  SUPPORTED_VIDEO_FORMATS$1 as SUPPORTED_VIDEO_FORMATS,
28768
- bV as SYSTEM_NOTIFICATION_TAG,
28907
+ bW as SYSTEM_NOTIFICATION_TAG,
28769
28908
  SafeAnchor,
28770
28909
  ScrollToLatestMessageButton,
28771
28910
  Search,
@@ -28792,26 +28931,30 @@ export {
28792
28931
  SuggestPollOptionPrompt,
28793
28932
  SuggestionList,
28794
28933
  SuggestionListItem,
28934
+ SwitchField,
28935
+ SwitchFieldDescription,
28936
+ SwitchFieldLabel,
28937
+ SwitchFieldTitle,
28795
28938
  TextInput,
28796
28939
  TextInputFieldSet,
28797
28940
  TextareaComposer,
28798
28941
  Thread,
28799
- c0 as ThreadContext,
28942
+ c1 as ThreadContext,
28800
28943
  ThreadHeader,
28801
28944
  ThreadList,
28802
28945
  ThreadListItem,
28803
28946
  ThreadListItemUI,
28804
- c1 as ThreadProvider,
28947
+ c2 as ThreadProvider,
28805
28948
  ThreadStart,
28806
28949
  Tooltip,
28807
28950
  TranslationBuilder,
28808
- ca as TranslationContext,
28951
+ cb as TranslationContext,
28809
28952
  TranslationProvider,
28810
28953
  TranslationTopic,
28811
- cb as TypingContext,
28954
+ cc as TypingContext,
28812
28955
  TypingIndicator,
28813
28956
  TypingIndicatorHeader,
28814
- cc as TypingProvider,
28957
+ cd as TypingProvider,
28815
28958
  UNREAD_MESSAGE_SEPARATOR_CLASS,
28816
28959
  UnMemoizedLoadMorePaginator,
28817
28960
  UnreadMessagesNotification,
@@ -28832,30 +28975,30 @@ export {
28832
28975
  VoiceRecordingPlayer,
28833
28976
  WaveProgressBar,
28834
28977
  Window,
28835
- bu as WithAudioPlayback,
28978
+ bv as WithAudioPlayback,
28836
28979
  WithComponents,
28837
28980
  WithDragAndDropUpload,
28838
- b_ as addNotificationTargetTag,
28981
+ b$ as addNotificationTargetTag,
28839
28982
  areMessagePropsEqual,
28840
28983
  areMessageUIPropsEqual,
28841
28984
  countEmojis,
28842
- bI as createIcon,
28985
+ bJ as createIcon,
28843
28986
  deTranslations,
28844
28987
  defaultAllowedTagNames,
28845
28988
  defaultAttachmentActionsDefaultFocus,
28846
28989
  defaultAttachmentSelectorActionSet,
28847
- bH as defaultChatViewSelectorItemSet,
28848
- defaultComponents,
28990
+ bI as defaultChatViewSelectorItemSet,
28991
+ defaultComponents$1 as defaultComponents,
28849
28992
  defaultDateTimeParser,
28850
28993
  defaultMessageActionSet,
28851
28994
  defaultReactionOptions,
28852
- bv as defaultRegisterAudioPlayerError,
28995
+ bw as defaultRegisterAudioPlayerError,
28853
28996
  defaultRenderMessages,
28854
28997
  defaultTranslatorFunction,
28855
28998
  displayDuration,
28856
28999
  divMod,
28857
29000
  downSample,
28858
- bw as elementIsPlaying,
29001
+ bx as elementIsPlaying,
28859
29002
  emojiMarkdownPlugin,
28860
29003
  emojiToUnicode,
28861
29004
  enTranslations,
@@ -28869,21 +29012,21 @@ export {
28869
29012
  getCssDimensionsVariables,
28870
29013
  getGroupChannelDisplayInfo,
28871
29014
  getGroupStyles,
28872
- bQ as getImages,
29015
+ bR as getImages,
28873
29016
  getIsFirstUnreadMessage,
28874
29017
  getLastReceived,
28875
29018
  getLatestMessagePreview,
28876
29019
  getMessageActions,
28877
- bR as getNonImageAttachments,
28878
- bX as getNotificationTargetPanel,
28879
- bY as getNotificationTargetPanels,
28880
- bZ as getNotificationTargetTag,
29020
+ bS as getNonImageAttachments,
29021
+ bY as getNotificationTargetPanel,
29022
+ bZ as getNotificationTargetPanels,
29023
+ b_ as getNotificationTargetTag,
28881
29024
  getReadByTooltipText,
28882
29025
  getTextareaCaretRect,
28883
29026
  getTranslatedMessageText,
28884
29027
  getWholeChar,
28885
29028
  handleActionWarning,
28886
- bU as hasMoreMessagesProbably,
29029
+ bV as hasMoreMessagesProbably,
28887
29030
  hasSystemNotificationTag,
28888
29031
  hiTranslations,
28889
29032
  htmlToTextPlugin,
@@ -28894,7 +29037,7 @@ export {
28894
29037
  isChrome,
28895
29038
  isDate,
28896
29039
  isDateSeparatorMessage,
28897
- cd as isDayOrMoment,
29040
+ ce as isDayOrMoment,
28898
29041
  isFirefox,
28899
29042
  isGalleryAttachmentType,
28900
29043
  isIntroMessage,
@@ -28902,12 +29045,13 @@ export {
28902
29045
  isLocalMessage,
28903
29046
  isMessageBlocked,
28904
29047
  isMessageBounced,
29048
+ isMessageDeleted,
28905
29049
  isMessageEdited,
28906
29050
  isMessageErrorRetryable,
28907
29051
  isNetworkSendFailure,
28908
29052
  isNotificationForPanel,
28909
- bW as isNotificationTargetPanel,
28910
- ce as isNumberOrString,
29053
+ bX as isNotificationTargetPanel,
29054
+ cf as isNumberOrString,
28911
29055
  isSafari,
28912
29056
  isSvgAttachment,
28913
29057
  isUserMuted,
@@ -28915,8 +29059,8 @@ export {
28915
29059
  jaTranslations,
28916
29060
  keepLineBreaksPlugin,
28917
29061
  koTranslations,
28918
- bT as makeDateMessageId,
28919
- bS as makeIntroMessage,
29062
+ bU as makeDateMessageId,
29063
+ bT as makeIntroMessage,
28920
29064
  mapEmojiMartData,
28921
29065
  mapToUserNameOrId,
28922
29066
  markDownRenderers,
@@ -28953,8 +29097,8 @@ export {
28953
29097
  upSample,
28954
29098
  useAIState,
28955
29099
  useActionHandler,
28956
- bt as useActiveAudioPlayer,
28957
- bD as useActiveThread,
29100
+ bu as useActiveAudioPlayer,
29101
+ bE as useActiveThread,
28958
29102
  useAttachmentManagerState,
28959
29103
  useAttachmentsForPreview,
28960
29104
  useAudioPlayer,
@@ -28962,12 +29106,12 @@ export {
28962
29106
  useCanCreatePoll,
28963
29107
  useChannelActionContext,
28964
29108
  useChannelDisplayName,
28965
- by as useChannelEditMessageHandler,
29109
+ bz as useChannelEditMessageHandler,
28966
29110
  useChannelListContext,
28967
29111
  useChannelListItemContext,
28968
29112
  useChannelMembersState,
28969
29113
  useChannelMembershipState,
28970
- bz as useChannelMentionsHandler,
29114
+ bA as useChannelMentionsHandler,
28971
29115
  useChannelPreviewInfo,
28972
29116
  useChannelStateContext,
28973
29117
  useChat,
@@ -28989,7 +29133,7 @@ export {
28989
29133
  useFlagHandler,
28990
29134
  useGalleryContext,
28991
29135
  useLastReadData,
28992
- b$ as useLegacyThreadContext,
29136
+ c0 as useLegacyThreadContext,
28993
29137
  useLiveLocationSharingManager,
28994
29138
  useMarkUnreadHandler,
28995
29139
  useMentionsHandler,
@@ -29042,6 +29186,6 @@ export {
29042
29186
  useTypingContext,
29043
29187
  useUserHandler,
29044
29188
  useUserRole,
29045
- bO as validateAndGetMessage
29189
+ bP as validateAndGetMessage
29046
29190
  };
29047
29191
  //# sourceMappingURL=index.mjs.map