stream-chat-react 14.9.0 → 14.11.0

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 (171) hide show
  1. package/README.md +20 -1
  2. package/dist/cjs/{useNotificationApi.bf76a16b.js → NotificationList.6abbee1c.js} +4935 -1232
  3. package/dist/cjs/NotificationList.6abbee1c.js.map +1 -0
  4. package/dist/cjs/{ReactPlayerWrapper.60a93742.js → ReactPlayerWrapper.a94a0cd1.js} +3 -3
  5. package/dist/cjs/{ReactPlayerWrapper.60a93742.js.map → ReactPlayerWrapper.a94a0cd1.js.map} +1 -1
  6. package/dist/cjs/channel-detail.js +288 -217
  7. package/dist/cjs/channel-detail.js.map +1 -1
  8. package/dist/cjs/emojis.js +31 -15
  9. package/dist/cjs/emojis.js.map +1 -1
  10. package/dist/cjs/index.js +2114 -7670
  11. package/dist/cjs/index.js.map +1 -1
  12. package/dist/cjs/useChannelHeaderOnlineStatus.7e17671e.js +961 -0
  13. package/dist/cjs/useChannelHeaderOnlineStatus.7e17671e.js.map +1 -0
  14. package/dist/cjs/useInertWhenHidden.22151beb.js +6426 -0
  15. package/dist/cjs/useInertWhenHidden.22151beb.js.map +1 -0
  16. package/dist/es/{useNotificationApi.ed6bff88.mjs → NotificationList.db01a56d.mjs} +4556 -1258
  17. package/dist/es/NotificationList.db01a56d.mjs.map +1 -0
  18. package/dist/es/channel-detail.mjs +115 -44
  19. package/dist/es/channel-detail.mjs.map +1 -1
  20. package/dist/es/emojis.mjs +25 -8
  21. package/dist/es/emojis.mjs.map +1 -1
  22. package/dist/es/index.mjs +1056 -6612
  23. package/dist/es/index.mjs.map +1 -1
  24. package/dist/es/useChannelHeaderOnlineStatus.a9357343.mjs +771 -0
  25. package/dist/es/useChannelHeaderOnlineStatus.a9357343.mjs.map +1 -0
  26. package/dist/es/useInertWhenHidden.3d5fbdba.mjs +5765 -0
  27. package/dist/es/useInertWhenHidden.3d5fbdba.mjs.map +1 -0
  28. package/dist/types/components/Attachment/Geolocation.d.ts.map +1 -1
  29. package/dist/types/components/Attachment/LinkPreview/Card.d.ts.map +1 -1
  30. package/dist/types/components/Attachment/LinkPreview/CardAudio.d.ts.map +1 -1
  31. package/dist/types/components/Attachment/ModalGallery.d.ts.map +1 -1
  32. package/dist/types/components/Attachment/UnsupportedAttachment.d.ts.map +1 -1
  33. package/dist/types/components/Attachment/VisibilityDisclaimer.d.ts.map +1 -1
  34. package/dist/types/components/Attachment/components/DownloadButton.d.ts.map +1 -1
  35. package/dist/types/components/Avatar/Avatar.d.ts +12 -3
  36. package/dist/types/components/Avatar/Avatar.d.ts.map +1 -1
  37. package/dist/types/components/Avatar/utils.d.ts +3 -0
  38. package/dist/types/components/Avatar/utils.d.ts.map +1 -0
  39. package/dist/types/components/Badge/Badge.d.ts.map +1 -1
  40. package/dist/types/components/Badge/MediaBadge.d.ts.map +1 -1
  41. package/dist/types/components/BaseImage/ImagePlaceholder.d.ts.map +1 -1
  42. package/dist/types/components/Button/PlayButton.d.ts.map +1 -1
  43. package/dist/types/components/Channel/Channel.d.ts.map +1 -1
  44. package/dist/types/components/ChannelListItem/ChannelListItem.d.ts.map +1 -1
  45. package/dist/types/components/ChannelListItem/ChannelListItemActionButtons.defaults.d.ts.map +1 -1
  46. package/dist/types/components/ChannelListItem/ChannelListItemUI.d.ts.map +1 -1
  47. package/dist/types/components/ChannelListItem/hooks/useMessageDeliveryStatus.d.ts.map +1 -1
  48. package/dist/types/components/ChannelListItem/utils.d.ts.map +1 -1
  49. package/dist/types/components/ChatView/ChatView.d.ts.map +1 -1
  50. package/dist/types/components/Dialog/components/Callout.d.ts.map +1 -1
  51. package/dist/types/components/Dialog/components/ContextMenu.d.ts +4 -0
  52. package/dist/types/components/Dialog/components/ContextMenu.d.ts.map +1 -1
  53. package/dist/types/components/Dialog/components/Prompt.d.ts.map +1 -1
  54. package/dist/types/components/Dialog/components/Viewer.d.ts.map +1 -1
  55. package/dist/types/components/EmptyStateIndicator/EmptyStateIndicator.d.ts.map +1 -1
  56. package/dist/types/components/Form/NumericInput.d.ts.map +1 -1
  57. package/dist/types/components/Form/TextInput.d.ts.map +1 -1
  58. package/dist/types/components/Gallery/GalleryHeader.d.ts.map +1 -1
  59. package/dist/types/components/Gallery/GalleryUI.d.ts.map +1 -1
  60. package/dist/types/components/Icons/index.d.ts +1 -0
  61. package/dist/types/components/Icons/index.d.ts.map +1 -1
  62. package/dist/types/components/Icons/slots.d.ts +10 -0
  63. package/dist/types/components/Icons/slots.d.ts.map +1 -0
  64. package/dist/types/components/Loading/LoadingIndicator.d.ts +2 -2
  65. package/dist/types/components/Loading/LoadingIndicator.d.ts.map +1 -1
  66. package/dist/types/components/Location/ShareLocationDialog.d.ts.map +1 -1
  67. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.d.ts.map +1 -1
  68. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.d.ts.map +1 -1
  69. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingPlayback.d.ts.map +1 -1
  70. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingPreview.d.ts.map +1 -1
  71. package/dist/types/components/Message/MessageAlsoSentInChannelIndicator.d.ts.map +1 -1
  72. package/dist/types/components/Message/MessageDeletedBubble.d.ts.map +1 -1
  73. package/dist/types/components/Message/MessageRepliesCountButton.d.ts.map +1 -1
  74. package/dist/types/components/Message/MessageStatus.d.ts.map +1 -1
  75. package/dist/types/components/Message/MessageTranslationIndicator.d.ts.map +1 -1
  76. package/dist/types/components/Message/MessageUI.d.ts.map +1 -1
  77. package/dist/types/components/Message/PinIndicator.d.ts.map +1 -1
  78. package/dist/types/components/Message/ReminderNotification.d.ts.map +1 -1
  79. package/dist/types/components/MessageActions/DownloadSubmenu.d.ts.map +1 -1
  80. package/dist/types/components/MessageActions/MessageActions.defaults.d.ts.map +1 -1
  81. package/dist/types/components/MessageActions/RemindMeSubmenu.d.ts.map +1 -1
  82. package/dist/types/components/MessageBounce/MessageBouncePrompt.d.ts.map +1 -1
  83. package/dist/types/components/MessageComposer/AttachmentPreviewList/AudioAttachmentPreview.d.ts.map +1 -1
  84. package/dist/types/components/MessageComposer/AttachmentPreviewList/FileAttachmentPreview.d.ts.map +1 -1
  85. package/dist/types/components/MessageComposer/AttachmentPreviewList/GeolocationPreview.d.ts.map +1 -1
  86. package/dist/types/components/MessageComposer/AttachmentPreviewList/MediaAttachmentPreview.d.ts.map +1 -1
  87. package/dist/types/components/MessageComposer/AttachmentPreviewList/UnsupportedAttachmentPreview.d.ts.map +1 -1
  88. package/dist/types/components/MessageComposer/AttachmentSelector/AttachmentSelector.d.ts.map +1 -1
  89. package/dist/types/components/MessageComposer/AttachmentSelector/CommandsMenu.d.ts.map +1 -1
  90. package/dist/types/components/MessageComposer/CommandChip.d.ts.map +1 -1
  91. package/dist/types/components/MessageComposer/LinkPreviewList.d.ts.map +1 -1
  92. package/dist/types/components/MessageComposer/MessageComposer.d.ts +10 -1
  93. package/dist/types/components/MessageComposer/MessageComposer.d.ts.map +1 -1
  94. package/dist/types/components/MessageComposer/MessageComposerActions.d.ts.map +1 -1
  95. package/dist/types/components/MessageComposer/QuotedMessagePreview.d.ts.map +1 -1
  96. package/dist/types/components/MessageComposer/RemoveAttachmentPreviewButton.d.ts.map +1 -1
  97. package/dist/types/components/MessageComposer/SendButton.d.ts.map +1 -1
  98. package/dist/types/components/MessageComposer/SendToChannelCheckbox.d.ts.map +1 -1
  99. package/dist/types/components/MessageComposer/WithDragAndDropUpload.d.ts.map +1 -1
  100. package/dist/types/components/MessageComposer/hooks/useMessageComposerCommands.d.ts.map +1 -1
  101. package/dist/types/components/MessageComposer/hooks/useMessageComposerController.d.ts.map +1 -1
  102. package/dist/types/components/MessageList/ScrollToLatestMessageButton.d.ts.map +1 -1
  103. package/dist/types/components/MessageList/UnreadMessagesNotification.d.ts.map +1 -1
  104. package/dist/types/components/MessageList/UnreadMessagesSeparator.d.ts.map +1 -1
  105. package/dist/types/components/MessageList/hooks/useMarkRead.d.ts.map +1 -1
  106. package/dist/types/components/Modal/CloseButtonOnModalOverlay.d.ts.map +1 -1
  107. package/dist/types/components/Notifications/Notification.d.ts.map +1 -1
  108. package/dist/types/components/Poll/PollActions/PollResults/PollOptionWithVotesHeader.d.ts.map +1 -1
  109. package/dist/types/components/Poll/PollCreationDialog/OptionFieldSet.d.ts.map +1 -1
  110. package/dist/types/components/Poll/PollCreationDialog/PollCreationDialogControls.d.ts.map +1 -1
  111. package/dist/types/components/Poll/PollCreationDialog/PollOptionReorderHandle.d.ts.map +1 -1
  112. package/dist/types/components/Poll/PollOptionSelector.d.ts.map +1 -1
  113. package/dist/types/components/Poll/PollVote.d.ts.map +1 -1
  114. package/dist/types/components/Reactions/MessageReactionsDetail.d.ts.map +1 -1
  115. package/dist/types/components/Reactions/ReactionSelector.d.ts.map +1 -1
  116. package/dist/types/components/Reactions/ReactionSelectorWithButton.d.ts +6 -1
  117. package/dist/types/components/Reactions/ReactionSelectorWithButton.d.ts.map +1 -1
  118. package/dist/types/components/Search/SearchResults/SearchResultItem.d.ts.map +1 -1
  119. package/dist/types/components/SummarizedMessagePreview/SummarizedMessagePreview.d.ts.map +1 -1
  120. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/BroadcastMentionItem.d.ts.map +1 -1
  121. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/RoleItem.d.ts.map +1 -1
  122. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/UserGroupItem.d.ts.map +1 -1
  123. package/dist/types/components/TextareaComposer/SuggestionList/MentionItem/UserItem.d.ts.map +1 -1
  124. package/dist/types/components/Thread/ThreadHeader.d.ts.map +1 -1
  125. package/dist/types/components/Threads/ThreadList/ThreadListEmptyPlaceholder.d.ts.map +1 -1
  126. package/dist/types/components/Threads/ThreadList/ThreadListItemUI.d.ts.map +1 -1
  127. package/dist/types/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.d.ts.map +1 -1
  128. package/dist/types/components/TypingIndicator/TypingIndicator.d.ts.map +1 -1
  129. package/dist/types/components/TypingIndicator/hooks/useDebouncedTypingActive.d.ts +2 -8
  130. package/dist/types/components/TypingIndicator/hooks/useDebouncedTypingActive.d.ts.map +1 -1
  131. package/dist/types/components/VideoPlayer/VideoThumbnail.d.ts.map +1 -1
  132. package/dist/types/context/ComponentContext.d.ts +7 -0
  133. package/dist/types/context/ComponentContext.d.ts.map +1 -1
  134. package/dist/types/context/WithComponents.d.ts.map +1 -1
  135. package/dist/types/context/index.d.ts +1 -0
  136. package/dist/types/context/index.d.ts.map +1 -1
  137. package/dist/types/context/useComponentContextIcons.d.ts +16 -0
  138. package/dist/types/context/useComponentContextIcons.d.ts.map +1 -0
  139. package/dist/types/plugins/ChannelDetail/ChannelDetail.d.ts.map +1 -1
  140. package/dist/types/plugins/ChannelDetail/ChannelDetailEmptyList.d.ts.map +1 -1
  141. package/dist/types/plugins/ChannelDetail/ChannelDetailSearchInput.d.ts.map +1 -1
  142. package/dist/types/plugins/ChannelDetail/SectionNavigator/SectionNavigatorHeader.d.ts.map +1 -1
  143. package/dist/types/plugins/ChannelDetail/Views/ChannelFilesView/ChannelFilesEmptyList.d.ts.map +1 -1
  144. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementActions.defaults.d.ts.map +1 -1
  145. package/dist/types/plugins/ChannelDetail/Views/ChannelManagementView/ChannelManagementView.d.ts.map +1 -1
  146. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaEmptyList.d.ts.map +1 -1
  147. package/dist/types/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.d.ts.map +1 -1
  148. package/dist/types/plugins/ChannelDetail/Views/ChannelMemberDetailView/ChannelMemberActions.defaults.d.ts.map +1 -1
  149. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersAddView.d.ts.map +1 -1
  150. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.d.ts.map +1 -1
  151. package/dist/types/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersHeaderActions.defaults.d.ts.map +1 -1
  152. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesEmptyList.d.ts.map +1 -1
  153. package/dist/types/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesView.d.ts.map +1 -1
  154. package/dist/types/plugins/Emojis/EmojiPicker.d.ts +6 -1
  155. package/dist/types/plugins/Emojis/EmojiPicker.d.ts.map +1 -1
  156. package/dist/types/plugins/Emojis/Picker.d.ts +7 -0
  157. package/dist/types/plugins/Emojis/Picker.d.ts.map +1 -0
  158. package/dist/types/utils/getChannelConfig.d.ts +8 -0
  159. package/dist/types/utils/getChannelConfig.d.ts.map +1 -0
  160. package/package.json +3 -9
  161. package/AI.md +0 -423
  162. package/dist/cjs/useChannelHeaderOnlineStatus.72753090.js +0 -4469
  163. package/dist/cjs/useChannelHeaderOnlineStatus.72753090.js.map +0 -1
  164. package/dist/cjs/useMessageComposerController.f8f20e3d.js +0 -99
  165. package/dist/cjs/useMessageComposerController.f8f20e3d.js.map +0 -1
  166. package/dist/cjs/useNotificationApi.bf76a16b.js.map +0 -1
  167. package/dist/es/useChannelHeaderOnlineStatus.c86db868.mjs +0 -3889
  168. package/dist/es/useChannelHeaderOnlineStatus.c86db868.mjs.map +0 -1
  169. package/dist/es/useMessageComposerController.d50f6168.mjs +0 -69
  170. package/dist/es/useMessageComposerController.d50f6168.mjs.map +0 -1
  171. package/dist/es/useNotificationApi.ed6bff88.mjs.map +0 -1
