stream-chat-react 12.0.0-rc.9 → 12.1.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 (161) hide show
  1. package/dist/components/Avatar/Avatar.js +5 -1
  2. package/dist/components/Channel/Channel.d.ts +5 -106
  3. package/dist/components/Channel/Channel.js +76 -24
  4. package/dist/components/Channel/hooks/useCreateChannelStateContext.js +2 -1
  5. package/dist/components/ChannelHeader/ChannelHeader.js +4 -5
  6. package/dist/components/ChannelPreview/hooks/useChannelPreviewInfo.js +14 -16
  7. package/dist/components/ChannelPreview/utils.js +9 -20
  8. package/dist/components/ChannelSearch/hooks/useChannelSearch.js +2 -3
  9. package/dist/components/Chat/Chat.d.ts +1 -1
  10. package/dist/components/Chat/hooks/useChat.d.ts +2 -2
  11. package/dist/components/Chat/hooks/useChat.js +11 -8
  12. package/dist/components/ChatView/ChatView.d.ts +18 -0
  13. package/dist/components/ChatView/ChatView.js +103 -0
  14. package/dist/components/ChatView/index.d.ts +1 -0
  15. package/dist/components/ChatView/index.js +1 -0
  16. package/dist/components/DateSeparator/DateSeparator.d.ts +1 -1
  17. package/dist/components/Dialog/DialogAnchor.d.ts +25 -0
  18. package/dist/components/Dialog/DialogAnchor.js +68 -0
  19. package/dist/components/Dialog/DialogManager.d.ts +43 -0
  20. package/dist/components/Dialog/DialogManager.js +98 -0
  21. package/dist/components/Dialog/DialogPortal.d.ts +7 -0
  22. package/dist/components/Dialog/DialogPortal.js +25 -0
  23. package/dist/components/Dialog/hooks/index.d.ts +1 -0
  24. package/dist/components/Dialog/hooks/index.js +1 -0
  25. package/dist/components/Dialog/hooks/useDialog.d.ts +4 -0
  26. package/dist/components/Dialog/hooks/useDialog.js +26 -0
  27. package/dist/components/Dialog/index.d.ts +4 -0
  28. package/dist/components/Dialog/index.js +4 -0
  29. package/dist/components/EventComponent/EventComponent.d.ts +1 -1
  30. package/dist/components/Message/Message.js +5 -6
  31. package/dist/components/Message/MessageOptions.d.ts +1 -2
  32. package/dist/components/Message/MessageOptions.js +14 -11
  33. package/dist/components/Message/MessageSimple.js +6 -14
  34. package/dist/components/Message/MessageTimestamp.d.ts +1 -1
  35. package/dist/components/Message/QuotedMessage.js +2 -1
  36. package/dist/components/Message/Timestamp.d.ts +1 -1
  37. package/dist/components/Message/Timestamp.js +2 -2
  38. package/dist/components/Message/hooks/useReactionHandler.d.ts +1 -7
  39. package/dist/components/Message/hooks/useReactionHandler.js +8 -63
  40. package/dist/components/Message/utils.d.ts +10 -1
  41. package/dist/components/Message/utils.js +19 -7
  42. package/dist/components/MessageActions/MessageActions.d.ts +1 -2
  43. package/dist/components/MessageActions/MessageActions.js +26 -55
  44. package/dist/components/MessageActions/MessageActionsBox.d.ts +1 -1
  45. package/dist/components/MessageActions/MessageActionsBox.js +6 -6
  46. package/dist/components/MessageInput/MessageInputFlat.js +2 -2
  47. package/dist/components/MessageInput/QuotedMessagePreview.js +2 -1
  48. package/dist/components/MessageInput/hooks/useUserTrigger.js +0 -1
  49. package/dist/components/MessageList/MessageList.js +9 -9
  50. package/dist/components/MessageList/MessageListMainPanel.d.ts +1 -1
  51. package/dist/components/MessageList/MessageListMainPanel.js +1 -1
  52. package/dist/components/MessageList/VirtualizedMessageList.d.ts +2 -1
  53. package/dist/components/MessageList/VirtualizedMessageList.js +45 -40
  54. package/dist/components/MessageList/VirtualizedMessageListComponents.d.ts +1 -1
  55. package/dist/components/MessageList/VirtualizedMessageListComponents.js +6 -6
  56. package/dist/components/MessageList/hooks/MessageList/useUnreadMessagesNotification.js +2 -2
  57. package/dist/components/MessageList/renderMessages.d.ts +2 -2
  58. package/dist/components/MessageList/renderMessages.js +4 -1
  59. package/dist/components/MessageList/utils.js +1 -1
  60. package/dist/components/Reactions/ReactionSelector.d.ts +6 -3
  61. package/dist/components/Reactions/ReactionSelector.js +34 -24
  62. package/dist/components/Reactions/ReactionSelectorWithButton.d.ts +13 -0
  63. package/dist/components/Reactions/ReactionSelectorWithButton.js +22 -0
  64. package/dist/components/Reactions/ReactionsList.d.ts +4 -4
  65. package/dist/components/Reactions/hooks/useProcessReactions.js +2 -1
  66. package/dist/components/Thread/Thread.js +38 -10
  67. package/dist/components/Threads/ThreadContext.d.ts +9 -0
  68. package/dist/components/Threads/ThreadContext.js +9 -0
  69. package/dist/components/Threads/ThreadList/ThreadList.d.ts +9 -0
  70. package/dist/components/Threads/ThreadList/ThreadList.js +41 -0
  71. package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.d.ts +2 -0
  72. package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.js +5 -0
  73. package/dist/components/Threads/ThreadList/ThreadListItem.d.ts +9 -0
  74. package/dist/components/Threads/ThreadList/ThreadListItem.js +52 -0
  75. package/dist/components/Threads/ThreadList/ThreadListItemUI.d.ts +15 -0
  76. package/dist/components/Threads/ThreadList/ThreadListItemUI.js +75 -0
  77. package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.d.ts +2 -0
  78. package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.js +14 -0
  79. package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.d.ts +2 -0
  80. package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.js +16 -0
  81. package/dist/components/Threads/ThreadList/index.d.ts +3 -0
  82. package/dist/components/Threads/ThreadList/index.js +3 -0
  83. package/dist/components/Threads/UnreadCountBadge.d.ts +6 -0
  84. package/dist/components/Threads/UnreadCountBadge.js +5 -0
  85. package/dist/components/Threads/hooks/useThreadManagerState.d.ts +2 -0
  86. package/dist/components/Threads/hooks/useThreadManagerState.js +6 -0
  87. package/dist/components/Threads/hooks/useThreadState.d.ts +5 -0
  88. package/dist/components/Threads/hooks/useThreadState.js +11 -0
  89. package/dist/components/Threads/icons.d.ts +8 -0
  90. package/dist/components/Threads/icons.js +13 -0
  91. package/dist/components/Threads/index.d.ts +2 -0
  92. package/dist/components/Threads/index.js +2 -0
  93. package/dist/components/index.d.ts +3 -0
  94. package/dist/components/index.js +3 -0
  95. package/dist/context/ComponentContext.d.ts +64 -40
  96. package/dist/context/ComponentContext.js +7 -9
  97. package/dist/context/DialogManagerContext.d.ts +10 -0
  98. package/dist/context/DialogManagerContext.js +14 -0
  99. package/dist/context/MessageContext.d.ts +3 -11
  100. package/dist/context/MessageContext.js +3 -2
  101. package/dist/context/TranslationContext.d.ts +1 -11
  102. package/dist/context/TranslationContext.js +1 -9
  103. package/dist/context/WithComponents.d.ts +5 -0
  104. package/dist/context/WithComponents.js +7 -0
  105. package/dist/context/index.d.ts +2 -0
  106. package/dist/context/index.js +2 -0
  107. package/dist/css/v2/index.css +2 -2
  108. package/dist/css/v2/index.layout.css +2 -2
  109. package/dist/i18n/Streami18n.d.ts +1 -3
  110. package/dist/i18n/Streami18n.js +1 -2
  111. package/dist/i18n/index.d.ts +2 -1
  112. package/dist/i18n/index.js +2 -0
  113. package/dist/i18n/types.d.ts +26 -0
  114. package/dist/i18n/types.js +1 -0
  115. package/dist/i18n/utils.d.ts +9 -20
  116. package/dist/i18n/utils.js +10 -1
  117. package/dist/index.browser.cjs +10823 -10583
  118. package/dist/index.browser.cjs.map +4 -4
  119. package/dist/index.d.ts +1 -0
  120. package/dist/index.js +1 -0
  121. package/dist/index.node.cjs +10765 -10541
  122. package/dist/index.node.cjs.map +4 -4
  123. package/dist/plugins/Emojis/index.browser.cjs +7 -169
  124. package/dist/plugins/Emojis/index.browser.cjs.map +4 -4
  125. package/dist/plugins/Emojis/index.node.cjs +7 -169
  126. package/dist/plugins/Emojis/index.node.cjs.map +4 -4
  127. package/dist/plugins/encoders/mp3.browser.cjs +2 -4
  128. package/dist/plugins/encoders/mp3.browser.cjs.map +1 -1
  129. package/dist/plugins/encoders/mp3.node.cjs +2 -4
  130. package/dist/plugins/encoders/mp3.node.cjs.map +1 -1
  131. package/dist/scss/v2/Avatar/Avatar-layout.scss +10 -2
  132. package/dist/scss/v2/Avatar/Avatar-theme.scss +5 -0
  133. package/dist/scss/v2/ChatView/ChatView-layout.scss +43 -0
  134. package/dist/scss/v2/ChatView/ChatView-theme.scss +32 -0
  135. package/dist/scss/v2/Dialog/Dialog-layout.scss +8 -0
  136. package/dist/scss/v2/LoadingIndicator/LoadingIndicator-layout.scss +16 -0
  137. package/dist/scss/v2/Message/Message-layout.scss +8 -0
  138. package/dist/scss/v2/MessageActionsBox/MessageActionsBox-theme.scss +8 -0
  139. package/dist/scss/v2/MessageList/MessageList-layout.scss +0 -6
  140. package/dist/scss/v2/MessageList/VirtualizedMessageList-layout.scss +0 -12
  141. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +16 -0
  142. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +6 -0
  143. package/dist/scss/v2/Thread/Thread-layout.scss +15 -1
  144. package/dist/scss/v2/ThreadList/ThreadList-layout.scss +152 -0
  145. package/dist/scss/v2/ThreadList/ThreadList-theme.scss +75 -0
  146. package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-layout.scss +49 -0
  147. package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-theme.scss +11 -0
  148. package/dist/scss/v2/_base.scss +31 -0
  149. package/dist/scss/v2/index.layout.scss +4 -0
  150. package/dist/scss/v2/index.scss +3 -0
  151. package/dist/store/hooks/index.d.ts +1 -0
  152. package/dist/store/hooks/index.js +1 -0
  153. package/dist/store/hooks/useStateStore.d.ts +3 -0
  154. package/dist/store/hooks/useStateStore.js +15 -0
  155. package/dist/store/index.d.ts +1 -0
  156. package/dist/store/index.js +1 -0
  157. package/package.json +7 -6
  158. package/dist/assets/Poweredby_100px-White_VertText.png +0 -0
  159. package/dist/assets/str-chat__reaction-list-sprite@1x.png +0 -0
  160. package/dist/assets/str-chat__reaction-list-sprite@2x.png +0 -0
  161. package/dist/assets/str-chat__reaction-list-sprite@3x.png +0 -0
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { TimestampFormatterOptions } from '../../i18n/utils';
2
+ import type { TimestampFormatterOptions } from '../../i18n/types';
3
3
  export interface TimestampProps extends TimestampFormatterOptions {
4
4
  customClass?: string;
5
5
  timestamp?: Date | string;
@@ -1,7 +1,7 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { useMessageContext } from '../../context/MessageContext';
3
- import { isDate, useTranslationContext } from '../../context/TranslationContext';
4
- import { getDateString } from '../../i18n/utils';
3
+ import { useTranslationContext } from '../../context/TranslationContext';
4
+ import { getDateString, isDate } from '../../i18n/utils';
5
5
  export function Timestamp(props) {
6
6
  const { calendar, calendarFormats, customClass, format, timestamp } = props;
7
7
  const { formatDate } = useMessageContext('MessageTimestamp');
@@ -1,11 +1,5 @@
1
- import React, { RefObject } from 'react';
1
+ import React from 'react';
2
2
  import { StreamMessage } from '../../../context/ChannelStateContext';
3
- import type { ReactEventHandler } from '../types';
4
3
  import type { DefaultStreamChatGenerics } from '../../../types/types';
5
4
  export declare const reactionHandlerWarning = "Reaction handler was called, but it is missing one of its required arguments.\nMake sure the ChannelAction and ChannelState contexts are properly set and the hook is initialized with a valid message.";
6
5
  export declare const useReactionHandler: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message?: StreamMessage<StreamChatGenerics>) => (reactionType: string, event?: React.BaseSyntheticEvent) => Promise<void>;
7
- export declare const useReactionClick: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message?: StreamMessage<StreamChatGenerics>, reactionSelectorRef?: RefObject<HTMLDivElement | null>, messageWrapperRef?: RefObject<HTMLDivElement | null>, closeReactionSelectorOnClick?: boolean) => {
8
- isReactionEnabled: boolean;
9
- onReactionListClick: ReactEventHandler;
10
- showDetailedReactions: boolean;
11
- };
@@ -1,11 +1,13 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
1
+ import { useCallback } from 'react';
2
2
  import throttle from 'lodash.throttle';
