stream-chat-react 12.1.0 → 12.2.1

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 (27) hide show
  1. package/dist/components/ChannelList/ChannelList.d.ts +3 -0
  2. package/dist/components/ChannelList/ChannelList.js +5 -2
  3. package/dist/components/ChannelList/hooks/useChannelDeletedListener.js +2 -3
  4. package/dist/components/ChannelList/hooks/useChannelHiddenListener.js +1 -2
  5. package/dist/components/ChannelList/hooks/useChannelTruncatedListener.js +1 -2
  6. package/dist/components/ChannelList/hooks/useChannelUpdatedListener.js +1 -2
  7. package/dist/components/ChannelList/hooks/useChannelVisibleListener.js +1 -2
  8. package/dist/components/ChannelList/hooks/useConnectionRecoveredListener.js +1 -2
  9. package/dist/components/ChannelList/hooks/useMessageNewListener.js +7 -2
  10. package/dist/components/ChannelList/hooks/useNotificationAddedToChannelListener.js +1 -2
  11. package/dist/components/ChannelList/hooks/useNotificationMessageNewListener.js +1 -2
  12. package/dist/components/ChannelList/hooks/useNotificationRemovedFromChannelListener.js +1 -2
  13. package/dist/components/ChannelList/hooks/useUserPresenceChangedListener.js +1 -2
  14. package/dist/components/ChannelPreview/ChannelPreview.d.ts +6 -1
  15. package/dist/components/ChannelPreview/ChannelPreview.js +11 -9
  16. package/dist/components/ChannelPreview/ChannelPreviewMessenger.js +2 -2
  17. package/dist/components/ChannelPreview/utils.js +2 -3
  18. package/dist/components/MessageList/VirtualizedMessageList.d.ts +2 -2
  19. package/dist/components/MessageList/VirtualizedMessageList.js +2 -1
  20. package/dist/components/MessageList/VirtualizedMessageListComponents.js +2 -2
  21. package/dist/components/MessageList/hooks/VirtualizedMessageList/useScrollToBottomOnNewMessage.d.ts +0 -1
  22. package/dist/components/MessageList/hooks/VirtualizedMessageList/useScrollToBottomOnNewMessage.js +21 -17
  23. package/dist/index.browser.cjs +80 -57
  24. package/dist/index.browser.cjs.map +3 -3
  25. package/dist/index.node.cjs +104 -81
  26. package/dist/index.node.cjs.map +3 -3
  27. package/package.json +1 -1
@@ -7,6 +7,7 @@ import { ChannelSearchProps } from '../ChannelSearch/ChannelSearch';
7
7
  import { EmptyStateIndicatorProps } from '../EmptyStateIndicator';
8
8
  import { LoadMorePaginatorProps } from '../LoadMore/LoadMorePaginator';
9
9
  import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
10
+ import type { TranslationContextValue } from '../../context/TranslationContext';
10
11
  import type { DefaultStreamChatGenerics, PaginatorProps } from '../../types/types';
11
12
  export type ChannelListProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
12
13
  /** Additional props for underlying ChannelSearch component and channel search controller, [available props](https://getstream.io/chat/docs/sdk/react/utility-components/channel_search/#props) */
@@ -32,6 +33,8 @@ export type ChannelListProps<StreamChatGenerics extends DefaultStreamChatGeneric
32
33
  EmptyStateIndicator?: React.ComponentType<EmptyStateIndicatorProps>;
33
34
  /** An object containing channel query filters */
34
35
  filters?: ChannelFilters<StreamChatGenerics>;
36
+ /** Custom function that generates the message preview in ChannelPreview component */
37
+ getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string | JSX.Element;
35
38
  /** 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) */
36
39
  List?: React.ComponentType<ChannelListMessengerProps<StreamChatGenerics>>;
37
40
  /** Custom UI component to display the loading error indicator, defaults to component that renders null */
@@ -28,7 +28,7 @@ const DEFAULT_FILTERS = {};
28
28
  const DEFAULT_OPTIONS = {};
29
29
  const DEFAULT_SORT = {};
