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.
- package/dist/components/ChannelList/ChannelList.d.ts +3 -0
- package/dist/components/ChannelList/ChannelList.js +5 -2
- package/dist/components/ChannelList/hooks/useChannelDeletedListener.js +2 -3
- package/dist/components/ChannelList/hooks/useChannelHiddenListener.js +1 -2
- package/dist/components/ChannelList/hooks/useChannelTruncatedListener.js +1 -2
- package/dist/components/ChannelList/hooks/useChannelUpdatedListener.js +1 -2
- package/dist/components/ChannelList/hooks/useChannelVisibleListener.js +1 -2
- package/dist/components/ChannelList/hooks/useConnectionRecoveredListener.js +1 -2
- package/dist/components/ChannelList/hooks/useMessageNewListener.js +7 -2
- package/dist/components/ChannelList/hooks/useNotificationAddedToChannelListener.js +1 -2
- package/dist/components/ChannelList/hooks/useNotificationMessageNewListener.js +1 -2
- package/dist/components/ChannelList/hooks/useNotificationRemovedFromChannelListener.js +1 -2
- package/dist/components/ChannelList/hooks/useUserPresenceChangedListener.js +1 -2
- package/dist/components/ChannelPreview/ChannelPreview.d.ts +6 -1
- package/dist/components/ChannelPreview/ChannelPreview.js +11 -9
- package/dist/components/ChannelPreview/ChannelPreviewMessenger.js +2 -2
- package/dist/components/ChannelPreview/utils.js +2 -3
- package/dist/components/MessageList/VirtualizedMessageList.d.ts +2 -2
- package/dist/components/MessageList/VirtualizedMessageList.js +2 -1
- package/dist/components/MessageList/VirtualizedMessageListComponents.js +2 -2
- package/dist/components/MessageList/hooks/VirtualizedMessageList/useScrollToBottomOnNewMessage.d.ts +0 -1
- package/dist/components/MessageList/hooks/VirtualizedMessageList/useScrollToBottomOnNewMessage.js +21 -17
- package/dist/index.browser.cjs +80 -57
- package/dist/index.browser.cjs.map +3 -3
- package/dist/index.node.cjs +104 -81
- package/dist/index.node.cjs.map +3 -3
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
}, [customHandler]);
|
|
25
|
+
}, [client, customHandler, setChannels]);
|
|
27
26
|
};
|
|
@@ -26,6 +26,11 @@ export const useMessageNewListener = (setChannels, customHandler, lockChannelOrd
|
|
|
26
26
|
return () => {
|
|
27
27
|
client.off('message.new', handleEvent);
|
|
28
28
|
};
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
}, [customHandler]);
|
|
18
|
+
}, [client, customHandler, setChannels]);
|
|
20
19
|
};
|
|
@@ -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
|
-
/**
|
|
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 = (
|
|
58
|
-
|
|
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
|
-
|
|
71
|
-
}, [refreshUnreadCount, channelUpdateCount]);
|
|
73
|
+
}, [channel, refreshUnreadCount, channelUpdateCount]);
|
|
72
74
|
if (!Preview)
|
|
73
75
|
return null;
|
|
74
|
-
const
|
|
75
|
-
return (React.createElement(Preview, { ...props, active: isActive, displayImage: displayImage, displayTitle: displayTitle, lastMessage: lastMessage, latestMessage:
|
|
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,
|
|
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' },
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
};
|
package/dist/components/MessageList/hooks/VirtualizedMessageList/useScrollToBottomOnNewMessage.d.ts
CHANGED
|
@@ -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 */
|
package/dist/components/MessageList/hooks/VirtualizedMessageList/useScrollToBottomOnNewMessage.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
2
|
export const useScrollToBottomOnNewMessage = ({ messages, scrollToBottom, scrollToLatestMessageOnFocus, }) => {
|
|
3
|
-
const [newMessagesReceivedInBackground, setNewMessagesReceivedInBackground] =
|
|
4
|
-
const
|
|
5
|
-
|
|
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
|
-
}
|
|
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',
|
|
20
|
-
window.addEventListener('blur',
|
|
24
|
+
window.addEventListener('focus', handleFocus);
|
|
25
|
+
window.addEventListener('blur', handleBlur);
|
|
21
26
|
}
|
|
22
27
|
return () => {
|
|
23
|
-
window.removeEventListener('focus',
|
|
24
|
-
window.removeEventListener('blur',
|
|
28
|
+
window.removeEventListener('focus', handleFocus);
|
|
29
|
+
window.removeEventListener('blur', handleBlur);
|
|
25
30
|
};
|
|
26
|
-
|
|
27
|
-
}, [scrollToBottomIfConfigured]);
|
|
31
|
+
}, []);
|
|
28
32
|
};
|