@@ -0,0 +1,771 @@
1
+ import { Lt as useAriaIdentifiers, Nt as Avatar, Qn as useModalContext, Sr as useComponentContextIcons, Vn as useTranslationContext, Xt as isMessageDeleted, ca as useChatContext, da as useChannelStateContext, mt as extractDisplayInfo } from "./NotificationList.db01a56d.mjs";
2
+ import React, { createContext, forwardRef, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
4
+ import clsx from "clsx";
5
+ import { useSyncExternalStore } from "use-sync-external-store/shim";
6
+ import { SKIP, visit } from "unist-util-visit";
7
+ import ReactMarkdown from "react-markdown";
8
+ import remarkGfm from "remark-gfm";
9
+ import { toString } from "mdast-util-to-string";
10
+ import remarkParse from "remark-parse";
11
+ import { unified } from "unified";
12
+ //#region src/context/MessageTranslationViewContext.tsx
13
+ /**
14
+ * Message translation view context: user-specific state for whether each message
15
+ * shows original text or a translation.
16
+ *
17
+ * ## Spec
18
+ *
19
+ * - **State**: Per message list (channel vs thread), we store a map
20
+ * `messageId → 'original' | 'translated'`. Default for messages with `message.i18n`
21
+ * is `'translated'`; otherwise `'original'`.
22
+ *
23
+ * - **Provider placement**: The provider is tied to the **message list**, not the channel.
24
+ * It is rendered inside `MessageList` and `VirtualizedMessageList`. That way the
25
+ * main channel list and the thread list each have their own translation view state
26
+ * (e.g. Thread.tsx gets correct behavior without sharing channel state).
27
+ *
28
+ * - **Multiple translations**: `message.i18n` can contain multiple languages, e.g.:
29
+ * `{ en_text: "Good morning", fr_text: "Bonjour", it_text: "Buongiorno", language: "en" }`.
30
+ * Which translation is shown is determined by the app’s **user language**
31
+ * (`useTranslationContext().userLanguage`). We use `message.i18n[userLanguage + '_text']`;
32
+ * if missing, we fall back to `message.text`. Only one translation is shown at a time.
33
+ *
34
+ * - **Source language**: When present, `message.i18n.language` is the original/source
35
+ * language of `message.text`. It can be used for the indicator label, e.g.
36
+ * "Translated from English · View original".
37
+ *
38
+ * - **Invariants**: The original message content, layout, grouping, and order stay
39
+ * unchanged. Removing or toggling translation only changes the annotation and which
40
+ * text is displayed. "View original" / "View translation" switch the displayed
41
+ * text and update the annotation (e.g. "Original · View translation" when showing
42
+ * original).
43
+ *
44
+ * - **Translation indicator visibility**: The translation indicator (e.g. "Translated ·
45
+ * View original") is **not** shown when the currently viewed text already corresponds
46
+ * to `userLanguage` — for example when viewing original and the original text is the
47
+ * user-language version (i.e. `getTranslatedMessageText({ language: userLanguage, message })`
48
+ * equals `message.text`). In that case there is no meaningful original/translated choice,
49
+ * so the indicator is hidden.
50
+ */
51
+ /**
52
+ * Returns the translated message text for a given language from `message.i18n`, or
53
+ * undefined if not present. Used to resolve which of the multiple translations to show.
54
+ */
55
+ var getTranslatedMessageText = ({ language, message }) => message?.i18n?.[`${language}_text`];
56
+ var defaultContextValue = {
57
+ getTranslationView: (_messageId, hasI18n) => hasI18n ? "translated" : "original",
58
+ setTranslationView: () => {}
59
+ };
60
+ var MessageTranslationViewContext = createContext(defaultContextValue);
61
+ var MessageTranslationViewProvider = ({ children }) => {
62
+ const [viewByMessageId, setViewByMessageId] = useState({});
63
+ const setTranslationView = useCallback((messageId, view) => {
64
+ setViewByMessageId((prev) => ({
65
+ ...prev,
66
+ [messageId]: view
67
+ }));
68
+ }, []);
69
+ const getTranslationView = useCallback((messageId, hasI18n) => viewByMessageId[messageId] ?? (hasI18n ? "translated" : "original"), [viewByMessageId]);
70
+ const stableValue = React.useMemo(() => ({
71
+ getTranslationView,
72
+ setTranslationView
73
+ }), [getTranslationView, setTranslationView]);
74
+ return /* @__PURE__ */ jsx(MessageTranslationViewContext.Provider, {
75
+ value: stableValue,
76
+ children
77
+ });
78
+ };
79
+ var useMessageTranslationViewContext = () => {
80
+ return useContext(MessageTranslationViewContext) ?? defaultContextValue;
81
+ };
82
+ //#endregion
83
+ //#region src/utils/useStableCallback.ts
84
+ /**
85
+ * A utility hook implementing a stable callback. It takes in an unstable method that
86
+ * is supposed to be invoked somewhere deeper in the DOM tree without making it
87
+ * change its reference every time the parent component rerenders. It will also return
88
+ * the value of the callback if it does return one.
89
+ * A common use-case would be having a function whose invocation depends on state
90
+ * somewhere high up in the DOM tree and wanting to use the same function deeper
91
+ * down, for example in a leaf node and simply using useCallback results in
92
+ * cascading dependency hell. If we wrap it in useStableCallback, we would be able
93
+ * to:
94
+ * - Use the same function as a dependency of another hook (since it is stable)
95
+ * - Still invoke it and get the latest state
96
+ *
97
+ * **Caveats:**
98
+ * - Never wrap a function that is supposed to return a React.ReactElement in
99
+ * useStableCallback, since React will not know that the DOM needs to be updated
100
+ * whenever the callback value changes (for example, renderItem from FlatList must
101
+ * never be wrapped in this hook)
102
+ * - Always prefer using a standard useCallback/stable function wherever possible
103
+ * (the purpose of useStableCallback is to bridge the gap between top level contexts
104
+ * and cascading rereders in downstream components - **not** as an escape hatch)
105
+ * @param callback - the callback we want to stabilize
106
+ */
107
+ var useStableCallback = (callback) => {
108
+ const ref = useRef(callback);
109
+ ref.current = callback;
110
+ return useCallback((...args) => ref.current(...args), []);
111
+ };
112
+ //#endregion
113
+ //#region src/components/Dialog/components/Alert.tsx
114
+ var Root = forwardRef(function AlertRoot({ children, className, ...props }, ref) {
115
+ return /* @__PURE__ */ jsx("div", {
116
+ ...props,
117
+ className: clsx("str-chat__alert-root", className),
118
+ ref,
119
+ children
120
+ });
121
+ });
122
+ var Header = forwardRef(function AlertHeader({ children, className, description, descriptionId, Icon, title, titleId, ...props }, ref) {
123
+ const { dialogId } = useModalContext();
124
+ const { descriptionId: derivedDescriptionId, titleId: derivedTitleId } = useAriaIdentifiers(dialogId);
125
+ const resolvedTitleId = titleId ?? derivedTitleId;
126
+ const resolvedDescriptionId = descriptionId ?? derivedDescriptionId;
127
+ return /* @__PURE__ */ jsx("div", {
128
+ ...props,
129
+ className: clsx("str-chat__alert-header", className),
130
+ ref,
131
+ children: title ? /* @__PURE__ */ jsxs(Fragment$1, { children: [Icon && /* @__PURE__ */ jsx(Icon, {}), /* @__PURE__ */ jsxs("div", {
132
+ className: "str-chat__alert-header__copy",
133
+ children: [/* @__PURE__ */ jsx("h2", {
134
+ className: "str-chat__alert-header__title",
135
+ id: resolvedTitleId,
136
+ children: title
137
+ }), description && /* @__PURE__ */ jsx("p", {
138
+ className: "str-chat__alert-header__description",
139
+ id: resolvedDescriptionId,
140
+ children: description
141
+ })]
142
+ })] }) : children
143
+ });
144
+ });
145
+ var Alert = {
146
+ Actions: forwardRef(function AlertActions({ children, className, ...props }, ref) {
147
+ return /* @__PURE__ */ jsx("div", {
148
+ ...props,
149
+ className: clsx("str-chat__alert-actions", className),
150
+ ref,
151
+ children
152
+ });
153
+ }),
154
+ Header,
155
+ Root
156
+ };
157
+ //#endregion
158
+ //#region src/components/Badge/Badge.tsx
159
+ /**
160
+ * Compact pill/circle badge for counts and labels.
161
+ * Uses design tokens: --badge-bg-*, --badge-text-*, --badge-border.
162
+ */
163
+ var Badge = ({ children, className, size = "md", variant = "default", ...spanProps }) => /* @__PURE__ */ jsx("div", {
164
+ ...spanProps,
165
+ className: clsx("str-chat__badge", `str-chat__badge--variant-${variant}`, { [`str-chat__badge--size-${size}`]: size }, className),
166
+ children
167
+ });
168
+ var ErrorBadge = ({ className, size = "sm", ...rest }) => {
169
+ const { IconExclamationMarkFill } = useComponentContextIcons();
170
+ return /* @__PURE__ */ jsx(Badge, {
171
+ ...rest,
172
+ className,
173
+ size,
174
+ variant: "error",
175
+ children: /* @__PURE__ */ jsx(IconExclamationMarkFill, {})
176
+ });
177
+ };
178
+ //#endregion
179
+ //#region src/components/Avatar/ChannelAvatar.tsx
180
+ var ChannelAvatar = ({ displayMembers, imageUrl, size, userName, ...sharedProps }) => {
181
+ return /* @__PURE__ */ jsx(GroupAvatar, {
182
+ displayMembers: useMemo(() => {
183
+ if (!imageUrl && displayMembers && displayMembers.length > 0) return displayMembers;
184
+ return [{
185
+ imageUrl,
186
+ userName
187
+ }];
188
+ }, [
189
+ displayMembers,
190
+ imageUrl,
191
+ userName
192
+ ]),
193
+ size,
194
+ ...sharedProps
195
+ });
196
+ };
197
+ //#endregion
198
+ //#region src/components/Avatar/GroupAvatar.tsx
199
+ /**
200
+ * Avatar component to display multiple users' avatars in a group.
201
+ * Renders a single Avatar if fewer than 2 members. Otherwise, renders up to 2 avatars (when overflowCount is set) or 4, plus an optional +N badge.
202
+ */
203
+ var GroupAvatar = ({ badgeSize, className, displayMembers = [], isOnline, size, ...rest }) => {
204
+ const displayMembersToRender = useMemo(() => displayMembers.length > 4 ? displayMembers.slice(0, 2) : displayMembers, [displayMembers]);
205
+ const overflowCount = displayMembers.length - displayMembersToRender.length;
206
+ if (displayMembers.length < 2) {
207
+ const firstUser = displayMembers[0];
208
+ return /* @__PURE__ */ jsx(Avatar, {
209
+ imageUrl: firstUser?.imageUrl,
210
+ isOnline,
211
+ size,
212
+ userName: firstUser?.userName,
213
+ ...rest
214
+ });
215
+ }
216
+ let avatarSize = null;
217
+ if (size === "2xl") avatarSize = "lg";
218
+ else if (size === "xl") avatarSize = "md";
219
+ else if (size === "lg") avatarSize = "sm";
220
+ return /* @__PURE__ */ jsxs("div", {
221
+ className: clsx("str-chat__avatar-group", {
222
+ "str-chat__avatar-group--offline": typeof isOnline === "boolean" && !isOnline,
223
+ "str-chat__avatar-group--online": typeof isOnline === "boolean" && isOnline,
224
+ [`str-chat__avatar-group--size-${size}`]: typeof size === "string"
225
+ }, className),
226
+ "data-testid": "group-avatar",
227
+ role: "button",
228
+ ...rest,
229
+ children: [displayMembersToRender.map(({ id, imageUrl, userName }, index) => /* @__PURE__ */ jsx(Avatar, {
230
+ imageUrl,
231
+ size: avatarSize,
232
+ userName
233
+ }, id || `${userName}-${imageUrl}-${index}`)), typeof overflowCount === "number" && overflowCount > 0 && /* @__PURE__ */ jsxs(Badge, {
234
+ className: "str-chat__avatar-group__count-badge",
235
+ "data-testid": "group-avatar-count-badge",
236
+ size: badgeSize,
237
+ variant: "counter",
238
+ children: ["+", overflowCount]
239
+ })]
240
+ });
241
+ };
242
+ //#endregion
243
+ //#region src/components/Message/renderText/remarkPlugins/htmlToTextPlugin.ts
244
+ var visitor = (node) => {
245
+ if (node.type !== "html") return;
246
+ node.type = "text";
247
+ };
248
+ var transform = (tree) => {
249
+ visit(tree, visitor);
250
+ };
251
+ var htmlToTextPlugin = () => transform;
252
+ //#endregion
253
+ //#region src/components/Message/renderText/remarkPlugins/imageToLink.ts
254
+ var text = (value) => ({
255
+ type: "text",
256
+ value
257
+ });
258
+ /**
259
+ * Converts image Markdown links (![Minion](https://octodex.github.com/images/minion.png))
260
+ * to HTML <a href={url}>{url | title | alt}</a>
261
+ *
262
+ * By default, the anchor text content is the image url so that image preview can be generated / enriched on the server.
263
+ * @param getTextLabelFrom
264
+ */
265
+ function imageToLink({ getTextLabelFrom = "url" } = {}) {
266
+ return (tree) => {
267
+ const visitor = (node, index, parent) => {
268
+ if (parent == null || index == null) return;
269
+ const link = {
270
+ children: [text(node[getTextLabelFrom] ?? node.url)],
271
+ title: node.title ?? node.alt ?? node.url,
272
+ type: "link",
273
+ url: node.url
274
+ };
275
+ parent.children.splice(index, 1, link);
276
+ return [SKIP, index + 1];
277
+ };
278
+ visit(tree, "image", visitor);
279
+ };
280
+ }
281
+ //#endregion
282
+ //#region src/components/Message/renderText/remarkPlugins/plusPlusToEmphasis.ts
283
+ /**
284
+ * \S → first char must be non-whitespace
285
+ * (?:...)?→ optional middle+closing when length > 1
286
+ * [\s\S]*?→ anything (including newlines), lazy
287
+ * final \S→ last char non-whitespace (only required when there’s more than 1)
288
+ *
289
+ * Matches:
290
+ * ++a++
291
+ * Does not match:
292
+ * ++++
293
+ * ++ ++
294
+ */
295
+ var INS_REGEX = /\+\+(\S(?:[\s\S]*?\S)?)\+\+/g;
296
+ var IGNORE_NODE_TYPES = /* @__PURE__ */ new Set([
297
+ "code",
298
+ "inlineCode",
299
+ "link",
300
+ "linkReference",
301
+ "definition",
302
+ "math",
303
+ "inlineMath"
304
+ ]);
305
+ /**
306
+ * Converts MD "++Some text++" to inserted text element rendered in HTML as <ins>Some text</ins>
307
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/ins
308
+ */
309
+ var plusPlusToEmphasis = () => {
310
+ const visitor = (node, index, parent) => {
311
+ if (IGNORE_NODE_TYPES.has(node.type)) return SKIP;
312
+ if (node.type !== "text" || parent == null || typeof index !== "number") return;
313
+ const value = node.value;
314
+ INS_REGEX.lastIndex = 0;
315
+ let match;
316
+ let last = 0;
317
+ const out = [];
318
+ while (match = INS_REGEX.exec(value)) {
319
+ const [full, inner] = match;
320
+ const start = match.index;
321
+ if (start > last) out.push({
322
+ type: "text",
323
+ value: value.slice(last, start)
324
+ });
325
+ out.push({
326
+ children: [{
327
+ type: "text",
328
+ value: inner
329
+ }],
330
+ data: { hName: "ins" },
331
+ type: "emphasis"
332
+ });
333
+ last = start + full.length;
334
+ }
335
+ if (out.length === 0) return;
336
+ if (last < value.length) out.push({
337
+ type: "text",
338
+ value: value.slice(last)
339
+ });
340
+ parent.children.splice(index, 1, ...out);
341
+ return [SKIP, index + out.length];
342
+ };
343
+ return (tree) => visit(tree, visitor);
344
+ };
345
+ //#endregion
346
+ //#region src/components/Form/Checkbox.tsx
347
+ var Checkbox = ({ checked, ...props }) => /* @__PURE__ */ jsx("div", {
348
+ ...props,
349
+ className: clsx("str-chat__checkmark str-chat__checkbox", {
350
+ "str-chat__checkbox--checked": checked,
351
+ "str-chat__checkmark--checked": checked
352
+ })
353
+ });
354
+ var Checkmark = Checkbox;
355
+ //#endregion
356
+ //#region src/components/ChannelListItem/utils.tsx
357
+ var remarkPlugins = [
358
+ htmlToTextPlugin,
359
+ [remarkGfm, { singleTilde: false }],
360
+ plusPlusToEmphasis,
361
+ imageToLink
362
+ ];
363
+ var renderPreviewText = (text) => /* @__PURE__ */ jsx(ReactMarkdown, {
364
+ remarkPlugins,
365
+ skipHtml: true,
366
+ children: text
367
+ });
368
+ var createPlainTextProcessor = () => unified().use(remarkParse).use(remarkPlugins);
369
+ var plainTextProcessor;
370
+ var stripMarkdownToText = (text) => {
371
+ try {
372
+ const processor = plainTextProcessor ?? (plainTextProcessor = createPlainTextProcessor());
373
+ return toString(processor.runSync(processor.parse(text))) || text;
374
+ } catch {
375
+ return text;
376
+ }
377
+ };
378
+ var getLatestPollVote = (latestVotesByOption) => {
379
+ let latestVote;
380
+ for (const optionVotes of Object.values(latestVotesByOption)) optionVotes.forEach((vote) => {
381
+ if (latestVote && new Date(latestVote.updated_at) >= new Date(vote.created_at)) return;
382
+ latestVote = vote;
383
+ });
384
+ return latestVote;
385
+ };
386
+ /**
387
+ * Resolves the channel's latest-message preview once: the localized DISPLAY string, a `kind`
388
+ * discriminant, and the bits announcements need (poll question, attachment type). Single source of
389
+ * truth for the display variant ({@link getLatestMessagePreview}) and the announcement variant
390
+ * ({@link getLatestMessagePreviewText}), so the translation lookup and branching run once per call.
391
+ */
392
+ var getLatestMessagePreviewParts = (channel, t, userLanguage = "en", latestMessageArg) => {
393
+ const latestMessage = latestMessageArg ?? channel.state.latestMessages[channel.state.latestMessages.length - 1];
394
+ const previewTextToRender = getTranslatedMessageText({
395
+ language: userLanguage,
396
+ message: latestMessage
397
+ }) || latestMessage?.text;
398
+ const poll = latestMessage?.poll;
399
+ if (!latestMessage) return {
400
+ isUserMessageText: false,
401
+ kind: "empty",
402
+ latestMessage,
403
+ text: t("Nothing yet...")
404
+ };
405
+ if (isMessageDeleted(latestMessage)) return {
406
+ isUserMessageText: false,
407
+ kind: "deleted",
408
+ latestMessage,
409
+ text: t("Message deleted")
410
+ };
411
+ if (poll) if (!poll.vote_count) {
412
+ const createdBy = poll.created_by?.id === channel.getClient().userID ? t("You") : poll.created_by?.name ?? t("Poll");
413
+ return {
414
+ isUserMessageText: false,
415
+ kind: "poll",
416
+ latestMessage,
417
+ pollName: poll.name,
418
+ text: t("📊 {{createdBy}} created: {{ pollName}}", {
419
+ createdBy,
420
+ pollName: poll.name
421
+ })
422
+ };
423
+ } else {
424
+ const latestVote = getLatestPollVote(poll.latest_votes_by_option);
425
+ const option = latestVote && poll.options.find((opt) => opt.id === latestVote.option_id);
426
+ if (option && latestVote) return {
427
+ isUserMessageText: false,
428
+ kind: "poll",
429
+ latestMessage,
430
+ pollName: poll.name,
431
+ text: t("📊 {{votedBy}} voted: {{pollOptionText}}", {
432
+ pollOptionText: option.text,
433
+ votedBy: latestVote?.user?.id === channel.getClient().userID ? t("You") : latestVote.user?.name ?? t("Poll")
434
+ })
435
+ };
436
+ }
437
+ if (previewTextToRender) return {
438
+ isUserMessageText: true,
439
+ kind: "text",
440
+ latestMessage,
441
+ text: previewTextToRender
442
+ };
443
+ if (latestMessage.command) return {
444
+ isUserMessageText: false,
445
+ kind: "command",
446
+ latestMessage,
447
+ text: `/${latestMessage.command}`
448
+ };
449
+ if (latestMessage.attachments?.length) return {
450
+ isUserMessageText: false,
451
+ kind: "attachment",
452
+ latestMessage,
453
+ text: t("🏙 Attachment...")
454
+ };
455
+ if (latestMessage.shared_location) return {
456
+ isUserMessageText: false,
457
+ kind: "location",
458
+ latestMessage,
459
+ text: t("📍Shared location")
460
+ };
461
+ return {
462
+ isUserMessageText: false,
463
+ kind: "text",
464
+ latestMessage,
465
+ text: t("Empty message...")
466
+ };
467
+ };
468
+ /**
469
+ * Maps a known attachment `type` to a localized, human-readable word (e.g. "image" → "Image"). The
470
+ * cases are literal `t('aria/…')` calls so `i18next-cli` extracts them. Unknown/custom types return
471
+ * `undefined`, so the announcement falls back to a generic "Attachment".
472
+ */
473
+ var getAttachmentTypeLabel = (type, t) => {
474
+ switch (type) {
475
+ case "audio": return t("aria/audio");
476
+ case "file": return t("aria/file");
477
+ case "giphy": return t("aria/GIF");
478
+ case "image": return t("aria/image");
479
+ case "video": return t("aria/video");
480
+ case "voiceRecording": return t("aria/voice message");
481
+ default: return;
482
+ }
483
+ };
484
+ /**
485
+ * The text announced for the channel's latest message (for an accessible name / live region).
486
+ * Non-text content gets a concise, screen-reader-friendly phrasing that may differ from the visible
487
+ * preview: a poll announces its question, an attachment its type, a shared location and a deleted
488
+ * message a plain phrase. User-message text is stripped of markdown so the words are read, not the
489
+ * syntax (AI-generated text is returned verbatim, matching the display path).
490
+ */
491
+ var getLatestMessagePreviewText = (channel, t, userLanguage = "en", isMessageAIGenerated, latestMessage) => {
492
+ const { isUserMessageText, kind, latestMessage: resolvedLatestMessage, pollName, text } = getLatestMessagePreviewParts(channel, t, userLanguage, latestMessage);
493
+ switch (kind) {
494
+ case "poll": return t("aria/Poll: {{ pollName }}", { pollName: pollName ?? "" });
495
+ case "attachment": {
496
+ const realAttachments = resolvedLatestMessage?.attachments?.filter((attachment) => !attachment.og_scrape_url) ?? [];
497
+ if (realAttachments.length > 1) return t("aria/Message with attachments");
498
+ const typeLabel = getAttachmentTypeLabel(realAttachments[0]?.type, t);
499
+ return typeLabel ? t("aria/Attachment {{ attachmentType }}", { attachmentType: typeLabel }) : t("aria/Attachment");
500
+ }
501
+ case "location": return t("aria/Shared location");
502
+ case "empty": return t("aria/There are no messages in this chat.");
503
+ case "text": return isUserMessageText && resolvedLatestMessage && !isMessageAIGenerated?.(resolvedLatestMessage) ? stripMarkdownToText(text) : text;
504
+ default: return text;
505
+ }
506
+ };
507
+ var getLatestMessagePreview = (channel, t, userLanguage = "en", isMessageAIGenerated, latestMessage) => {
508
+ const { isUserMessageText, latestMessage: resolvedLatestMessage, text } = getLatestMessagePreviewParts(channel, t, userLanguage, latestMessage);
509
+ return isUserMessageText && resolvedLatestMessage && !isMessageAIGenerated?.(resolvedLatestMessage) ? renderPreviewText(text) : text;
510
+ };
511
+ /**
512
+ * Channel display image: channel.data.image, or for DM (2 members) the other member's user.image.
513
+ */
514
+ var getChannelDisplayImage = (channel, currentUserId) => {
515
+ const data = channel.data;
516
+ if (data?.image && typeof data.image === "string") return data.image;
517
+ const memberList = Object.values(channel.state.members);
518
+ if (memberList.length === 2) {
519
+ const image = memberList.find((m) => m.user?.id !== currentUserId)?.user?.image;
520
+ if (image && typeof image === "string") return image;
521
+ }
522
+ };
523
+ var getGroupChannelDisplayInfo = (channel) => {
524
+ const members = Object.values(channel.state.members);
525
+ if (members.length <= 2) return;
526
+ const memberList = [];
527
+ for (const member of members) {
528
+ const { user } = member;
529
+ if (!user?.name && !user?.image) continue;
530
+ memberList.push(extractDisplayInfo(member));
531
+ }
532
+ return { members: memberList };
533
+ };
534
+ //#endregion
535
+ //#region src/components/ChannelListItem/hooks/useChannelDisplayName.ts
536
+ /**
537
+ * 1. channel.data.name
538
+ * 2. DM (exactly 2 members): other member's name, then directMessageLabel
539
+ * 3. Group (3+ members): comma-separated list of 2 other members' names (no ellipsis)
540
+ * 4. undefined otherwise
541
+ */
542
+ function computeChannelDisplayName(channel, directMessageLabel, currentUserId) {
543
+ const data = channel.data;
544
+ if (data?.name && typeof data.name === "string") return data.name;
545
+ const memberList = Object.values(channel.state.members);
546
+ const otherMembers = memberList.filter((m) => m.user?.id !== currentUserId);
547
+ if (memberList.length === 2 && otherMembers.length === 1) return otherMembers[0].user?.name || directMessageLabel;
548
+ if (otherMembers.length >= 2) {
549
+ const names = otherMembers.map((m) => m.user?.name).filter(Boolean).slice(0, 2);
550
+ if (names.length > 0) return names.join(", ");
551
+ }
552
+ }
553
+ /**
554
+ * Channel display name with translation context.
555
+ * 1. channel.data.name
556
+ * 2. DM (exactly 2 members): other member's name, then translated "Direct message"
557
+ * 3. Group (3+ members): comma-separated list of 2 other members' names (no ellipsis)
558
+ * 4. undefined otherwise
559
+ */
560
+ var useChannelDisplayName = (channel) => {
561
+ const { client } = useChatContext("useChannelDisplayName");
562
+ const { t } = useTranslationContext("useChannelDisplayName");
563
+ const directMessageLabel = t("Direct message");
564
+ const [displayName, setDisplayName] = useState(() => channel ? computeChannelDisplayName(channel, directMessageLabel, client.userID ?? void 0) : void 0);
565
+ useEffect(() => {
566
+ if (!channel) {
567
+ setDisplayName(void 0);
568
+ return;
569
+ }
570
+ const updateDisplayName = () => setDisplayName(computeChannelDisplayName(channel, directMessageLabel, client.userID ?? void 0));
571
+ updateDisplayName();
572
+ client.on("user.updated", updateDisplayName);
573
+ return () => {
574
+ client.off("user.updated", updateDisplayName);
575
+ };
576
+ }, [
577
+ channel,
578
+ channel?.data,
579
+ client,
580
+ directMessageLabel
581
+ ]);
582
+ return displayName;
583
+ };
584
+ //#endregion
585
+ //#region src/components/ChannelListItem/hooks/useChannelPreviewInfo.ts
586
+ var emptyGroupInfo = {
587
+ members: [],
588
+ overflowCount: void 0
589
+ };
590
+ var useChannelPreviewInfo = (props) => {
591
+ const { channel, overrideImage, overrideTitle } = props;
592
+ const { client } = useChatContext();
593
+ const channelDisplayName = useChannelDisplayName(channel);
594
+ const displayTitle = overrideTitle ?? channelDisplayName;
595
+ const [displayImage, setDisplayImage] = useState(() => channel ? overrideImage ?? getChannelDisplayImage(channel, client.userID ?? void 0) : void 0);
596
+ const [groupChannelDisplayInfo, setGroupChannelDisplayInfo] = useState(() => channel ? getGroupChannelDisplayInfo(channel) ?? emptyGroupInfo : emptyGroupInfo);
597
+ useEffect(() => {
598
+ if (!channel) return;
599
+ if (overrideImage) return;
600
+ const updateInfo = () => {
601
+ setDisplayImage(getChannelDisplayImage(channel, client.userID ?? void 0));
602
+ setGroupChannelDisplayInfo(getGroupChannelDisplayInfo(channel) ?? emptyGroupInfo);
603
+ };
604
+ updateInfo();
605
+ const { unsubscribe: unsubscribeChannelUpdated } = channel.on("channel.updated", updateInfo);
606
+ const { unsubscribe: unsubscribeUserUpdated } = client.on("user.updated", updateInfo);
607
+ return () => {
608
+ unsubscribeChannelUpdated();
609
+ unsubscribeUserUpdated();
610
+ };
611
+ }, [
612
+ channel,
613
+ channel?.data,
614
+ client,
615
+ overrideImage
616
+ ]);
617
+ return useMemo(() => ({
618
+ displayImage: overrideImage ?? displayImage,
619
+ displayTitle,
620
+ groupChannelDisplayInfo
621
+ }), [
622
+ displayImage,
623
+ displayTitle,
624
+ groupChannelDisplayInfo,
625
+ overrideImage
626
+ ]);
627
+ };
628
+ //#endregion
629
+ //#region src/utils/isDmChannel.ts
630
+ var isDmChannel = ({ channel, ownUserId }) => {
631
+ const memberCount = channel.data?.member_count ?? 0;
632
+ return memberCount === 1 || memberCount === 2 && !!ownUserId && Object.values(channel.state?.members ?? {}).some(({ user }) => user?.id === ownUserId);
633
+ };
634
+ //#endregion
635
+ //#region src/components/ChannelList/hooks/useSelectedChannelState.ts
636
+ var noop = () => {};
637
+ function useSelectedChannelState({ channel, selector, stateChangeEventKeys = ["all"] }) {
638
+ return useSyncExternalStore(useCallback((onStoreChange) => {
639
+ if (!channel) return noop;
640
+ const subscriptions = stateChangeEventKeys.map((et) => channel.on(et, () => {
641
+ onStoreChange(selector(channel));
642
+ }));
643
+ return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
644
+ }, [
645
+ channel,
646
+ selector,
647
+ stateChangeEventKeys
648
+ ]), useCallback(() => {
649
+ if (!channel) return void 0;
650
+ return selector(channel);
651
+ }, [channel, selector]));
652
+ }
653
+ //#endregion
654
+ //#region src/components/ChannelList/hooks/useChannelMembershipState.ts
655
+ var selector = (c) => c.state.membership;
656
+ var keys = ["member.updated"];
657
+ function useChannelMembershipState(channel) {
658
+ return useSelectedChannelState({
659
+ channel,
660
+ selector,
661
+ stateChangeEventKeys: keys
662
+ });
663
+ }
664
+ //#endregion
665
+ //#region src/components/ChannelListItem/hooks/useIsChannelMuted.ts
666
+ /**
667
+ * `channel.muteStatus()` throws in two cases:
668
+ * - the channel has not been initialized yet (i.e. `.watch()`/`.query()` has not
669
+ * resolved) - the ChannelList can briefly hold such channels, e.g. a
670
+ * `message.new` for a channel that has not been watched yet (#2474);
671
+ * - the channel is disconnected (e.g. after `client.disconnectUser()`), in which
672
+ * case `channel.getClient()` throws even though the channel stays initialized
673
+ * (#2393 failure class).
674
+ * Guard against both to avoid crashing the whole app.
675
+ */
676
+ var getMuteStatus = (channel) => channel.initialized && !channel.disconnected ? channel.muteStatus() : {
677
+ createdAt: null,
678
+ expiresAt: null,
679
+ muted: false
680
+ };
681
+ var useIsChannelMuted = (channel) => {
682
+ const { client } = useChatContext("useIsChannelMuted");
683
+ const [muted, setMuted] = useState(() => getMuteStatus(channel));
684
+ useEffect(() => {
685
+ const handleEvent = () => {
686
+ setMuted(getMuteStatus(channel));
687
+ };
688
+ return client.on("notification.channel_mutes_updated", handleEvent).unsubscribe;
689
+ }, [channel, client]);
690
+ return muted;
691
+ };
692
+ //#endregion
693
+ //#region src/components/ChannelListItem/hooks/useIsUserMuted.ts
694
+ var useIsUserMuted = (targetUserId) => {
695
+ const { mutes } = useChatContext();
696
+ return useMemo(() => !!targetUserId && mutes.some((mute) => mute.target.id === targetUserId), [mutes, targetUserId]);
697
+ };
698
+ //#endregion
699
+ //#region src/components/ChannelHeader/hooks/useChannelHasMembersOnline.ts
700
+ var getOtherWatchers = (watchers, ownUserId) => {
701
+ const next = Object.assign({}, watchers ?? {});
702
+ if (ownUserId) delete next[ownUserId];
703
+ return next;
704
+ };
705
+ var useChannelHasMembersOnline = ({ channel: channelOverride, enabled = true } = {}) => {
706
+ const { channel: contextChannel } = useChannelStateContext();
707
+ const { client } = useChatContext();
708
+ const channel = channelOverride ?? contextChannel;
709
+ const ownUserId = client.user?.id;
710
+ const [watchers, setWatchers] = useState(() => getOtherWatchers(channel?.state?.watchers, ownUserId));
711
+ useEffect(() => {
712
+ setWatchers(getOtherWatchers(channel?.state?.watchers, ownUserId));
713
+ }, [channel, ownUserId]);
714
+ useEffect(() => {
715
+ if (!enabled || !channel) return;
716
+ const startSubscription = channel.on("user.watching.start", (event) => {
717
+ setWatchers((prev) => {
718
+ if (!event.user?.id || event.user.id === ownUserId) return prev;
719
+ if (prev[event.user.id]) return prev;
720
+ return Object.assign({ [event.user.id]: event.user }, prev);
721
+ });
722
+ });
723
+ const stopSubscription = channel.on("user.watching.stop", (event) => {
724
+ setWatchers((prev) => {
725
+ if (!event.user?.id || !prev[event.user.id]) return prev;
726
+ const next = Object.assign({}, prev);
727
+ delete next[event.user.id];
728
+ return next;
729
+ });
730
+ });
731
+ return () => {
732
+ startSubscription.unsubscribe();
733
+ stopSubscription.unsubscribe();
734
+ };
735
+ }, [
736
+ channel,
737
+ enabled,
738
+ ownUserId
739
+ ]);
740
+ if (!enabled) return false;
741
+ return Object.keys(watchers).length > 0;
742
+ };
743
+ //#endregion
744
+ //#region src/components/ChannelHeader/hooks/useChannelHeaderOnlineStatus.ts
745
+ /**
746
+ * Returns the channel header online status text (e.g. "Online", "Offline", or "X members, Y online").
747
+ * Returns null when the channel has no members (nothing to show).
748
+ */
749
+ function useChannelHeaderOnlineStatus({ channel: channelOverride, watcherCount: watcherCountOverride } = {}) {
750
+ const { t } = useTranslationContext();
751
+ const { client } = useChatContext();
752
+ const { channel: contextChannel, watcherCount: contextWatcherCount = 0 } = useChannelStateContext();
753
+ const channel = channelOverride ?? contextChannel;
754
+ const watcherCount = watcherCountOverride ?? contextWatcherCount;
755
+ const { member_count: memberCount = 0 } = channel?.data || {};
756
+ const isDirectMessagingChannel = isDmChannel({
757
+ channel,
758
+ ownUserId: client.user?.id
759
+ });
760
+ const hasMembersOnline = useChannelHasMembersOnline({
761
+ channel,
762
+ enabled: isDirectMessagingChannel
763
+ });
764
+ if (!memberCount) return null;
765
+ if (isDirectMessagingChannel) return hasMembersOnline ? t("Online") : t("Offline");
766
+ return `${t("{{ memberCount }} members", { memberCount })} · ${t("{{ watcherCount }} online", { watcherCount })}`;
767
+ }
768
+ //#endregion
769
+ export { getTranslatedMessageText as A, ErrorBadge as C, useStableCallback as D, Root as E, MessageTranslationViewContext as O, Badge as S, Header as T, plusPlusToEmphasis as _, useChannelMembershipState as a, GroupAvatar as b, useChannelPreviewInfo as c, getGroupChannelDisplayInfo as d, getLatestMessagePreview as f, Checkmark as g, Checkbox as h, useIsChannelMuted as i, useMessageTranslationViewContext as j, MessageTranslationViewProvider as k, useChannelDisplayName as l, renderPreviewText as m, useChannelHasMembersOnline as n, useSelectedChannelState as o, getLatestMessagePreviewText as p, useIsUserMuted as r, isDmChannel as s, useChannelHeaderOnlineStatus as t, getChannelDisplayImage as u, imageToLink as v, Alert as w, ChannelAvatar as x, htmlToTextPlugin as y };
770
+
771
+ //# sourceMappingURL=useChannelHeaderOnlineStatus.a9357343.mjs.map