30
30
  const UnMemoizedChannelList = (props) => {
31
- const { additionalChannelSearchProps, Avatar = DefaultAvatar, allowNewMessagesFromUnfilteredChannels, channelRenderFilterFn, ChannelSearch = DefaultChannelSearch, customActiveChannel, customQueryChannels, EmptyStateIndicator = DefaultEmptyStateIndicator, filters, LoadingErrorIndicator = NullComponent, LoadingIndicator = LoadingChannels, List = ChannelListMessenger, lockChannelOrder, 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;
31
+ const { additionalChannelSearchProps, Avatar = DefaultAvatar, allowNewMessagesFromUnfilteredChannels, channelRenderFilterFn, ChannelSearch = DefaultChannelSearch, customActiveChannel, customQueryChannels, EmptyStateIndicator = DefaultEmptyStateIndicator, filters, getLatestMessagePreview, LoadingErrorIndicator = NullComponent, LoadingIndicator = LoadingChannels, List = ChannelListMessenger, lockChannelOrder, 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;
32
32
  const { channel, channelsQueryState, client, closeMobileNav, customClasses, navOpen = false, setActiveChannel, theme, useImageFlagEmojisOnWindows, } = useChatContext('ChannelList');
33
33
  const channelListRef = useRef(null);
34
34
  const [channelUpdateCount, setChannelUpdateCount] = useState(0);
@@ -66,7 +66,9 @@ const UnMemoizedChannelList = (props) => {
66
66
  * For some events, inner properties on the channel will update but the shallow comparison will not
67
67
  * force a re-render. Incrementing this dummy variable ensures the channel previews update.
68
68
  */
69
- const forceUpdate = () => setChannelUpdateCount((count) => count + 1);
69
+ const forceUpdate = useCallback(() => setChannelUpdateCount((count) => count + 1), [
70
+ setChannelUpdateCount,
71
+ ]);
70
72
  const onSearch = useCallback((event) => {
71
73
  if (!event.target.value) {
72
74
  setSearchActive(false);
@@ -117,6 +119,7 @@ const UnMemoizedChannelList = (props) => {
117
119
  channel: item,
118
120
  // forces the update of preview component on channel update
119
121
  channelUpdateCount,
122
+ getLatestMessagePreview,
120
123
  key: item.cid,
121
124
  Preview,
122
125
  setActiveChannel,
@@ -12,7 +12,7 @@ export const useChannelDeletedListener = (setChannels, customHandler) => {
12
12
  const channelIndex = channels.findIndex((channel) => channel.cid === event.cid);
13
13
  if (channelIndex < 0)
14
14
  return [...channels];
15
- // Remove the deleted channel from the list.s
15
+ // Remove the deleted channel from the list
16
16
  channels.splice(channelIndex, 1);
17
17
  return [...channels];
18
18
  });
@@ -22,6 +22,5 @@ export const useChannelDeletedListener = (setChannels, customHandler) => {
22
22
  return () => {
23
23
  client.off('channel.deleted', handleEvent);
24
24
  };
25
- // eslint-disable-next-line react-hooks/exhaustive-deps
26
- }, [customHandler]);
25
+ }, [client, customHandler, setChannels]);
27
26
  };
@@ -22,6 +22,5 @@ export const useChannelHiddenListener = (setChannels, customHandler) => {
22
22
  return () => {
23
23
  client.off('channel.hidden', handleEvent);
24
24
  };
25
- // eslint-disable-next-line react-hooks/exhaustive-deps
26
- }, [customHandler]);
25
+ }, [client, customHandler, setChannels]);
27
26
  };
@@ -16,6 +16,5 @@ export const useChannelTruncatedListener = (setChannels, customHandler, forceUpd
16
16
  return () => {
17
17
  client.off('channel.truncated', handleEvent);
18
18
  };
19
- // eslint-disable-next-line react-hooks/exhaustive-deps
20
- }, [customHandler]);
19
+ }, [client, customHandler, forceUpdate, setChannels]);
21
20
  };
@@ -28,6 +28,5 @@ export const useChannelUpdatedListener = (setChannels, customHandler, forceUpdat
28
28
  return () => {
29
29
  client.off('channel.updated', handleEvent);
30
30
  };
31
- // eslint-disable-next-line react-hooks/exhaustive-deps
32
- }, [customHandler]);
31
+ }, [client, customHandler, forceUpdate, setChannels]);
33
32
  };