3
3
  import { useChannelActionContext } from '../../../context/ChannelActionContext';
4
4
  import { useChannelStateContext } from '../../../context/ChannelStateContext';
5
5
  import { useChatContext } from '../../../context/ChatContext';
6
+ import { useThreadContext } from '../../Threads';
6
7
  export const reactionHandlerWarning = `Reaction handler was called, but it is missing one of its required arguments.
7
8
  Make sure the ChannelAction and ChannelState contexts are properly set and the hook is initialized with a valid message.`;
8
9
  export const useReactionHandler = (message) => {
10
+ const thread = useThreadContext();
9
11
  const { updateMessage } = useChannelActionContext('useReactionHandler');
10
12
  const { channel, channelCapabilities } = useChannelStateContext('useReactionHandler');
11
13
  const { client } = useChatContext('useReactionHandler');
@@ -64,14 +66,19 @@ export const useReactionHandler = (message) => {
64
66
  const tempMessage = createMessagePreview(add, newReaction, message);
65
67
  try {
66
68
  updateMessage(tempMessage);
69
+ // @ts-expect-error
70
+ thread?.upsertReplyLocally({ message: tempMessage });
67
71
  const messageResponse = add
68
72
  ? await channel.sendReaction(id, { type })
69
73
  : await channel.deleteReaction(id, type);
74
+ // seems useless as we're expecting WS event to come in and replace this anyway
70
75
  updateMessage(messageResponse.message);
71
76
  }
72
77
  catch (error) {
73
78
  // revert to the original message if the API call fails
74
79
  updateMessage(message);
80
+ // @ts-expect-error
81
+ thread?.upsertReplyLocally({ message });
75
82
  }
76
83
  }, 1000);
