stream-chat-react 12.11.1 → 12.13.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 (102) hide show
  1. package/dist/components/Channel/Channel.d.ts +1 -1
  2. package/dist/components/Channel/Channel.js +41 -26
  3. package/dist/components/Channel/channelState.d.ts +1 -1
  4. package/dist/components/Channel/channelState.js +2 -1
  5. package/dist/components/ChannelList/ChannelList.d.ts +2 -2
  6. package/dist/components/ChannelList/ChannelList.js +15 -9
  7. package/dist/components/ChannelPreview/ChannelPreview.d.ts +6 -6
  8. package/dist/components/ChannelPreview/ChannelPreview.js +3 -4
  9. package/dist/components/ChannelPreview/ChannelPreviewMessenger.d.ts +1 -1
  10. package/dist/components/ChannelPreview/utils.d.ts +2 -2
  11. package/dist/components/ChannelSearch/SearchBar.js +7 -8
  12. package/dist/components/ChannelSearch/SearchResults.js +8 -7
  13. package/dist/components/Chat/Chat.d.ts +5 -2
  14. package/dist/components/Chat/Chat.js +12 -2
  15. package/dist/components/Chat/hooks/useChat.js +1 -1
  16. package/dist/components/Chat/hooks/useCreateChatContext.js +3 -1
  17. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.d.ts +1 -1
  18. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +1 -1
  19. package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.d.ts +1 -0
  20. package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.js +8 -2
  21. package/dist/components/MediaRecorder/classes/MediaRecorderController.d.ts +6 -1
  22. package/dist/components/MediaRecorder/classes/MediaRecorderController.js +6 -8
  23. package/dist/components/Message/QuotedMessage.d.ts +3 -1
  24. package/dist/components/Message/QuotedMessage.js +14 -11
  25. package/dist/components/Message/renderText/renderText.d.ts +3 -3
  26. package/dist/components/Message/renderText/renderText.js +3 -3
  27. package/dist/components/Message/types.d.ts +2 -2
  28. package/dist/components/MessageInput/MessageInputFlat.js +2 -1
  29. package/dist/components/MessageInput/QuotedMessagePreview.d.ts +3 -1
  30. package/dist/components/MessageInput/QuotedMessagePreview.js +4 -3
  31. package/dist/components/MessageList/utils.js +3 -0
  32. package/dist/context/ChatContext.d.ts +3 -1
  33. package/dist/context/ComponentContext.d.ts +23 -0
  34. package/dist/context/MessageContext.d.ts +2 -2
  35. package/dist/css/v2/index.css +1 -1
  36. package/dist/css/v2/index.layout.css +1 -1
  37. package/dist/experimental/Search/Search.d.ts +12 -0
  38. package/dist/experimental/Search/Search.js +25 -0
  39. package/dist/experimental/Search/SearchBar/SearchBar.d.ts +2 -0
  40. package/dist/experimental/Search/SearchBar/SearchBar.js +56 -0
  41. package/dist/experimental/Search/SearchBar/index.d.ts +1 -0
  42. package/dist/experimental/Search/SearchBar/index.js +1 -0
  43. package/dist/experimental/Search/SearchContext.d.ts +23 -0
  44. package/dist/experimental/Search/SearchContext.js +10 -0
  45. package/dist/experimental/Search/SearchResults/SearchResultItem.d.ts +19 -0
  46. package/dist/experimental/Search/SearchResults/SearchResultItem.js +62 -0
  47. package/dist/experimental/Search/SearchResults/SearchResults.d.ts +3 -0
  48. package/dist/experimental/Search/SearchResults/SearchResults.js +21 -0
  49. package/dist/experimental/Search/SearchResults/SearchResultsHeader.d.ts +3 -0
  50. package/dist/experimental/Search/SearchResults/SearchResultsHeader.js +31 -0
  51. package/dist/experimental/Search/SearchResults/SearchResultsPresearch.d.ts +6 -0
  52. package/dist/experimental/Search/SearchResults/SearchResultsPresearch.js +6 -0
  53. package/dist/experimental/Search/SearchResults/SearchSourceResultList.d.ts +9 -0
  54. package/dist/experimental/Search/SearchResults/SearchSourceResultList.js +22 -0
  55. package/dist/experimental/Search/SearchResults/SearchSourceResultListFooter.d.ts +3 -0
  56. package/dist/experimental/Search/SearchResults/SearchSourceResultListFooter.js +16 -0
  57. package/dist/experimental/Search/SearchResults/SearchSourceResults.d.ts +7 -0
  58. package/dist/experimental/Search/SearchResults/SearchSourceResults.js +21 -0
  59. package/dist/experimental/Search/SearchResults/SearchSourceResultsEmpty.d.ts +2 -0
  60. package/dist/experimental/Search/SearchResults/SearchSourceResultsEmpty.js +6 -0
  61. package/dist/experimental/Search/SearchResults/SearchSourceResultsHeader.d.ts +1 -0
  62. package/dist/experimental/Search/SearchResults/SearchSourceResultsHeader.js +1 -0
  63. package/dist/experimental/Search/SearchResults/SearchSourceResultsLoadingIndicator.d.ts +2 -0
  64. package/dist/experimental/Search/SearchResults/SearchSourceResultsLoadingIndicator.js +8 -0
  65. package/dist/experimental/Search/SearchResults/index.d.ts +9 -0
  66. package/dist/experimental/Search/SearchResults/index.js +9 -0
  67. package/dist/experimental/Search/SearchSourceResultsContext.d.ts +13 -0
  68. package/dist/experimental/Search/SearchSourceResultsContext.js +10 -0
  69. package/dist/experimental/Search/hooks/index.d.ts +2 -0
  70. package/dist/experimental/Search/hooks/index.js +2 -0
  71. package/dist/experimental/Search/hooks/useSearchFocusedMessage.d.ts +2 -0
  72. package/dist/experimental/Search/hooks/useSearchFocusedMessage.js +8 -0
  73. package/dist/experimental/Search/hooks/useSearchQueriesInProgress.d.ts +6 -0
  74. package/dist/experimental/Search/hooks/useSearchQueriesInProgress.js +22 -0
  75. package/dist/experimental/Search/index.d.ts +5 -0
  76. package/dist/experimental/Search/index.js +5 -0
  77. package/dist/experimental/index.browser.cjs +11286 -301
  78. package/dist/experimental/index.browser.cjs.map +4 -4
  79. package/dist/experimental/index.d.ts +1 -0
  80. package/dist/experimental/index.js +1 -0
  81. package/dist/experimental/index.node.cjs +13176 -301
  82. package/dist/experimental/index.node.cjs.map +4 -4
  83. package/dist/i18n/Streami18n.d.ts +7 -0
  84. package/dist/i18n/de.json +7 -0
  85. package/dist/i18n/en.json +7 -0
  86. package/dist/i18n/es.json +7 -0
  87. package/dist/i18n/fr.json +8 -1
  88. package/dist/i18n/hi.json +7 -0
  89. package/dist/i18n/it.json +7 -0
  90. package/dist/i18n/ja.json +7 -0
  91. package/dist/i18n/ko.json +7 -0
  92. package/dist/i18n/nl.json +7 -0
  93. package/dist/i18n/pt.json +8 -1
  94. package/dist/i18n/ru.json +7 -0
  95. package/dist/i18n/tr.json +7 -0
  96. package/dist/index.browser.cjs +22862 -21873
  97. package/dist/index.browser.cjs.map +4 -4
  98. package/dist/index.node.cjs +17830 -16963
  99. package/dist/index.node.cjs.map +4 -4
  100. package/dist/scss/v2/Message/Message-layout.scss +1 -1
  101. package/dist/scss/v2/Message/Message-theme.scss +5 -5
  102. package/package.json +9 -10