@@ -22,6 +22,5 @@ export const useChannelVisibleListener = (setChannels, customHandler) => {
22
22
  return () => {
23
23
  client.off('channel.visible', handleEvent);
24
24
  };
25
- // eslint-disable-next-line react-hooks/exhaustive-deps
26
- }, [customHandler]);
25
+ }, [client, customHandler, setChannels]);
27
26
  };
@@ -12,6 +12,5 @@ export const useConnectionRecoveredListener = (forceUpdate) => {
12
12
  return () => {
13
13
  client.off('connection.recovered', handleEvent);
14
14
  };
15
- // eslint-disable-next-line react-hooks/exhaustive-deps
16
- }, []);
15
+ }, [client, forceUpdate]);
17
16
  };
@@ -26,6 +26,11 @@ export const useMessageNewListener = (setChannels, customHandler, lockChannelOrd
26
26
  return () => {
27
27
  client.off('message.new', handleEvent);
28
28
  };
29
- // eslint-disable-next-line react-hooks/exhaustive-deps
30
- }, [lockChannelOrder]);
29
+ }, [
30
+ allowNewMessagesFromUnfilteredChannels,
31
+ client,
32
+ customHandler,
33
+ lockChannelOrder,
34
+ setChannels,
35
+ ]);
31
36
  };
@@ -29,6 +29,5 @@ export const useNotificationAddedToChannelListener = (setChannels, customHandler
29
29
  return () => {
30
30
  client.off('notification.added_to_channel', handleEvent);
31
31
  };
32
- // eslint-disable-next-line react-hooks/exhaustive-deps
33
- }, [customHandler]);
32
+ }, [allowNewMessagesFromUnfilteredChannels, client, customHandler, setChannels]);
34
33
  };
@@ -22,6 +22,5 @@ export const useNotificationMessageNewListener = (setChannels, customHandler, al
22
22
  return () => {
23
23
  client.off('notification.message_new', handleEvent);
24
24
  };
25
- // eslint-disable-next-line react-hooks/exhaustive-deps
26
- }, [customHandler]);
25
+ }, [allowNewMessagesFromUnfilteredChannels, client, customHandler, setChannels]);
27
26
  };
@@ -15,6 +15,5 @@ export const useNotificationRemovedFromChannelListener = (setChannels, customHan
15
15
  return () => {
16
16
  client.off('notification.removed_from_channel', handleEvent);
17
17
  };
18
- // eslint-disable-next-line react-hooks/exhaustive-deps
19
- }, [customHandler]);
18
+ }, [client, customHandler, setChannels]);
20
19
  };
@@ -20,6 +20,5 @@ export const useUserPresenceChangedListener = (setChannels) => {
20
20
  return () => {
21
21
  client.off('user.presence.changed', handleEvent);
22
22
  };
23
- // eslint-disable-next-line react-hooks/exhaustive-deps
24
- }, []);
23
+ }, [client, setChannels]);
25
24
  };
@@ -4,6 +4,7 @@ import { MessageDeliveryStatus } from './hooks/useMessageDeliveryStatus';
4
4
  import type { Channel } from 'stream-chat';
5
5
  import type { AvatarProps } from '../Avatar/Avatar';
6
6
  import type { StreamMessage } from '../../context/ChannelStateContext';
7
+ import type { TranslationContextValue } from '../../context/TranslationContext';
7
8
  import type { DefaultStreamChatGenerics } from '../../types/types';