77
84
  return async (reactionType, event) => {
@@ -107,65 +114,3 @@ export const useReactionHandler = (message) => {
107
114
  }
108
115
  };
109
116
  };
110
- export const useReactionClick = (message, reactionSelectorRef, messageWrapperRef, closeReactionSelectorOnClick) => {
111
- const { channelCapabilities = {} } = useChannelStateContext('useReactionClick');
112
- const [showDetailedReactions, setShowDetailedReactions] = useState(false);
113
- const hasListener = useRef(false);
114
- const isReactionEnabled = channelCapabilities['send-reaction'];
115
- const messageDeleted = !!message?.deleted_at;
116
- const closeDetailedReactions = useCallback((event) => {
117
- if (event.target instanceof HTMLElement &&
118
- reactionSelectorRef?.current?.contains(event.target) &&
119
- !closeReactionSelectorOnClick) {
120
- return;
121
- }
122
- setShowDetailedReactions(false);
123
- },
124
- // eslint-disable-next-line react-hooks/exhaustive-deps
125
- [setShowDetailedReactions, reactionSelectorRef]);
126
- useEffect(() => {
127
- const messageWrapper = messageWrapperRef?.current;
128
- if (showDetailedReactions && !hasListener.current) {
129
- hasListener.current = true;
130
- document.addEventListener('click', closeDetailedReactions);
131
- if (messageWrapper) {
132
- messageWrapper.addEventListener('mouseleave', closeDetailedReactions);
133
- }
134
- }
135
- if (!showDetailedReactions && hasListener.current) {
136
- document.removeEventListener('click', closeDetailedReactions);
137
- if (messageWrapper) {
138
- messageWrapper.removeEventListener('mouseleave', closeDetailedReactions);
139
- }
140
- hasListener.current = false;
141
- }
142
- return () => {
143
- if (hasListener.current) {
144
- document.removeEventListener('click', closeDetailedReactions);
145
- if (messageWrapper) {
146
- messageWrapper.removeEventListener('mouseleave', closeDetailedReactions);
147
- }
148
- hasListener.current = false;
149
- }
150
- };
151
- }, [showDetailedReactions, closeDetailedReactions, messageWrapperRef]);
152
- useEffect(() => {
153
- const messageWrapper = messageWrapperRef?.current;
154
- if (messageDeleted && hasListener.current) {
155
- document.removeEventListener('click', closeDetailedReactions);
156
- if (messageWrapper) {
157
- messageWrapper.removeEventListener('mouseleave', closeDetailedReactions);
158
- }
159
- hasListener.current = false;
160
- }
161
- }, [messageDeleted, closeDetailedReactions, messageWrapperRef]);
162
- const onReactionListClick = (event) => {
163
- event?.stopPropagation?.();
164
- setShowDetailedReactions((prev) => !prev);
165
- };
166
- return {
167
- isReactionEnabled,
168
- onReactionListClick,
169
- showDetailedReactions,
170
- };
171
- };
@@ -2,7 +2,7 @@ import type { TFunction } from 'i18next';
2
2
  import type { MessageResponse, Mute, StreamChat, UserResponse } from 'stream-chat';