@@ -1,8 +1,8 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
- import { ChannelQueryOptions, EventAPIResponse, Message, MessageResponse, Channel as StreamChannel, StreamChat, UpdatedMessage } from 'stream-chat';
3
2
  import { OnMentionAction } from './hooks/useMentionsHandlers';
4
3
  import { LoadingErrorIndicatorProps } from '../Loading';
5
4
  import { ComponentContextValue, StreamMessage } from '../../context';
5
+ import type { ChannelQueryOptions, EventAPIResponse, Message, MessageResponse, Channel as StreamChannel, StreamChat, UpdatedMessage } from 'stream-chat';
6
6
  import type { MessageInputProps } from '../MessageInput';
7
7
  import type { ChannelUnreadUiState, CustomTrigger, DefaultStreamChatGenerics, GiphyVersions, ImageAttachmentSizeHandler, SendMessageOptions, UpdateMessageOptions, VideoAttachmentSizeHandler } from '../../types/types';
8
8
  import type { URLEnrichmentConfig } from '../MessageInput/hooks/useLinkPreviews';
@@ -14,14 +14,15 @@ import { LoadingErrorIndicator as DefaultLoadingErrorIndicator, } from '../Loadi
14
14
  import { LoadingChannel as DefaultLoadingIndicator } from './LoadingChannel';
15
15
  import { DropzoneProvider } from '../MessageInput/DropzoneProvider';
16
16
  import { ChannelActionProvider, ChannelStateProvider, TypingProvider, useChatContext, useTranslationContext, WithComponents, } from '../../context';
17
+ import { CHANNEL_CONTAINER_ID } from './constants';
17
18
  import { DEFAULT_HIGHLIGHT_DURATION, DEFAULT_INITIAL_CHANNEL_PAGE_SIZE, DEFAULT_JUMP_TO_PAGE_SIZE, DEFAULT_NEXT_CHANNEL_PAGE_SIZE, DEFAULT_THREAD_PAGE_SIZE, } from '../../constants/limits';
18
19
  import { hasMoreMessagesProbably } from '../MessageList';
19
20
  import { getChatContainerClass, useChannelContainerClasses, useImageFlagEmojisOnWindowsClass, } from './hooks/useChannelContainerClasses';
20
21
  import { findInMsgSetByDate, findInMsgSetById, makeAddNotifications } from './utils';
22
+ import { useThreadContext } from '../Threads';
21
23
  import { getChannel } from '../../utils';
22
24
  import { getImageAttachmentConfiguration, getVideoAttachmentConfiguration, } from '../Attachment/attachment-sizing';
23
- import { useThreadContext } from '../Threads';
24
- import { CHANNEL_CONTAINER_ID } from './constants';
25
+ import { useSearchFocusedMessage } from '../../experimental/Search/hooks';
25
26
  const isUserResponseArray = (output) => output[0]?.id != null;