8
9
  export type ChannelPreviewUIComponentProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = ChannelPreviewProps<StreamChatGenerics> & {
9
10
  /** If the component's channel is the active (selected) Channel */
@@ -14,8 +15,10 @@ export type ChannelPreviewUIComponentProps<StreamChatGenerics extends DefaultStr
14
15
  displayTitle?: string;
15
16
  /** The last message received in a channel */
16
17
  lastMessage?: StreamMessage<StreamChatGenerics>;
17
- /** Latest message preview to display, will be a string or JSX element supporting markdown. */
18
+ /** @deprecated Use latestMessagePreview prop instead. */
18
19
  latestMessage?: string | JSX.Element;
20
+ /** Latest message preview to display, will be a string or JSX element supporting markdown. */
21
+ latestMessagePreview?: string | JSX.Element;
19
22
  /** 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. */
20
23
  messageDeliveryStatus?: MessageDeliveryStatus;
21
24
  /** Number of unread Messages */
@@ -32,6 +35,8 @@ export type ChannelPreviewProps<StreamChatGenerics extends DefaultStreamChatGene
32
35
  channelUpdateCount?: number;
33
36
  /** Custom class for the channel preview root */
34
37
  className?: string;
38
+ /** Custom function that generates the message preview in ChannelPreview component */
39
+ getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string | JSX.Element;
35
40
  key?: string;
36
41
  /** Custom ChannelPreview click handler function */
37
42
  onSelect?: (event: React.MouseEvent) => void;
@@ -3,12 +3,12 @@ import React, { useEffect, useMemo, useState } from 'react';
3
3
  import { ChannelPreviewMessenger } from './ChannelPreviewMessenger';
4
4
  import { useIsChannelMuted } from './hooks/useIsChannelMuted';
5
5
  import { useChannelPreviewInfo } from './hooks/useChannelPreviewInfo';
6
- import { getLatestMessagePreview } from './utils';
6
+ import { getLatestMessagePreview as defaultGetLatestMessagePreview } from './utils';
7
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, Preview = ChannelPreviewMessenger, channelUpdateCount } = props;
11
+ const { channel, Preview = ChannelPreviewMessenger, channelUpdateCount, getLatestMessagePreview = defaultGetLatestMessagePreview, } = props;
12
12
  const { channel: activeChannel, client, setActiveChannel } = useChatContext('ChannelPreview');
13
13
  const { t, userLanguage } = useTranslationContext('ChannelPreview');
14
14
  const { displayImage, displayTitle } = useChannelPreviewInfo({ channel });
@@ -54,23 +54,25 @@ export const ChannelPreview = (props) => {
54
54
  }, 400), [channel, muted]);
55
55
  useEffect(() => {
56
56
  refreshUnreadCount();
57
- const handleEvent = (event) => {
58
- if (event.message)
59
- setLastMessage(event.message);
57
+ const handleEvent = () => {
58
+ setLastMessage(channel.state.latestMessages[channel.state.latestMessages.length - 1]);
60
59
  refreshUnreadCount();
61
60
  };
62
61
  channel.on('message.new', handleEvent);
63
62
  channel.on('message.updated', handleEvent);
64
63
  channel.on('message.deleted', handleEvent);
64
+ channel.on('message.undeleted', handleEvent);
65
+ channel.on('channel.truncated', handleEvent);
65
66
  return () => {
66
67
  channel.off('message.new', handleEvent);
67
68
  channel.off('message.updated', handleEvent);
68
69
  channel.off('message.deleted', handleEvent);
70
+ channel.off('message.undeleted', handleEvent);
71
+ channel.off('channel.truncated', handleEvent);
69
72
  };
70
- // eslint-disable-next-line react-hooks/exhaustive-deps
71
- }, [refreshUnreadCount, channelUpdateCount]);
73
+ }, [channel, refreshUnreadCount, channelUpdateCount]);
72
74
  if (!Preview)
73
75
  return null;
74
- const latestMessage = getLatestMessagePreview(channel, t, userLanguage);
75
- return (React.createElement(Preview, { ...props, active: isActive, displayImage: displayImage, displayTitle: displayTitle, lastMessage: lastMessage, latestMessage: latestMessage, messageDeliveryStatus: messageDeliveryStatus, setActiveChannel: setActiveChannel, unread: unread }));
76
+ const latestMessagePreview = getLatestMessagePreview(channel, t, userLanguage);
77
+ return (React.createElement(Preview, { ...props, active: isActive, displayImage: displayImage, displayTitle: displayTitle, lastMessage: lastMessage, latestMessage: latestMessagePreview, latestMessagePreview: latestMessagePreview, messageDeliveryStatus: messageDeliveryStatus, setActiveChannel: setActiveChannel, unread: unread }));
76
78
  };