3
3
  import type { PinPermissions } from './hooks';
4
4
  import type { MessageProps } from './types';
5
- import type { MessageContextValue, StreamMessage } from '../../context';
5
+ import type { ComponentContextValue, CustomMessageActions, MessageContextValue, StreamMessage } from '../../context';
6
6
  import type { DefaultStreamChatGenerics } from '../../types/types';
7
7
  /**
8
8
  * Following function validates a function which returns notification message.
@@ -39,7 +39,16 @@ export type Capabilities = {
39
39
  };
40
40
  export declare const getMessageActions: (actions: MessageActionsArray | boolean, { canDelete, canEdit, canFlag, canMarkUnread, canMute, canPin, canQuote, canReact, canReply, }: Capabilities) => MessageActionsArray<string>;
41
41
  export declare const ACTIONS_NOT_WORKING_IN_THREAD: string[];
42
+ /**
43
+ * @deprecated use `shouldRenderMessageActions` instead
44
+ */
42
45
  export declare const showMessageActionsBox: (actions: MessageActionsArray<string>, inThread?: boolean | undefined) => boolean;
46
+ export declare const shouldRenderMessageActions: <SCG extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ customMessageActions, CustomMessageActionsList, inThread, messageActions, }: {
47
+ messageActions: MessageActionsArray;
48
+ customMessageActions?: CustomMessageActions<SCG>;
49
+ CustomMessageActionsList?: ComponentContextValue<SCG>['CustomMessageActionsList'];
50
+ inThread?: boolean;
51
+ }) => boolean;
43
52
  export declare const areMessagePropsEqual: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(prevProps: MessageProps<StreamChatGenerics> & {
44
53
  mutes?: Mute<StreamChatGenerics>[];
45
54
  showDetailedReactions?: boolean;
@@ -140,22 +140,31 @@ export const getMessageActions = (actions, { canDelete, canEdit, canFlag, canMar
140
140
  };
141
141
  export const ACTIONS_NOT_WORKING_IN_THREAD = [
142
142
  MESSAGE_ACTIONS.pin,
143
- MESSAGE_ACTIONS.react,
144
143
  MESSAGE_ACTIONS.reply,
145
144
  MESSAGE_ACTIONS.markUnread,
146
145
  ];
147
- export const showMessageActionsBox = (actions, inThread) => {
148
- if (actions.length === 0) {
146
+ /**
147
+ * @deprecated use `shouldRenderMessageActions` instead
148
+ */
149
+ export const showMessageActionsBox = (actions, inThread) => shouldRenderMessageActions({ inThread, messageActions: actions });
150
+ export const shouldRenderMessageActions = ({ customMessageActions, CustomMessageActionsList, inThread, messageActions, }) => {
151
+ if (typeof CustomMessageActionsList !== 'undefined' ||
152
+ typeof customMessageActions !== 'undefined')
153
+ return true;
154
+ if (!messageActions.length)
149
155
  return false;
150
- }
151
156
  if (inThread &&
152
- actions.filter((action) => !ACTIONS_NOT_WORKING_IN_THREAD.includes(action)).length === 0) {
157
+ messageActions.filter((action) => !ACTIONS_NOT_WORKING_IN_THREAD.includes(action)).length === 0) {
153
158
  return false;
154
159
  }
155
- if (actions.length === 1 && (actions.includes('react') || actions.includes('reply'))) {
160
+ if (messageActions.length === 1 &&
161
+ (messageActions.includes(MESSAGE_ACTIONS.react) ||
162
+ messageActions.includes(MESSAGE_ACTIONS.reply))) {
156
163
  return false;
157
164
  }
158
- if (actions.length === 2 && actions.includes('react') && actions.includes('reply')) {
165
+ if (messageActions.length === 2 &&
166
+ messageActions.includes(MESSAGE_ACTIONS.react) &&
167
+ messageActions.includes(MESSAGE_ACTIONS.reply)) {
159
168
  return false;
160
169
  }
161
170
  return true;
@@ -185,6 +194,9 @@ export const areMessagePropsEqual = (prevProps, nextProps) => {
185
194
  if (nextProps.showDetailedReactions !== prevProps.showDetailedReactions) {
186
195
  return false;
187
196
  }
197
+ if (nextProps.closeReactionSelectorOnClick !== prevProps.closeReactionSelectorOnClick) {
198
+ return false;
199
+ }
188
200
  const messagesAreEqual = areMessagesEqual(prevMessage, nextMessage);
189
201
  if (!messagesAreEqual)
190
202
  return false;
@@ -6,13 +6,12 @@ export type MessageActionsProps<StreamChatGenerics extends DefaultStreamChatGene
6
6
  ActionsIcon?: React.ComponentType<IconProps>;
7
7
  customWrapperClass?: string;
8
8
  inline?: boolean;
9
- messageWrapperRef?: React.RefObject<HTMLDivElement>;
10
9
  mine?: () => boolean;
11
10
  };
12
11
  export declare const MessageActions: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: MessageActionsProps<StreamChatGenerics>) => React.JSX.Element | null;
13
12
  export type MessageActionsWrapperProps = {
14
- setActionsBoxOpen: React.Dispatch<React.SetStateAction<boolean>>;
15
13
  customWrapperClass?: string;
16
14
  inline?: boolean;
15
+ toggleOpen?: () => void;
17
16
  };
18
17
  export {};
@@ -1,15 +1,17 @@
1
- import React, { useCallback, useEffect, useRef, useState, } from 'react';
1
+ import clsx from 'clsx';
2
+ import React, { useCallback, useRef } from 'react';
2
3
  import { MessageActionsBox } from './MessageActionsBox';
4
+ import { DialogAnchor, useDialog, useDialogIsOpen } from '../Dialog';
3
5
  import { ActionsIcon as DefaultActionsIcon } from '../Message/icons';
4
- import { isUserMuted } from '../Message/utils';
6
+ import { isUserMuted, shouldRenderMessageActions } from '../Message/utils';
5
7
  import { useChatContext } from '../../context/ChatContext';
6
8
  import { useMessageContext } from '../../context/MessageContext';
7
- import { useMessageActionsBoxPopper } from './hooks';
8
- import { useTranslationContext } from '../../context';
9
+ import { useComponentContext, useTranslationContext } from '../../context';
9
10
  export const MessageActions = (props) => {
10
- const { ActionsIcon = DefaultActionsIcon, customWrapperClass = '', getMessageActions: propGetMessageActions, handleDelete: propHandleDelete, handleFlag: propHandleFlag, handleMarkUnread: propHandleMarkUnread, handleMute: propHandleMute, handlePin: propHandlePin, inline, message: propMessage, messageWrapperRef, mine, } = props;
11
+ const { ActionsIcon = DefaultActionsIcon, customWrapperClass = '', getMessageActions: propGetMessageActions, handleDelete: propHandleDelete, handleFlag: propHandleFlag, handleMarkUnread: propHandleMarkUnread, handleMute: propHandleMute, handlePin: propHandlePin, inline, message: propMessage, mine, } = props;
11
12
  const { mutes } = useChatContext('MessageActions');
12
- const { customMessageActions, getMessageActions: contextGetMessageActions, handleDelete: contextHandleDelete, handleFlag: contextHandleFlag, handleMarkUnread: contextHandleMarkUnread, handleMute: contextHandleMute, handlePin: contextHandlePin, isMyMessage, message: contextMessage, setEditingState, } = useMessageContext('MessageActions');
13
+ const { customMessageActions, getMessageActions: contextGetMessageActions, handleDelete: contextHandleDelete, handleFlag: contextHandleFlag, handleMarkUnread: contextHandleMarkUnread, handleMute: contextHandleMute, handlePin: contextHandlePin, isMyMessage, message: contextMessage, setEditingState, threadList, } = useMessageContext('MessageActions');
14
+ const { CustomMessageActionsList } = useComponentContext('MessageActions');
13
15
  const { t } = useTranslationContext('MessageActions');
14
16
  const getMessageActions = propGetMessageActions || contextGetMessageActions;
15
17
  const handleDelete = propHandleDelete || contextHandleDelete;
@@ -19,64 +21,33 @@ export const MessageActions = (props) => {
19
21
  const handlePin = propHandlePin || contextHandlePin;
20
22
  const message = propMessage || contextMessage;
21
23
  const isMine = mine ? mine() : isMyMessage();
22
- const [actionsBoxOpen, setActionsBoxOpen] = useState(false);
23
24
  const isMuted = useCallback(() => isUserMuted(message, mutes), [message, mutes]);
24
- const hideOptions = useCallback((event) => {
25
- if (event instanceof KeyboardEvent && event.key !== 'Escape') {
26
- return;
27
- }
28
- setActionsBoxOpen(false);
29
- }, []);
25
+ const dialogId = `message-actions--${message.id}`;
26
+ const dialog = useDialog({ id: dialogId });
27
+ const dialogIsOpen = useDialogIsOpen(dialogId);
30
28
  const messageActions = getMessageActions();
31
- const messageDeletedAt = !!message?.deleted_at;
32
- useEffect(() => {
33
- if (messageWrapperRef?.current) {
34
- messageWrapperRef.current.addEventListener('mouseleave', hideOptions);
35
- }
36
- }, [hideOptions, messageWrapperRef]);
37
- useEffect(() => {
38
- if (messageDeletedAt) {
39
- document.removeEventListener('click', hideOptions);
40
- }
41
- }, [hideOptions, messageDeletedAt]);
42
- useEffect(() => {
43
- if (!actionsBoxOpen)
44
- return;
45
- document.addEventListener('click', hideOptions);
46
- document.addEventListener('keyup', hideOptions);
47
- return () => {
48
- document.removeEventListener('click', hideOptions);
49
- document.removeEventListener('keyup', hideOptions);
50
- };
51
- }, [actionsBoxOpen, hideOptions]);
52
- const actionsBoxButtonRef = useRef(null);
53
- const { attributes, popperElementRef, styles } = useMessageActionsBoxPopper({
54
- open: actionsBoxOpen,
55
- placement: isMine ? 'top-end' : 'top-start',
56
- referenceElement: actionsBoxButtonRef.current,
29
+ const renderMessageActions = shouldRenderMessageActions({
30
+ customMessageActions,
31
+ CustomMessageActionsList,
32
+ inThread: threadList,
33
+ messageActions,
57
34
  });
58
- if (!messageActions.length && !customMessageActions)
35
+ const actionsBoxButtonRef = useRef(null);
36
+ if (!renderMessageActions)
59
37
  return null;
60
- return (React.createElement(MessageActionsWrapper, { customWrapperClass: customWrapperClass, inline: inline, setActionsBoxOpen: setActionsBoxOpen },
61
- React.createElement(MessageActionsBox, { ...attributes.popper, getMessageActions: getMessageActions, handleDelete: handleDelete, handleEdit: setEditingState, handleFlag: handleFlag, handleMarkUnread: handleMarkUnread, handleMute: handleMute, handlePin: handlePin, isUserMuted: isMuted, mine: isMine, open: actionsBoxOpen, ref: popperElementRef, style: styles.popper }),
62
- React.createElement("button", { "aria-expanded": actionsBoxOpen, "aria-haspopup": 'true', "aria-label": t('aria/Open Message Actions Menu'), className: 'str-chat__message-actions-box-button', ref: actionsBoxButtonRef },
38
+ return (React.createElement(MessageActionsWrapper, { customWrapperClass: customWrapperClass, inline: inline, toggleOpen: dialog?.toggle },
39
+ React.createElement(DialogAnchor, { id: dialogId, placement: isMine ? 'top-end' : 'top-start', referenceElement: actionsBoxButtonRef.current, trapFocus: true },
40
+ React.createElement(MessageActionsBox, { getMessageActions: getMessageActions, handleDelete: handleDelete, handleEdit: setEditingState, handleFlag: handleFlag, handleMarkUnread: handleMarkUnread, handleMute: handleMute, handlePin: handlePin, isUserMuted: isMuted, mine: isMine, open: dialogIsOpen })),
41
+ React.createElement("button", { "aria-expanded": dialogIsOpen, "aria-haspopup": 'true', "aria-label": t('aria/Open Message Actions Menu'), className: 'str-chat__message-actions-box-button', "data-testid": 'message-actions-toggle-button', ref: actionsBoxButtonRef },
63
42
  React.createElement(ActionsIcon, { className: 'str-chat__message-action-icon' }))));
64
43
  };
65
44
  const MessageActionsWrapper = (props) => {
66
- const { children, customWrapperClass, inline, setActionsBoxOpen } = props;
67
- const defaultWrapperClass = `
68
- str-chat__message-simple__actions__action
69
- str-chat__message-simple__actions__action--options
70
- str-chat__message-actions-container`;
71
- const wrapperClass = customWrapperClass || defaultWrapperClass;
72
- const onClickOptionsAction = (event) => {
73
- event.stopPropagation();
74
- setActionsBoxOpen((prev) => !prev);
75
- };
45
+ const { children, customWrapperClass, inline, toggleOpen } = props;
46
+ const defaultWrapperClass = clsx('str-chat__message-simple__actions__action', 'str-chat__message-simple__actions__action--options', 'str-chat__message-actions-container');
76
47
  const wrapperProps = {
77
- className: wrapperClass,
48
+ className: customWrapperClass || defaultWrapperClass,
78
49
  'data-testid': 'message-actions',
79
- onClick: onClickOptionsAction,
50
+ onClick: toggleOpen,
80
51
  };
81
52
  if (inline)
82
53
  return React.createElement("span", { ...wrapperProps }, children);
@@ -10,5 +10,5 @@ export type MessageActionsBoxProps<StreamChatGenerics extends DefaultStreamChatG
10
10
  /**
11
11
  * A popup box that displays the available actions on a message, such as edit, delete, pin, etc.
12
12
  */
13
- export declare const MessageActionsBox: React.ForwardRefExoticComponent<Omit<MessageActionsBoxProps<DefaultStreamChatGenerics>, "ref"> & React.RefAttributes<HTMLDivElement | null>>;
13
+ export declare const MessageActionsBox: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: MessageActionsBoxProps<StreamChatGenerics>) => React.JSX.Element;
14
14
  export {};
@@ -1,10 +1,10 @@
1
- import React from 'react';
2
1
  import clsx from 'clsx';
2
+ import React from 'react';
3
3
  import { MESSAGE_ACTIONS } from '../Message/utils';
4
4
  import { useChannelActionContext, useComponentContext, useMessageContext, useTranslationContext, } from '../../context';
5
5
  import { CustomMessageActionsList as DefaultCustomMessageActionsList } from './CustomMessageActionsList';
6
- const UnMemoizedMessageActionsBox = React.forwardRef((props, ref) => {
7
- const { getMessageActions, handleDelete, handleEdit, handleFlag, handleMarkUnread, handleMute, handlePin, isUserMuted, mine, open = false, ...restDivProps } = props;
6
+ const UnMemoizedMessageActionsBox = (props) => {
7
+ const { className, getMessageActions, handleDelete, handleEdit, handleFlag, handleMarkUnread, handleMute, handlePin, isUserMuted, mine, open, ...restDivProps } = props;
8
8
  const { CustomMessageActionsList = DefaultCustomMessageActionsList, } = useComponentContext('MessageActionsBox');
9
9
  const { setQuotedMessage } = useChannelActionContext('MessageActionsBox');
10
10
  const { customMessageActions, message, threadList } = useMessageContext('MessageActionsBox');
@@ -20,11 +20,11 @@ const UnMemoizedMessageActionsBox = React.forwardRef((props, ref) => {
20
20
  textarea.focus();
21
21
  }
22
22
  };
23
- const rootClassName = clsx('str-chat__message-actions-box', {
23
+ const rootClassName = clsx('str-chat__message-actions-box', className, {
24
24
  'str-chat__message-actions-box--open': open,
25
25
  });
26
26
  const buttonClassName = 'str-chat__message-actions-list-item str-chat__message-actions-list-item-button';
27
- return (React.createElement("div", { ...restDivProps, className: rootClassName, "data-testid": 'message-actions-box', ref: ref },
27
+ return (React.createElement("div", { ...restDivProps, className: rootClassName, "data-testid": 'message-actions-box' },
28
28
  React.createElement("div", { "aria-label": t('aria/Message Options'), className: 'str-chat__message-actions-list', role: 'listbox' },
29
29
  React.createElement(CustomMessageActionsList, { customMessageActions: customMessageActions, message: message }),
30
30
  messageActions.indexOf(MESSAGE_ACTIONS.quote) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleQuote, role: 'option' }, t('Reply'))),
@@ -34,7 +34,7 @@ const UnMemoizedMessageActionsBox = React.forwardRef((props, ref) => {
34
34
  messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleMute, role: 'option' }, isUserMuted() ? t('Unmute') : t('Mute'))),
35
35
  messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleEdit, role: 'option' }, t('Edit Message'))),
36
36
  messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleDelete, role: 'option' }, t('Delete'))))));
37
- });
37
+ };
38
38
  /**
39
39
  * A popup box that displays the available actions on a message, such as edit, delete, pin, etc.
40
40
  */
@@ -20,7 +20,7 @@ import { useMessageInputContext } from '../../context/MessageInputContext';
20
20
  import { useComponentContext } from '../../context/ComponentContext';
21
21
  export const MessageInputFlat = () => {
22
22
  const { t } = useTranslationContext('MessageInputFlat');
23
- const { asyncMessagesMultiSendEnabled, attachments, cooldownRemaining, findAndEnqueueURLsToEnrich, handleSubmit, hideSendButton, isUploadEnabled, linkPreviews, maxFilesLeft, message, numberOfUploads, recordingController, setCooldownRemaining, text, uploadNewFiles, } = useMessageInputContext('MessageInputFlat');
23
+ const { asyncMessagesMultiSendEnabled, attachments, cooldownRemaining, findAndEnqueueURLsToEnrich, handleSubmit, hideSendButton, isUploadEnabled, linkPreviews, maxFilesLeft, message, numberOfUploads, parent, recordingController, setCooldownRemaining, text, uploadNewFiles, } = useMessageInputContext('MessageInputFlat');
24
24
  const { AudioRecorder = DefaultAudioRecorder, AttachmentPreviewList = DefaultAttachmentPreviewList, CooldownTimer = DefaultCooldownTimer, FileUploadIcon = DefaultUploadIcon, LinkPreviewList = DefaultLinkPreviewList, QuotedMessagePreview = DefaultQuotedMessagePreview, RecordingPermissionDeniedNotification = DefaultRecordingPermissionDeniedNotification, SendButton = DefaultSendButton, StartRecordingAudioButton = DefaultStartRecordingAudioButton, EmojiPicker, } = useComponentContext('MessageInputFlat');
25
25
  const { acceptedFiles = [], multipleUploads, quotedMessage, } = useChannelStateContext('MessageInputFlat');
26
26
  const { setQuotedMessage } = useChannelActionContext('MessageInputFlat');
@@ -64,7 +64,7 @@ export const MessageInputFlat = () => {
64
64
  return React.createElement(AudioRecorder, null);
65
65
  // TODO: "!message" condition is a temporary fix for shared
66
66
  // state when editing a message (fix shared state issue)
67
- const displayQuotedMessage = !message && quotedMessage && !quotedMessage.parent_id;
67
+ const displayQuotedMessage = !message && quotedMessage && quotedMessage.parent_id === parent?.id;
68
68
  const recordingEnabled = !!(recordingController.recorder && navigator.mediaDevices); // account for requirement on iOS as per this bug report: https://bugs.webkit.org/show_bug.cgi?id=252303
69
69
  const isRecording = !!recordingController.recordingState;
70
70
  return (React.createElement(React.Fragment, null,
@@ -1,4 +1,5 @@
1
1
  import React, { useMemo } from 'react';
2
+ import { Attachment as DefaultAttachment } from '../Attachment';
2
3
  import { Avatar as DefaultAvatar } from '../Avatar';
3
4
  import { CloseIcon } from './icons';
4
5
  import { useChannelActionContext } from '../../context/ChannelActionContext';
@@ -13,7 +14,7 @@ export const QuotedMessagePreviewHeader = () => {
13
14
  React.createElement(CloseIcon, null))));
14
15
  };
15
16
  export const QuotedMessagePreview = ({ quotedMessage, }) => {
16
- const { Attachment, Avatar = DefaultAvatar } = useComponentContext('QuotedMessagePreview');
17
+ const { Attachment = DefaultAttachment, Avatar = DefaultAvatar, } = useComponentContext('QuotedMessagePreview');
17
18
  const { userLanguage } = useTranslationContext('QuotedMessagePreview');
18
19
  const quotedMessageText = quotedMessage.i18n?.[`${userLanguage}_text`] ||
19
20
  quotedMessage.text;
@@ -52,7 +52,6 @@ export const useUserTrigger = (params) => {
52
52
  // @ts-expect-error
53
53
  {
54
54
  $or: [{ id: { $autocomplete: query } }, { name: { $autocomplete: query } }],
55
- id: { $ne: client.userID },
56
55
  ...(typeof mentionQueryParams.filters === 'function'
57
56
  ? mentionQueryParams.filters(query)
58
57
  : mentionQueryParams.filters),
@@ -7,6 +7,7 @@ import { MessageListNotifications as DefaultMessageListNotifications } from './M
7
7
  import { UnreadMessagesNotification as DefaultUnreadMessagesNotification } from './UnreadMessagesNotification';
8
8
  import { useChannelActionContext, } from '../../context/ChannelActionContext';
9
9
  import { useChannelStateContext, } from '../../context/ChannelStateContext';
10
+ import { DialogManagerProvider } from '../../context';
10
11
  import { useChatContext } from '../../context/ChatContext';
11
12
  import { useComponentContext } from '../../context/ComponentContext';
12
13
  import { MessageListContextProvider } from '../../context/MessageListContext';
@@ -15,7 +16,7 @@ import { InfiniteScroll } from '../InfiniteScrollPaginator/InfiniteScroll';
15
16
  import { LoadingIndicator as DefaultLoadingIndicator } from '../Loading';
16
17
  import { defaultPinPermissions, MESSAGE_ACTIONS } from '../Message/utils';
17
18
  import { TypingIndicator as DefaultTypingIndicator } from '../TypingIndicator';
18
- import { MessageListMainPanel } from './MessageListMainPanel';
19
+ import { MessageListMainPanel as DefaultMessageListMainPanel } from './MessageListMainPanel';
19
20
  import { defaultRenderMessages } from './renderMessages';
20
21
  import { DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, DEFAULT_NEXT_CHANNEL_PAGE_SIZE, } from '../../constants/limits';
21
22
  const MessageListWithContext = (props) => {
@@ -24,7 +25,7 @@ const MessageListWithContext = (props) => {
24
25
  const [listElement, setListElement] = React.useState(null);
25
26
  const [ulElement, setUlElement] = React.useState(null);
26
27
  const { customClasses } = useChatContext('MessageList');
27
- const { EmptyStateIndicator = DefaultEmptyStateIndicator, LoadingIndicator = DefaultLoadingIndicator, MessageListNotifications = DefaultMessageListNotifications, MessageNotification = DefaultMessageNotification, TypingIndicator = DefaultTypingIndicator, UnreadMessagesNotification = DefaultUnreadMessagesNotification, } = useComponentContext('MessageList');
28
+ const { EmptyStateIndicator = DefaultEmptyStateIndicator, LoadingIndicator = DefaultLoadingIndicator, MessageListNotifications = DefaultMessageListNotifications, MessageNotification = DefaultMessageNotification, TypingIndicator = DefaultTypingIndicator, UnreadMessagesNotification = DefaultUnreadMessagesNotification, MessageListMainPanel = DefaultMessageListMainPanel, } = useComponentContext('MessageList');
28
29
  const { hasNewMessages, isMessageListScrolledToBottom, onScroll, scrollToBottom, wrapperRect, } = useScrollLocationLogic({
29
30
  hasMoreNewer,
30
31
  listElement,
@@ -126,13 +127,12 @@ const MessageListWithContext = (props) => {
126
127
  const showEmptyStateIndicator = elements.length === 0 && !threadList;
127
128
  return (React.createElement(MessageListContextProvider, { value: { listElement, scrollToBottom } },
128
129
  React.createElement(MessageListMainPanel, null,
129
- !threadList && showUnreadMessagesNotification && (React.createElement(UnreadMessagesNotification, { unreadCount: channelUnreadUiState?.unread_messages })),
130
- React.createElement("div", { className: clsx(messageListClass, {
131
- [customClasses?.threadList || 'str-chat__thread-list']: threadList,
132
- }), onScroll: onScroll, ref: setListElement, tabIndex: 0 }, showEmptyStateIndicator ? (React.createElement(EmptyStateIndicator, { key: 'empty-state-indicator', listType: threadList ? 'thread' : 'message' })) : (React.createElement(InfiniteScroll, { className: 'str-chat__message-list-scroll', "data-testid": 'reverse-infinite-scroll', hasNextPage: props.hasMoreNewer, hasPreviousPage: props.hasMore, head: props.head, isLoading: props.loadingMore, loader: React.createElement("div", { className: 'str-chat__list__loading', key: 'loading-indicator' }, props.loadingMore && React.createElement(LoadingIndicator, { size: 20 })), loadNextPage: loadMoreNewer, loadPreviousPage: loadMore, threshold: loadMoreScrollThreshold, ...restInternalInfiniteScrollProps },
133
- React.createElement("ul", { className: 'str-chat__ul', ref: setUlElement }, elements),
134
- React.createElement(TypingIndicator, { threadList: threadList }),
135
- React.createElement("div", { key: 'bottom' }))))),
130
+ React.createElement(DialogManagerProvider, { id: 'message-list-dialog-manager' },
131
+ !threadList && showUnreadMessagesNotification && (React.createElement(UnreadMessagesNotification, { unreadCount: channelUnreadUiState?.unread_messages })),
132
+ React.createElement("div", { className: clsx(messageListClass, customClasses?.threadList), onScroll: onScroll, ref: setListElement, tabIndex: 0 }, showEmptyStateIndicator ? (React.createElement(EmptyStateIndicator, { listType: threadList ? 'thread' : 'message' })) : (React.createElement(InfiniteScroll, { className: 'str-chat__message-list-scroll', "data-testid": 'reverse-infinite-scroll', hasNextPage: props.hasMoreNewer, hasPreviousPage: props.hasMore, head: props.head, isLoading: props.loadingMore, loader: React.createElement("div", { className: 'str-chat__list__loading', key: 'loading-indicator' }, props.loadingMore && React.createElement(LoadingIndicator, { size: 20 })), loadNextPage: loadMoreNewer, loadPreviousPage: loadMore, threshold: loadMoreScrollThreshold, ...restInternalInfiniteScrollProps },
133
+ React.createElement("ul", { className: 'str-chat__ul', ref: setUlElement }, elements),
134
+ React.createElement(TypingIndicator, { threadList: threadList }),
135
+ React.createElement("div", { key: 'bottom' })))))),
136
136
  React.createElement(MessageListNotifications, { hasNewMessages: hasNewMessages, isMessageListScrolledToBottom: isMessageListScrolledToBottom, isNotAtLatestMessageSet: hasMoreNewer, MessageNotification: MessageNotification, notifications: notifications, scrollToBottom: scrollToBottomFromNotification, threadList: threadList, unreadCount: threadList ? undefined : channelUnreadUiState?.unread_messages })));
137
137
  };
138
138
  /**
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
2
  import type { PropsWithChildrenOnly } from '../../types/types';
3
- export declare const MESSAGE_LIST_MAIN_PANEL_CLASS: "str-chat__main-panel-inner";
3
+ export declare const MESSAGE_LIST_MAIN_PANEL_CLASS: "str-chat__main-panel-inner str-chat__message-list-main-panel";
4
4
  export declare const MessageListMainPanel: ({ children }: PropsWithChildrenOnly) => React.JSX.Element;
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
- export const MESSAGE_LIST_MAIN_PANEL_CLASS = 'str-chat__main-panel-inner';
2
+ export const MESSAGE_LIST_MAIN_PANEL_CLASS = 'str-chat__main-panel-inner str-chat__message-list-main-panel';
3
3
  export const MessageListMainPanel = ({ children }) => (React.createElement("div", { className: MESSAGE_LIST_MAIN_PANEL_CLASS }, children));
@@ -3,7 +3,7 @@ import { ScrollSeekConfiguration, ScrollSeekPlaceholderProps, VirtuosoHandle, Vi
3
3
  import { GroupStyle, ProcessMessagesParams } from './utils';
4
4
  import { MessageProps, MessageUIComponentProps } from '../Message';
5
5
  import { ChannelActionContextValue } from '../../context/ChannelActionContext';
6
- import { StreamMessage } from '../../context/ChannelStateContext';
6
+ import { ChannelStateContextValue, StreamMessage } from '../../context/ChannelStateContext';
7
7
  import { ChatContextValue } from '../../context/ChatContext';
8
8
  import { ComponentContextValue } from '../../context/ComponentContext';
9
9
  import type { UserResponse } from 'stream-chat';
@@ -40,6 +40,7 @@ type PropsDrilledToMessage = 'additionalMessageInputProps' | 'customMessageActio
40
40
  export type VirtualizedMessageListProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Partial<Pick<MessageProps<StreamChatGenerics>, PropsDrilledToMessage>> & {
41
41
  /** Additional props to be passed the underlying [`react-virtuoso` virtualized list dependency](https://virtuoso.dev/virtuoso-api-reference/) */
42
42
  additionalVirtuosoProps?: VirtuosoProps<UnknownType, VirtuosoContext<StreamChatGenerics>>;
43
+ channelUnreadUiState?: ChannelStateContextValue['channelUnreadUiState'];
43
44
  /** If true, picking a reaction from the `ReactionSelector` component will close the selector */
44
45
  closeReactionSelectorOnClick?: boolean;
45
46
  /** Custom render function, if passed, certain UI props are ignored */