26
27
  const ChannelContainer = ({ children, className: additionalClassName, ...props }) => {
27
28
  const { customClasses, theme } = useChatContext('Channel');
@@ -53,7 +54,7 @@ const ChannelInner = (props) => {
53
54
  const channelQueryOptions = useMemo(() => defaultsDeep(propChannelQueryOptions, {
54
55
  messages: { limit: DEFAULT_INITIAL_CHANNEL_PAGE_SIZE },
55
56
  }), [propChannelQueryOptions]);
56
- const { client, customClasses, latestMessageDatesByChannels, mutes } = useChatContext('Channel');
57
+ const { client, customClasses, latestMessageDatesByChannels, mutes, searchController } = useChatContext('Channel');
57
58
  const { t } = useTranslationContext('Channel');
58
59
  const chatContainerClass = getChatContainerClass(customClasses?.chatContainer);
59
60
  const windowsEmojiClass = useImageFlagEmojisOnWindowsClass();
@@ -72,10 +73,12 @@ const ChannelInner = (props) => {
72
73
  hasMore: channel.state.messagePagination.hasPrev,
73
74
  loading: !channel.initialized,
74
75
  });
76
+ const jumpToMessageFromSearch = useSearchFocusedMessage();
75
77
  const isMounted = useIsMounted();
76
78
  const originalTitle = useRef('');
77
79
  const lastRead = useRef(undefined);
78
80
  const online = useRef(true);
81
+ const clearHighlightedMessageTimeoutId = useRef(null);
79
82
  const channelCapabilitiesArray = channel.data?.own_capabilities;
80
83
  const throttledCopyStateFromChannel = throttle(() => dispatch({ channel, type: 'copyStateFromChannelOnEvent' }), 500, {
81
84
  leading: true,
@@ -282,6 +285,28 @@ const ChannelInner = (props) => {
282
285
  if (message)
283
286
  dispatch({ message, type: 'setThread' });
284
287
  }, [state.messages, state.thread]);
288
+ const handleHighlightedMessageChange = useCallback(({ highlightDuration, highlightedMessageId, }) => {
289
+ dispatch({
290
+ channel,
291
+ highlightedMessageId,
292
+ type: 'jumpToMessageFinished',
293
+ });
294
+ if (clearHighlightedMessageTimeoutId.current) {
295
+ clearTimeout(clearHighlightedMessageTimeoutId.current);
296
+ }
297
+ clearHighlightedMessageTimeoutId.current = setTimeout(() => {
298
+ if (searchController._internalState.getLatestValue().focusedMessage) {
299
+ searchController._internalState.partialNext({ focusedMessage: undefined });
300
+ }
301
+ clearHighlightedMessageTimeoutId.current = null;
302
+ dispatch({ type: 'clearHighlightedMessage' });
303
+ }, highlightDuration ?? DEFAULT_HIGHLIGHT_DURATION);
304
+ }, [channel, searchController]);
305
+ useEffect(() => {
306
+ if (!jumpToMessageFromSearch?.id)
307
+ return;
308
+ handleHighlightedMessageChange({ highlightedMessageId: jumpToMessageFromSearch.id });
309
+ }, [jumpToMessageFromSearch, handleHighlightedMessageChange]);
285
310
  /** MESSAGE */
286
311
  // Adds a temporary notification to message list, will be removed after 5 seconds
287
312
  const addNotification = useMemo(() => makeAddNotifications(setNotifications, notificationTimeouts.current), []);
@@ -351,24 +376,15 @@ const ChannelInner = (props) => {
351
376
  });
352
377
  return queryResponse.messages.length;
353
378
  };