@@ -2,7 +2,7 @@ import React, { useRef } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { Avatar as DefaultAvatar } from '../Avatar';
4
4
  const UnMemoizedChannelPreviewMessenger = (props) => {
5
- const { active, Avatar = DefaultAvatar, channel, className: customClassName = '', displayImage, displayTitle, latestMessage, onSelect: customOnSelectChannel, setActiveChannel, unread, watchers, } = props;
5
+ const { active, Avatar = DefaultAvatar, channel, className: customClassName = '', displayImage, displayTitle, latestMessagePreview, onSelect: customOnSelectChannel, setActiveChannel, unread, watchers, } = props;
6
6
  const channelPreviewButton = useRef(null);
7
7
  const avatarName = displayTitle || channel.state.messages[channel.state.messages.length - 1]?.user?.id;
8
8
  const onSelectChannel = (e) => {
@@ -24,7 +24,7 @@ const UnMemoizedChannelPreviewMessenger = (props) => {
24
24
  React.createElement("div", { className: 'str-chat__channel-preview-messenger--name' },
25
25
  React.createElement("span", null, displayTitle)),
26
26
  !!unread && (React.createElement("div", { className: 'str-chat__channel-preview-unread-badge', "data-testid": 'unread-badge' }, unread))),
27
- React.createElement("div", { className: 'str-chat__channel-preview-messenger--last-message' }, latestMessage))));
27
+ React.createElement("div", { className: 'str-chat__channel-preview-messenger--last-message' }, latestMessagePreview))));
28
28
  };
29
29
  /**
30
30
  * Used as preview component for channel item in [ChannelList](#channellist) component.
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import ReactMarkdown from 'react-markdown';
3
3
  export const renderPreviewText = (text) => React.createElement(ReactMarkdown, { skipHtml: true }, text);
4
4
  export const getLatestMessagePreview = (channel, t, userLanguage = 'en') => {
5
- const latestMessage = channel.state.messages[channel.state.messages.length - 1];
5
+ const latestMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
6
6
  const previewTextToRender = latestMessage?.i18n?.[`${userLanguage}_text`] ||
7
7
  latestMessage?.text;
8
8
  if (!latestMessage) {
@@ -12,8 +12,7 @@ export const getLatestMessagePreview = (channel, t, userLanguage = 'en') => {
12
12
  return t('Message deleted');
13
13
  }
14
14
  if (previewTextToRender) {
15
- const renderedText = renderPreviewText(previewTextToRender);
16
- return renderedText;
15
+ return renderPreviewText(previewTextToRender);
17
16
  }
18
17
  if (latestMessage.command) {
19
18
  return `/${latestMessage.command}`;
@@ -8,7 +8,8 @@ import { ChatContextValue } from '../../context/ChatContext';
8
8
  import { ComponentContextValue } from '../../context/ComponentContext';
9
9
  import type { UserResponse } from 'stream-chat';
10
10
  import type { DefaultStreamChatGenerics, UnknownType } from '../../types/types';
11
- type VirtualizedMessageListPropsForContext = 'additionalMessageInputProps' | 'closeReactionSelectorOnClick' | 'customMessageActions' | 'customMessageRenderer' | 'formatDate' | 'head' | 'loadingMore' | 'Message' | 'messageActions' | 'shouldGroupByUser' | 'reactionDetailsSort' | 'sortReactions' | 'sortReactionDetails' | 'threadList';
11
+ type PropsDrilledToMessage = 'additionalMessageInputProps' | 'customMessageActions' | 'formatDate' | 'messageActions' | 'openThread' | 'reactionDetailsSort' | 'sortReactions' | 'sortReactionDetails';
12
+ type VirtualizedMessageListPropsForContext = PropsDrilledToMessage | 'closeReactionSelectorOnClick' | 'customMessageRenderer' | 'head' | 'loadingMore' | 'Message' | 'shouldGroupByUser' | 'threadList';
12
13
  /**
13
14
  * Context object provided to some Virtuoso props that are functions (components rendered by Virtuoso and other functions)
14
15
  */
@@ -36,7 +37,6 @@ export type VirtuosoContext<StreamChatGenerics extends DefaultStreamChatGenerics
36
37
  /** The number of unread messages in the current channel. */
37
38
  unreadMessageCount?: number;
38
39
  };
39
- type PropsDrilledToMessage = 'additionalMessageInputProps' | 'customMessageActions' | 'formatDate' | 'messageActions' | 'reactionDetailsSort' | 'sortReactions' | 'sortReactionDetails';
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>>;
@@ -50,7 +50,7 @@ function calculateInitialTopMostItemIndex(messages, highlightedMessageId) {
50
50
  return messages.length - 1;
51
51
  }