354
- const clearHighlightedMessageTimeoutId = useRef(null);
355
379
  const jumpToMessage = useCallback(async (messageId, messageLimit = DEFAULT_JUMP_TO_PAGE_SIZE, highlightDuration = DEFAULT_HIGHLIGHT_DURATION) => {
356
380
  dispatch({ loadingMore: true, type: 'setLoadingMore' });
357
381
  await channel.state.loadMessageIntoState(messageId, undefined, messageLimit);
358
382
  loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
359
- dispatch({
360
- hasMoreNewer: channel.state.messagePagination.hasNext,
383
+ handleHighlightedMessageChange({
384
+ highlightDuration,
361
385
  highlightedMessageId: messageId,
362
- type: 'jumpToMessageFinished',
363
386
  });
364
- if (clearHighlightedMessageTimeoutId.current) {
365
- clearTimeout(clearHighlightedMessageTimeoutId.current);
366
- }
367
- clearHighlightedMessageTimeoutId.current = setTimeout(() => {
368
- clearHighlightedMessageTimeoutId.current = null;
369
- dispatch({ type: 'clearHighlightedMessage' });
370
- }, highlightDuration);
371
- }, [channel, loadMoreFinished]);
387
+ }, [channel, handleHighlightedMessageChange, loadMoreFinished]);
372
388
  const jumpToLatestMessage = useCallback(async () => {
373
389
  await channel.state.loadMessageIntoState('latest');
374
390
  loadMoreFinished(channel.state.messagePagination.hasPrev, channel.state.messages);
@@ -468,19 +484,18 @@ const ChannelInner = (props) => {
468
484
  first_unread_message_id: firstUnreadMessageId,
469
485
  last_read_message_id: lastReadMessageId,
470
486
  });
471
- dispatch({
472
- hasMoreNewer: channel.state.messagePagination.hasNext,
487
+ handleHighlightedMessageChange({
488
+ highlightDuration,
473
489
  highlightedMessageId: firstUnreadMessageId,
474
- type: 'jumpToMessageFinished',
475
490
  });
476
- if (clearHighlightedMessageTimeoutId.current) {
477
- clearTimeout(clearHighlightedMessageTimeoutId.current);
478
- }
479
- clearHighlightedMessageTimeoutId.current = setTimeout(() => {
480
- clearHighlightedMessageTimeoutId.current = null;
481
- dispatch({ type: 'clearHighlightedMessage' });
482
- }, highlightDuration);
483
- }, [addNotification, channel, loadMoreFinished, t, channelUnreadUiState]);
491
+ }, [
492
+ addNotification,
493
+ channel,
494
+ handleHighlightedMessageChange,
495
+ loadMoreFinished,
496
+ t,
497
+ channelUnreadUiState,
498
+ ]);
484
499
  const deleteMessage = useCallback(async (message) => {
485
500
  if (!message?.id) {
486
501
  throw new Error('Cannot delete a message - missing message ID.');
@@ -13,7 +13,7 @@ export type ChannelStateReducerAction<StreamChatGenerics extends DefaultStreamCh
13
13
  channel: Channel<StreamChatGenerics>;
14
14
  type: 'copyStateFromChannelOnEvent';
15
15
  } | {
16
- hasMoreNewer: boolean;
16
+ channel: Channel<StreamChatGenerics>;
17
17
  highlightedMessageId: string;
18
18
  type: 'jumpToMessageFinished';
19
19
  } | {
@@ -59,8 +59,9 @@ export const makeChannelReducer = () => (state, action) => {
59
59
  case 'jumpToMessageFinished': {
60
60
  return {
61
61
  ...state,
62
- hasMoreNewer: action.hasMoreNewer,
62
+ hasMoreNewer: action.channel.state.messagePagination.hasNext,
63
63
  highlightedMessageId: action.highlightedMessageId,
64
+ messages: action.channel.state.messages,
64
65
  };
65
66
  }
66
67
  case 'clearHighlightedMessage': {
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
2
  import { ChannelListMessengerProps } from './ChannelListMessenger';
3
3
  import { CustomQueryChannelsFn } from './hooks/usePaginatedChannels';
4
4
  import { ChannelPreviewUIComponentProps } from '../ChannelPreview/ChannelPreview';
@@ -35,7 +35,7 @@ export type ChannelListProps<StreamChatGenerics extends DefaultStreamChatGeneric
35
35
  /** An object containing channel query filters */
36
36
  filters?: ChannelFilters<StreamChatGenerics>;
37
37
  /** Custom function that generates the message preview in ChannelPreview component */
38
- getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue['isMessageAIGenerated']) => string | JSX.Element;
38
+ getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue['isMessageAIGenerated']) => ReactNode;
39
39
  /** Custom UI component to display the container for the queried channels, defaults to and accepts same props as: [ChannelListMessenger](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListMessenger.tsx) */
40
40
  List?: React.ComponentType<ChannelListMessengerProps<StreamChatGenerics>>;
41
41
  /** Custom UI component to display the loading error indicator, defaults to component that renders null */
@@ -4,6 +4,7 @@ import { ChannelListMessenger } from './ChannelListMessenger';
4
4
  import { useConnectionRecoveredListener } from './hooks/useConnectionRecoveredListener';
5
5
  import { useMobileNavigation } from './hooks/useMobileNavigation';
6
6
  import { usePaginatedChannels, } from './hooks/usePaginatedChannels';
7
+ import { useChannelListShape, usePrepareShapeHandlers, } from './hooks/useChannelListShape';
7
8
  import { MAX_QUERY_CHANNELS_LIMIT, moveChannelUpwards } from './utils';
8
9
  import { Avatar as DefaultAvatar } from '../Avatar';
9
10
  import { ChannelPreview, } from '../ChannelPreview/ChannelPreview';
@@ -12,18 +13,24 @@ import { EmptyStateIndicator as DefaultEmptyStateIndicator, } from '../EmptyStat
12
13
  import { LoadingChannels } from '../Loading/LoadingChannels';
13
14
  import { LoadMorePaginator } from '../LoadMore/LoadMorePaginator';
14
15
  import { NullComponent } from '../UtilityComponents';
15
- import { ChannelListContextProvider } from '../../context';
16
+ import { ChannelListContextProvider, useComponentContext, } from '../../context';
16
17
  import { useChatContext } from '../../context/ChatContext';
17
- import { useChannelListShape, usePrepareShapeHandlers, } from './hooks/useChannelListShape';
18
+ import { useStateStore } from '../../store';
18
19
  const DEFAULT_FILTERS = {};
19
20
  const DEFAULT_OPTIONS = {};
20
21
  const DEFAULT_SORT = {};
22
+ const searchControllerStateSelector = (nextValue) => ({
23
+ searchIsActive: nextValue.isActive,
24
+ });
21
25
  const UnMemoizedChannelList = (props) => {
22
26
  const { additionalChannelSearchProps, allowNewMessagesFromUnfilteredChannels = true, Avatar = DefaultAvatar, channelRenderFilterFn, ChannelSearch = DefaultChannelSearch, customActiveChannel, customQueryChannels, EmptyStateIndicator = DefaultEmptyStateIndicator, filters = {}, getLatestMessagePreview, List = ChannelListMessenger, LoadingErrorIndicator = NullComponent, LoadingIndicator = LoadingChannels, lockChannelOrder = false, onAddedToChannel, onChannelDeleted, onChannelHidden, onChannelTruncated, onChannelUpdated, onChannelVisible, onMessageNew, onMessageNewHandler, onRemovedFromChannel, options, Paginator = LoadMorePaginator, Preview, recoveryThrottleIntervalMs, renderChannels, sendChannelsToList = false, setActiveChannelOnMount = true, showChannelSearch = false, sort = DEFAULT_SORT, watchers = {}, } = props;
23
- const { channel, channelsQueryState, client, closeMobileNav, customClasses, navOpen = false, setActiveChannel, theme, useImageFlagEmojisOnWindows, } = useChatContext('ChannelList');
27
+ const { channel, channelsQueryState, client, closeMobileNav, customClasses, navOpen = false, searchController, setActiveChannel, theme, useImageFlagEmojisOnWindows, } = useChatContext('ChannelList');
28
+ const { Search } = useComponentContext(); // FIXME: us component context to retrieve ChannelPreview UI components too
24
29
  const channelListRef = useRef(null);
25
30
  const [channelUpdateCount, setChannelUpdateCount] = useState(0);
26
31
  const [searchActive, setSearchActive] = useState(false);
32
+ // Indicator relevant when Search component that relies on SearchController is used
33
+ const { searchIsActive } = useStateStore(searchController.state, searchControllerStateSelector);
27
34
  /**
28
35
  * Set a channel with id {customActiveChannel} as active and move it to the top of the list.
29
36
  * If customActiveChannel prop is absent, then set the first channel in list as active channel.
@@ -65,13 +72,11 @@ const UnMemoizedChannelList = (props) => {
65
72
  const onSearch = useCallback((event) => {
66
73
  setSearchActive(!!event.target.value);
67
74
  additionalChannelSearchProps?.onSearch?.(event);
68
- // eslint-disable-next-line react-hooks/exhaustive-deps
69
- }, []);
75
+ }, [additionalChannelSearchProps]);
70
76
  const onSearchExit = useCallback(() => {
71
77
  setSearchActive(false);
72
78
  additionalChannelSearchProps?.onSearchExit?.();
73
- // eslint-disable-next-line react-hooks/exhaustive-deps
74
- }, []);
79
+ }, [additionalChannelSearchProps]);
75
80
  const { channels, hasNextPage, loadNextPage, setChannels } = usePaginatedChannels(client, filters || DEFAULT_FILTERS, sort || DEFAULT_SORT, options || DEFAULT_OPTIONS, activeChannelHandler, recoveryThrottleIntervalMs, customQueryChannels);
76
81
  const loadedChannels = channelRenderFilterFn
77
82
  ? channelRenderFilterFn(channels)
@@ -132,10 +137,11 @@ const UnMemoizedChannelList = (props) => {
132
137
  'str-chat--windows-flags': useImageFlagEmojisOnWindows && navigator.userAgent.match(/Win/),
133
138
  [`${baseClass}--open`]: navOpen,
134
139
  });
135
- const showChannelList = !searchActive || additionalChannelSearchProps?.popupResults;
140
+ const showChannelList = (!searchActive && !searchIsActive) || additionalChannelSearchProps?.popupResults;
136
141
  return (React.createElement(ChannelListContextProvider, { value: { channels, setChannels } },
137
142
  React.createElement("div", { className: className, ref: channelListRef },
138
- showChannelSearch && (React.createElement(ChannelSearch, { onSearch: onSearch, onSearchExit: onSearchExit, setChannels: setChannels, ...additionalChannelSearchProps })),
143
+ showChannelSearch &&
144
+ (Search ? (React.createElement(Search, { directMessagingChannelType: additionalChannelSearchProps?.channelType, disabled: additionalChannelSearchProps?.disabled, exitSearchOnInputBlur: additionalChannelSearchProps?.clearSearchOnClickOutside, placeholder: additionalChannelSearchProps?.placeholder })) : (React.createElement(ChannelSearch, { onSearch: onSearch, onSearchExit: onSearchExit, setChannels: setChannels, ...additionalChannelSearchProps }))),
139
145
  showChannelList && (React.createElement(List, { error: channelsQueryState.error, loadedChannels: sendChannelsToList ? loadedChannels : undefined, loading: !!channelsQueryState.queryInProgress &&
140
146
  ['reload', 'uninitialized'].includes(channelsQueryState.queryInProgress), LoadingErrorIndicator: LoadingErrorIndicator, LoadingIndicator: LoadingIndicator, setChannels: setChannels }, !loadedChannels?.length ? (React.createElement(EmptyStateIndicator, { listType: 'channel' })) : (React.createElement(Paginator, { hasNextPage: hasNextPage, isLoading: channelsQueryState.queryInProgress === 'load-more', loadNextPage: loadNextPage }, renderChannels
141
147
  ? renderChannels(loadedChannels, renderChannel)
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
2
  import { ChatContextValue } from '../../context/ChatContext';
3
3
  import { MessageDeliveryStatus } from './hooks/useMessageDeliveryStatus';
4
4
  import type { Channel } from 'stream-chat';
@@ -8,8 +8,6 @@ import type { StreamMessage } from '../../context/ChannelStateContext';
8
8
  import type { TranslationContextValue } from '../../context/TranslationContext';
9
9
  import type { DefaultStreamChatGenerics } from '../../types/types';
10
10
  export type ChannelPreviewUIComponentProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = ChannelPreviewProps<StreamChatGenerics> & {
11
- /** If the component's channel is the active (selected) Channel */
12
- active?: boolean;
13
11
  /** Image of Channel to display */
14
12
  displayImage?: string;
15
13
  /** Title of Channel to display */
@@ -19,9 +17,9 @@ export type ChannelPreviewUIComponentProps<StreamChatGenerics extends DefaultStr
19
17
  /** The last message received in a channel */
20
18
  lastMessage?: StreamMessage<StreamChatGenerics>;
21
19
  /** @deprecated Use latestMessagePreview prop instead. */
22
- latestMessage?: string | JSX.Element;
20
+ latestMessage?: ReactNode;
23
21
  /** Latest message preview to display, will be a string or JSX element supporting markdown. */
24
- latestMessagePreview?: string | JSX.Element;
22
+ latestMessagePreview?: ReactNode;
25
23
  /** Status describing whether own message has been delivered or read by another. If the last message is not an own message, then the status is undefined. */
26
24
  messageDeliveryStatus?: MessageDeliveryStatus;
27
25
  /** Number of unread Messages */
@@ -30,6 +28,8 @@ export type ChannelPreviewUIComponentProps<StreamChatGenerics extends DefaultStr
30
28
  export type ChannelPreviewProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
31
29
  /** Comes from either the `channelRenderFilterFn` or `usePaginatedChannels` call from [ChannelList](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelList.tsx) */
32
30
  channel: Channel<StreamChatGenerics>;
31
+ /** If the component's channel is the active (selected) Channel */
32
+ active?: boolean;
33
33
  /** Current selected channel object */
34
34
  activeChannel?: Channel<StreamChatGenerics>;
35
35
  /** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */
@@ -39,7 +39,7 @@ export type ChannelPreviewProps<StreamChatGenerics extends DefaultStreamChatGene
39
39
  /** Custom class for the channel preview root */
40
40
  className?: string;
41
41
  /** Custom function that generates the message preview in ChannelPreview component */
42
- getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string | JSX.Element;
42
+ getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => ReactNode;
43
43
  key?: string;
44
44
  /** Custom ChannelPreview click handler function */
45
45
  onSelect?: (event: React.MouseEvent) => void;
@@ -8,7 +8,7 @@ import { useChatContext } from '../../context/ChatContext';
8
8
  import { useTranslationContext } from '../../context/TranslationContext';
9
9
  import { useMessageDeliveryStatus, } from './hooks/useMessageDeliveryStatus';
10
10
  export const ChannelPreview = (props) => {
11
- const { channel, channelUpdateCount, getLatestMessagePreview = defaultGetLatestMessagePreview, Preview = ChannelPreviewMessenger, } = props;
11
+ const { active, channel, channelUpdateCount, getLatestMessagePreview = defaultGetLatestMessagePreview, Preview = ChannelPreviewMessenger, } = props;
12
12
  const { channel: activeChannel, client, isMessageAIGenerated, setActiveChannel, } = useChatContext('ChannelPreview');
13
13
  const { t, userLanguage } = useTranslationContext('ChannelPreview');
14
14
  const { displayImage, displayTitle, groupChannelDisplayInfo } = useChannelPreviewInfo({
@@ -20,7 +20,7 @@ export const ChannelPreview = (props) => {
20
20
  channel,
21
21
  lastMessage,
22
22
  });
23
- const isActive = activeChannel?.cid === channel.cid;
23
+ const isActive = typeof active === 'undefined' ? activeChannel?.cid === channel.cid : active;
24
24
  const { muted } = useIsChannelMuted(channel);
25
25
  useEffect(() => {
26
26
  const handleEvent = (event) => {
@@ -31,8 +31,7 @@ export const ChannelPreview = (props) => {
31
31
  };
32
32
  client.on('notification.mark_read', handleEvent);
33
33
  return () => client.off('notification.mark_read', handleEvent);
34
- // eslint-disable-next-line react-hooks/exhaustive-deps
35
- }, []);
34
+ }, [channel, client]);
36
35
  useEffect(() => {
37
36
  const handleEvent = (event) => {
38
37
  if (channel.cid !== event.cid)
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import type { DefaultStreamChatGenerics } from '../../types/types';
3
2
  import type { ChannelPreviewUIComponentProps } from './ChannelPreview';
3
+ import type { DefaultStreamChatGenerics } from '../../types/types';
4
4
  /**
5
5
  * Used as preview component for channel item in [ChannelList](#channellist) component.
6
6
  * Its best suited for messenger type chat.
@@ -1,10 +1,10 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
2
  import type { Channel, UserResponse } from 'stream-chat';
3
3
  import type { TranslationContextValue } from '../../context/TranslationContext';
4
4
  import type { DefaultStreamChatGenerics } from '../../types/types';
5
5
  import { ChatContextValue } from '../../context';
6
6
  export declare const renderPreviewText: (text: string) => React.JSX.Element;
7
- export declare const getLatestMessagePreview: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage?: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue<StreamChatGenerics>['isMessageAIGenerated']) => string | JSX.Element;
7
+ export declare const getLatestMessagePreview: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage?: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue<StreamChatGenerics>['isMessageAIGenerated']) => ReactNode;
8
8
  export type GroupChannelDisplayInfo = {
9
9
  image?: string;
10
10
  name?: string;
@@ -33,25 +33,24 @@ export const SearchBar = (props) => {
33
33
  useEffect(() => {
34
34
  if (!props.inputRef.current)
35
35
  return;
36
+ const input = props.inputRef.current;
36
37
  const handleFocus = () => {
37
38
  activateSearch();
38
39
  };
39
40
  const handleBlur = (e) => {
40
41
  e.stopPropagation(); // handle blur/focus state with React state
41
42
  };
42
- props.inputRef.current.addEventListener('focus', handleFocus);
43
- props.inputRef.current.addEventListener('blur', handleBlur);
43
+ input.addEventListener('focus', handleFocus);
44
+ input.addEventListener('blur', handleBlur);
44
45
  return () => {
45
- props.inputRef.current?.removeEventListener('focus', handleFocus);
46
- props.inputRef.current?.addEventListener('blur', handleBlur);
46
+ input.removeEventListener('focus', handleFocus);
47
+ input.removeEventListener('blur', handleBlur);
47
48
  };
48
- // eslint-disable-next-line react-hooks/exhaustive-deps
49
- }, []);
49
+ }, [activateSearch, props.inputRef]);
50
50
  const handleClearClick = useCallback(() => {
51
51
  exitSearch();
52
52
  inputProps.inputRef.current?.focus();
53
- // eslint-disable-next-line react-hooks/exhaustive-deps
54
- }, []);
53
+ }, [exitSearch, inputProps.inputRef]);
55
54
  const closeAppMenu = useCallback(() => setMenuIsOpen(false), []);
56
55
  return (React.createElement("div", { className: 'str-chat__channel-search-bar', "data-testid": 'search-bar', ref: searchBarRef },
57
56
  inputIsFocused ? (React.createElement(SearchBarButton, { className: 'str-chat__channel-search-bar-button--exit-search', onClick: exitSearch },
@@ -60,14 +60,15 @@ export const SearchResults = (props) => {
60
60
  }
61
61
  if (event.key === 'Enter') {
62
62
  event.preventDefault();
63
- if (focusedResult !== undefined) {
64
- selectResult(results[focusedResult]);
65
- return setFocusedResult(undefined);
66
- }
63
+ setFocusedResult((prevFocused) => {
64
+ if (typeof prevFocused !== 'undefined') {
65
+ selectResult(results[prevFocused]);
66
+ return undefined;
67
+ }
68
+ return prevFocused;
69
+ });
67
70
  }
68
- },
69
- // eslint-disable-next-line react-hooks/exhaustive-deps
70
- [focusedResult]);
71
+ }, [results, selectResult]);
71
72
  useEffect(() => {
72
73
  document.addEventListener('keydown', handleKeyDown, false);
73
74
  return () => document.removeEventListener('keydown', handleKeyDown);
@@ -1,10 +1,11 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
- import { CustomClasses } from '../../context/ChatContext';
3
2
  import type { StreamChat } from 'stream-chat';
3
+ import { SearchController } from 'stream-chat';
4
+ import { CustomClasses } from '../../context/ChatContext';
5
+ import type { MessageContextValue } from '../../context';
4
6
  import type { SupportedTranslations } from '../../i18n/types';
5
7
  import type { Streami18n } from '../../i18n/Streami18n';
6
8
  import type { DefaultStreamChatGenerics } from '../../types/types';
7
- import type { MessageContextValue } from '../../context';
8
9
  export type ChatProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
9
10
  /** The StreamChat client object */
10
11
  client: StreamChat<StreamChatGenerics>;
@@ -16,6 +17,8 @@ export type ChatProps<StreamChatGenerics extends DefaultStreamChatGenerics = Def
16
17
  i18nInstance?: Streami18n;
17
18
  /** Initial status of mobile navigation */
18
19
  initialNavOpen?: boolean;
20
+ /** Instance of SearchController class that allows to control all the search operations. */
21
+ searchController?: SearchController<StreamChatGenerics>;
19
22
  /** Used for injecting className/s to the Channel and ChannelList components */
20
23
  theme?: string;
21
24
  /**
@@ -1,4 +1,5 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
+ import { ChannelSearchSource, MessageSearchSource, SearchController, UserSearchSource, } from 'stream-chat';
2
3
  import { useChat } from './hooks/useChat';
3
4
  import { useCreateChatContext } from './hooks/useCreateChatContext';
4
5
  import { useChannelsQueryState } from './hooks/useChannelsQueryState';
@@ -9,9 +10,17 @@ import { TranslationProvider } from '../../context/TranslationContext';
9
10
  * as it provides the ChatContext.
10
11
  */
11
12
  export const Chat = (props) => {
12
- const { children, client, customClasses, defaultLanguage, i18nInstance, initialNavOpen = true, isMessageAIGenerated, theme = 'messaging light', useImageFlagEmojisOnWindows = false, } = props;
13
+ const { children, client, customClasses, defaultLanguage, i18nInstance, initialNavOpen = true, isMessageAIGenerated, searchController: customChannelSearchController, theme = 'messaging light', useImageFlagEmojisOnWindows = false, } = props;
13
14
  const { channel, closeMobileNav, getAppSettings, latestMessageDatesByChannels, mutes, navOpen, openMobileNav, setActiveChannel, translators, } = useChat({ client, defaultLanguage, i18nInstance, initialNavOpen });
14
15
  const channelsQueryState = useChannelsQueryState();
16
+ const searchController = useMemo(() => customChannelSearchController ??
17
+ new SearchController({
18
+ sources: [
19
+ new ChannelSearchSource(client),
20
+ new UserSearchSource(client),
21
+ new MessageSearchSource(client),
22
+ ],
23
+ }), [client, customChannelSearchController]);
15
24
  const chatContextValue = useCreateChatContext({
16
25
  channel,
17
26
  channelsQueryState,
@@ -24,6 +33,7 @@ export const Chat = (props) => {
24
33
  mutes,
25
34
  navOpen,
26
35
  openMobileNav,
36
+ searchController,
27
37
  setActiveChannel,
28
38
  theme,
29
39
  useImageFlagEmojisOnWindows,
@@ -28,7 +28,7 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
28
28
  if (!userAgent.includes('stream-chat-react')) {
29
29
  // result looks like: 'stream-chat-react-2.3.2-stream-chat-javascript-client-browser-2.2.2'
30
30
  // the upper-case text between double underscores is replaced with the actual semantic version of the library
31
- client.setUserAgent(`stream-chat-react-12.11.1-${userAgent}`);
31
+ client.setUserAgent(`stream-chat-react-12.13.0-${userAgent}`);
32
32
  }
33
33
  client.threads.registerSubscriptions();
34
34
  client.polls.registerSubscriptions();
@@ -1,6 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  export const useCreateChatContext = (value) => {
3
- const { channel, channelsQueryState, client, closeMobileNav, customClasses, getAppSettings, isMessageAIGenerated, latestMessageDatesByChannels, mutes, navOpen, openMobileNav, setActiveChannel, theme, useImageFlagEmojisOnWindows, } = value;
3
+ const { channel, channelsQueryState, client, closeMobileNav, customClasses, getAppSettings, isMessageAIGenerated, latestMessageDatesByChannels, mutes, navOpen, openMobileNav, searchController, setActiveChannel, theme, useImageFlagEmojisOnWindows, } = value;
4
4
  const channelCid = channel?.cid;
5
5
  const channelsQueryError = channelsQueryState.error;
6
6
  const channelsQueryInProgress = channelsQueryState.queryInProgress;
@@ -19,6 +19,7 @@ export const useCreateChatContext = (value) => {
19
19
  mutes,
20
20
  navOpen,
21
21
  openMobileNav,
22
+ searchController,
22
23
  setActiveChannel,
23
24
  theme,
24
25
  useImageFlagEmojisOnWindows,
@@ -30,6 +31,7 @@ export const useCreateChatContext = (value) => {
30
31
  channelsQueryInProgress,
31
32
  clientValues,
32
33
  getAppSettings,
34
+ searchController,
33
35
  mutedUsersLength,
34
36
  navOpen,
35
37
  isMessageAIGenerated,
@@ -34,7 +34,7 @@ export type InfiniteScrollProps = PaginatorProps & {
34
34
  /**
35
35
  * This component serves a single purpose - load more items on scroll inside the MessageList component
36
36
  * It is not a general purpose infinite scroll controller, because:
37
- * 1. It is re-rendered whenever isLoading, hasNext, hasPrev changes. This can lead to scrollListener to have stale data.
37
+ * 1. It is re-rendered whenever queryInProgress, hasNext, hasPrev changes. This can lead to scrollListener to have stale data.
38
38
  * 2. It pretends to invoke scrollListener on resize event even though this event is emitted only on window resize. It should
39
39
  * rather use ResizeObserver. But then again, it ResizeObserver would invoke a stale version of scrollListener.
40
40
  *
@@ -13,7 +13,7 @@ const mousewheelListener = (event) => {
13
13
  /**
14
14
  * This component serves a single purpose - load more items on scroll inside the MessageList component
15
15
  * It is not a general purpose infinite scroll controller, because:
16
- * 1. It is re-rendered whenever isLoading, hasNext, hasPrev changes. This can lead to scrollListener to have stale data.
16
+ * 1. It is re-rendered whenever queryInProgress, hasNext, hasPrev changes. This can lead to scrollListener to have stale data.
17
17
  * 2. It pretends to invoke scrollListener on resize event even though this event is emitted only on window resize. It should
18
18
  * rather use ResizeObserver. But then again, it ResizeObserver would invoke a stale version of scrollListener.
19
19
  *
@@ -1,6 +1,7 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
2
  export type InfiniteScrollPaginatorProps = React.ComponentProps<'div'> & {
3
3
  listenToScroll?: (distanceFromBottom: number, distanceFromTop: number, threshold: number) => void;
4
+ loadNextDebounceMs?: number;
4
5
  loadNextOnScrollToBottom?: () => void;
5
6
  loadNextOnScrollToTop?: () => void;
6
7
  /** Offset from when to start the loadNextPage call */
@@ -12,7 +12,7 @@ const mousewheelListener = (event) => {
12
12
  }
13
13
  };
14
14
  export const InfiniteScrollPaginator = (props) => {
15
- const { children, className, listenToScroll, loadNextOnScrollToBottom, loadNextOnScrollToTop, threshold = DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, useCapture = false, ...componentProps } = props;
15
+ const { children, className, listenToScroll, loadNextDebounceMs = 500, loadNextOnScrollToBottom, loadNextOnScrollToTop, threshold = DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, useCapture = false, ...componentProps } = props;
16
16
  const rootRef = useRef(null);
17
17
  const childRef = useRef(null);
18
18
  const scrollListener = useMemo(() => debounce(() => {
@@ -32,7 +32,13 @@ export const InfiniteScrollPaginator = (props) => {
32
32
  if (distanceFromBottom < Number(threshold)) {
33
33
  loadNextOnScrollToBottom?.();
34
34
  }
35
- }, 500), [listenToScroll, loadNextOnScrollToBottom, loadNextOnScrollToTop, threshold]);
35
+ }, loadNextDebounceMs), [
36
+ listenToScroll,
37
+ loadNextDebounceMs,
38
+ loadNextOnScrollToBottom,
39
+ loadNextOnScrollToTop,
40
+ threshold,
41
+ ]);
36
42
  useEffect(() => {
37
43
  const scrollElement = rootRef.current;
38
44
  if (!scrollElement)
@@ -6,7 +6,12 @@ import { RecordedMediaType } from '../../ReactFileUtilities';
6
6
  import { TranslationContextValue } from '../../../context';
7
7
  import type { LocalVoiceRecordingAttachment } from '../../MessageInput';
8
8
  import type { DefaultStreamChatGenerics } from '../../../types';
9
- export declare const DEFAULT_MEDIA_RECORDER_CONFIG: MediaRecorderConfig;
9
+ export declare const RECORDED_MIME_TYPE_BY_BROWSER: {
10
+ readonly audio: {
11
+ readonly others: "audio/webm";
12
+ readonly safari: "audio/mp4;codecs=mp4a.40.2";
13
+ };
14
+ };
10
15
  export declare const DEFAULT_AUDIO_TRANSCODER_CONFIG: TranscoderConfig;
11
16
  type MediaRecorderConfig = Omit<MediaRecorderOptions, 'mimeType'> & Required<Pick<MediaRecorderOptions, 'mimeType'>>;
12
17
  export type AudioRecorderConfig = {
@@ -7,19 +7,13 @@ import { transcode } from '../transcode';
7
7
  import { resampleWaveformData } from '../../Attachment';
8
8
  import { createFileFromBlobs, getExtensionFromMimeType, getRecordedMediaTypeFromMimeType, } from '../../ReactFileUtilities';
9
9
  import { defaultTranslatorFunction } from '../../../i18n';
10
- import { isSafari } from '../../../utils/browsers';
11
10
  import { mergeDeepUndefined } from '../../../utils/mergeDeep';
12
- const RECORDED_MIME_TYPE_BY_BROWSER = {
11
+ export const RECORDED_MIME_TYPE_BY_BROWSER = {
13
12
  audio: {
14
13
  others: 'audio/webm',
15
14
  safari: 'audio/mp4;codecs=mp4a.40.2',
16
15
  },
17
16
  };
18
- export const DEFAULT_MEDIA_RECORDER_CONFIG = {
19
- mimeType: isSafari()
20
- ? RECORDED_MIME_TYPE_BY_BROWSER.audio.safari
21
- : RECORDED_MIME_TYPE_BY_BROWSER.audio.others,
22
- };
23
17
  export const DEFAULT_AUDIO_TRANSCODER_CONFIG = {
24
18
  sampleRate: 16000,
25
19
  };
@@ -241,7 +235,11 @@ export class MediaRecorderController {
241
235
  };
242
236
  this.t = t || defaultTranslatorFunction;
243
237
  this.amplitudeRecorderConfig = mergeDeepUndefined({ ...config?.amplitudeRecorderConfig }, DEFAULT_AMPLITUDE_RECORDER_CONFIG);
244
- this.mediaRecorderConfig = mergeDeepUndefined({ ...config?.mediaRecorderConfig }, DEFAULT_MEDIA_RECORDER_CONFIG);
238
+ this.mediaRecorderConfig = mergeDeepUndefined({ ...config?.mediaRecorderConfig }, {
239
+ mimeType: MediaRecorder.isTypeSupported('audio/webm')
240
+ ? RECORDED_MIME_TYPE_BY_BROWSER.audio.others
241
+ : RECORDED_MIME_TYPE_BY_BROWSER.audio.safari,
242
+ });
245
243
  this.transcoderConfig = mergeDeepUndefined({ ...config?.transcoderConfig }, DEFAULT_AUDIO_TRANSCODER_CONFIG);
246
244
  const mediaType = getRecordedMediaTypeFromMimeType(this.mediaRecorderConfig.mimeType);
247
245
  if (!mediaType) {
@@ -1,3 +1,5 @@
1
1
  import React from 'react';
2
+ import type { MessageContextValue } from '../../context/MessageContext';
3
+ export type QuotedMessageProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Pick<MessageContextValue<StreamChatGenerics>, 'renderText'>;
2
4
  import type { DefaultStreamChatGenerics } from '../../types/types';
3
- export declare const QuotedMessage: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => React.JSX.Element | null;
5
+ export declare const QuotedMessage: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ renderText: propsRenderText, }: QuotedMessageProps) => React.JSX.Element | null;