52
52
  const VirtualizedMessageListWithContext = (props) => {
53
- const { additionalMessageInputProps, additionalVirtuosoProps = {}, channel, channelUnreadUiState, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, defaultItemHeight, disableDateSeparator = true, formatDate, groupStyles, hasMoreNewer, head, hideDeletedMessages = false, hideNewMessageSeparator = false, highlightedMessageId, jumpToLatestMessage, loadingMore, loadMore, loadMoreNewer, maxTimeBetweenGroupedMessages, Message: MessageUIComponentFromProps, messageActions, messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, messages, notifications,
53
+ const { additionalMessageInputProps, additionalVirtuosoProps = {}, channel, channelUnreadUiState, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, defaultItemHeight, disableDateSeparator = true, formatDate, groupStyles, hasMoreNewer, head, hideDeletedMessages = false, hideNewMessageSeparator = false, highlightedMessageId, jumpToLatestMessage, loadingMore, loadMore, loadMoreNewer, maxTimeBetweenGroupedMessages, Message: MessageUIComponentFromProps, messageActions, messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, messages, notifications, openThread,
54
54
  // TODO: refactor to scrollSeekPlaceHolderConfiguration and components.ScrollSeekPlaceholder, like the Virtuoso Component
55
55
  overscan = 0, read, returnAllReadData = false, reviewProcessedMessage, scrollSeekPlaceHolder, scrollToLatestMessageOnFocus = false, separateGiphyPreview = false, shouldGroupByUser = false, showUnreadNotificationAlways, reactionDetailsSort, sortReactionDetails, sortReactions, stickToBottomScrollBehavior = 'smooth', suppressAutoscroll, threadList, } = props;
56
56
  const { components: virtuosoComponentsFromProps, ...overridingVirtuosoProps } = additionalVirtuosoProps;
@@ -218,6 +218,7 @@ const VirtualizedMessageListWithContext = (props) => {
218
218
  messageGroupStyles,
219
219
  MessageSystem,
220
220
  numItemsPrepended,
221
+ openThread,
221
222
  ownMessagesReadByOthers,
222
223
  processedMessages,
223
224
  reactionDetailsSort,
@@ -47,7 +47,7 @@ export const EmptyPlaceholder = ({ context, }) => {
47
47
  return (React.createElement(React.Fragment, null, EmptyStateIndicator && (React.createElement(EmptyStateIndicator, { listType: context?.threadList ? 'thread' : 'message' }))));
48
48
  };
49
49
  export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
50
- const { additionalMessageInputProps, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, DateSeparator, firstUnreadMessageId, formatDate, lastReadDate, lastReadMessageId, lastReceivedMessageId, Message: MessageUIComponent, messageActions, messageGroupStyles, MessageSystem, numItemsPrepended, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, shouldGroupByUser, sortReactionDetails, sortReactions, threadList, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = virtuosoContext;
50
+ const { additionalMessageInputProps, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, DateSeparator, firstUnreadMessageId, formatDate, lastReadDate, lastReadMessageId, lastReceivedMessageId, Message: MessageUIComponent, messageActions, messageGroupStyles, MessageSystem, numItemsPrepended, openThread, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, shouldGroupByUser, sortReactionDetails, sortReactions, threadList, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = virtuosoContext;
51
51
  const streamMessageIndex = calculateItemIndex(virtuosoIndex, numItemsPrepended);
52
52
  if (customMessageRenderer) {
53
53
  return customMessageRenderer(messageList, streamMessageIndex);
@@ -89,7 +89,7 @@ export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
89
89
  return (React.createElement(React.Fragment, null,
90
90
  showUnreadSeparatorAbove && (React.createElement("div", { className: 'str-chat__unread-messages-separator-wrapper' },
91
91
  React.createElement(UnreadMessagesSeparator, { unreadCount: unreadMessageCount }))),
92
- React.createElement(Message, { additionalMessageInputProps: additionalMessageInputProps, autoscrollToBottom: virtuosoRef.current?.autoscrollToBottom, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: customMessageActions, endOfGroup: endOfGroup, firstOfGroup: firstOfGroup, formatDate: formatDate, groupedByUser: groupedByUser, groupStyles: [messageGroupStyles[message.id] ?? ''], lastReceivedId: lastReceivedMessageId, message: message, Message: MessageUIComponent, messageActions: messageActions, reactionDetailsSort: reactionDetailsSort, readBy: ownMessagesReadByOthers[message.id] || [], sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: threadList }),
92
+ React.createElement(Message, { additionalMessageInputProps: additionalMessageInputProps, autoscrollToBottom: virtuosoRef.current?.autoscrollToBottom, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: customMessageActions, endOfGroup: endOfGroup, firstOfGroup: firstOfGroup, formatDate: formatDate, groupedByUser: groupedByUser, groupStyles: [messageGroupStyles[message.id] ?? ''], lastReceivedId: lastReceivedMessageId, message: message, Message: MessageUIComponent, messageActions: messageActions, openThread: openThread, reactionDetailsSort: reactionDetailsSort, readBy: ownMessagesReadByOthers[message.id] || [], sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: threadList }),
93
93
  showUnreadSeparatorBelow && (React.createElement("div", { className: 'str-chat__unread-messages-separator-wrapper' },
94
94
  React.createElement(UnreadMessagesSeparator, { unreadCount: unreadMessageCount })))));
95
95
  };
@@ -1,7 +1,6 @@
1
1
  import { StreamMessage } from '../../../../context';
2
2
  import { DefaultStreamChatGenerics } from '../../../../types/types';
3
3
  type UseScrollToBottomOnNewMessageParams<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
4
- /** */
5
4
  scrollToBottom: () => void;
6
5
  messages?: StreamMessage<StreamChatGenerics>[];
7
6
  /** When `true`, the list will scroll to the latest message when the window regains focus */
@@ -1,28 +1,32 @@
1
- import React, { useCallback, useEffect } from 'react';
1
+ import { useEffect, useRef, useState } from 'react';
2
2
  export const useScrollToBottomOnNewMessage = ({ messages, scrollToBottom, scrollToLatestMessageOnFocus, }) => {
3
- const [newMessagesReceivedInBackground, setNewMessagesReceivedInBackground] = React.useState(false);
4
- const resetNewMessagesReceivedInBackground = useCallback(() => {
5
- setNewMessagesReceivedInBackground(false);
6
- }, []);
7
- useEffect(() => {
8
- setNewMessagesReceivedInBackground(true);
9
- }, [messages]);
10
- const scrollToBottomIfConfigured = useCallback((event) => {
3
+ const [newMessagesReceivedInBackground, setNewMessagesReceivedInBackground] = useState(false);
4
+ const scrollToBottomIfConfigured = useRef();
5
+ scrollToBottomIfConfigured.current = (event) => {
11
6
  if (!scrollToLatestMessageOnFocus ||
12
7
  !newMessagesReceivedInBackground ||
13
- event.target !== window)
8
+ event.target !== window) {
14
9
  return;
10
+ }
15
11
  setTimeout(scrollToBottom, 100);
16
- }, [scrollToLatestMessageOnFocus, scrollToBottom, newMessagesReceivedInBackground]);
12
+ };
17
13
  useEffect(() => {
14
+ setNewMessagesReceivedInBackground(true);
15
+ }, [messages]);
16
+ useEffect(() => {
17
+ const handleFocus = (event) => {
18
+ scrollToBottomIfConfigured.current?.(event);
19
+ };
20
+ const handleBlur = () => {
21
+ setNewMessagesReceivedInBackground(false);
22
+ };
18
23
  if (typeof window !== 'undefined') {
19
- window.addEventListener('focus', scrollToBottomIfConfigured);
20
- window.addEventListener('blur', resetNewMessagesReceivedInBackground);
24
+ window.addEventListener('focus', handleFocus);
25
+ window.addEventListener('blur', handleBlur);
21
26
  }
22
27
  return () => {
23
- window.removeEventListener('focus', scrollToBottomIfConfigured);
24
- window.removeEventListener('blur', resetNewMessagesReceivedInBackground);
28
+ window.removeEventListener('focus', handleFocus);
29
+ window.removeEventListener('blur', handleBlur);
25
30
  };
26
- // eslint-disable-next-line react-hooks/exhaustive-deps
27
- }, [scrollToBottomIfConfigured]);
31
+ }, []);
28
32
  };