stream-chat-react 13.10.0 → 13.10.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/Channel/hooks/useCreateChannelStateContext.js +7 -3
- package/dist/components/Chat/hooks/useChat.js +1 -1
- package/dist/components/Message/Message.js +1 -1
- package/dist/components/Message/MessageStatus.js +6 -2
- package/dist/components/Message/types.d.ts +4 -0
- package/dist/components/MessageList/MessageList.js +6 -0
- package/dist/components/MessageList/VirtualizedMessageList.d.ts +3 -1
- package/dist/components/MessageList/VirtualizedMessageList.js +6 -0
- package/dist/components/MessageList/VirtualizedMessageListComponents.js +2 -2
- package/dist/components/MessageList/hooks/MessageList/useMessageListElements.d.ts +1 -0
- package/dist/components/MessageList/hooks/MessageList/useMessageListElements.js +7 -2
- package/dist/components/MessageList/hooks/useLastDeliveredData.d.ts +1 -0
- package/dist/components/MessageList/hooks/useLastDeliveredData.js +20 -10
- package/dist/components/MessageList/hooks/useLastOwnMessage.d.ts +5 -0
- package/dist/components/MessageList/hooks/useLastOwnMessage.js +4 -0
- package/dist/components/MessageList/hooks/useLastReadData.d.ts +1 -0
- package/dist/components/MessageList/hooks/useLastReadData.js +20 -10
- package/dist/components/MessageList/renderMessages.d.ts +4 -2
- package/dist/components/MessageList/renderMessages.js +2 -2
- package/dist/context/MessageContext.d.ts +4 -0
- package/dist/experimental/index.browser.cjs.map +2 -2
- package/dist/experimental/index.node.cjs.map +2 -2
- package/dist/index.browser.cjs +836 -761
- package/dist/index.browser.cjs.map +4 -4
- package/dist/index.node.cjs +836 -761
- package/dist/index.node.cjs.map +4 -4
- package/dist/plugins/Emojis/index.browser.cjs.map +2 -2
- package/dist/plugins/Emojis/index.node.cjs.map +2 -2
- package/dist/utils/findReverse.d.ts +1 -0
- package/dist/utils/findReverse.js +9 -0
- package/package.json +1 -1
|
@@ -8,9 +8,13 @@ export const useCreateChannelStateContext = (value) => {
|
|
|
8
8
|
const notificationsLength = notifications.length;
|
|
9
9
|
const readUsers = Object.values(read);
|
|
10
10
|
const readUsersLength = readUsers.length;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const readUsersLastReadDateStrings = [];
|
|
12
|
+
for (const { last_read } of readUsers) {
|
|
13
|
+
if (!lastRead)
|
|
14
|
+
continue;
|
|
15
|
+
readUsersLastReadDateStrings.push(last_read?.toISOString());
|
|
16
|
+
}
|
|
17
|
+
const readUsersLastReads = readUsersLastReadDateStrings.join();
|
|
14
18
|
const threadMessagesLength = threadMessages?.length;
|
|
15
19
|
const channelCapabilities = {};
|
|
16
20
|
channelCapabilitiesArray.forEach((capability) => {
|
|
@@ -24,7 +24,7 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
|
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
if (!client)
|
|
26
26
|
return;
|
|
27
|
-
const version = "13.10.
|
|
27
|
+
const version = "13.10.1";
|
|
28
28
|
const userAgent = client.getUserAgent();
|
|
29
29
|
if (!userAgent.includes('stream-chat-react')) {
|
|
30
30
|
// result looks like: 'stream-chat-react-2.3.2-stream-chat-javascript-client-browser-2.2.2'
|
|
@@ -116,5 +116,5 @@ export const Message = (props) => {
|
|
|
116
116
|
notify: addNotification,
|
|
117
117
|
});
|
|
118
118
|
const highlighted = highlightedMessageId === message.id;
|
|
119
|
-
return (React.createElement(MemoizedMessage, { additionalMessageInputProps: props.additionalMessageInputProps, autoscrollToBottom: props.autoscrollToBottom, canPin: canPin, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: props.customMessageActions, deliveredTo: props.deliveredTo, disableQuotedMessages: props.disableQuotedMessages, endOfGroup: props.endOfGroup, firstOfGroup: props.firstOfGroup, formatDate: props.formatDate, groupedByUser: props.groupedByUser, groupStyles: props.groupStyles, handleAction: handleAction, handleDelete: handleDelete, handleFetchReactions: handleFetchReactions, handleFlag: handleFlag, handleMarkUnread: handleMarkUnread, handleMute: handleMute, handleOpenThread: handleOpenThread, handlePin: handlePin, handleReaction: handleReaction, handleRetry: handleRetry, highlighted: highlighted, initialMessage: props.initialMessage, lastReceivedId: props.lastReceivedId, message: message, Message: props.Message, messageActions: props.messageActions, messageListRect: props.messageListRect, mutes: mutes, onMentionsClickMessage: onMentionsClick, onMentionsHoverMessage: onMentionsHover, onUserClick: props.onUserClick, onUserHover: props.onUserHover, pinPermissions: props.pinPermissions, reactionDetailsSort: reactionDetailsSort, readBy: props.readBy, renderText: props.renderText, sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: props.threadList, unsafeHTML: props.unsafeHTML, userRoles: userRoles }));
|
|
119
|
+
return (React.createElement(MemoizedMessage, { additionalMessageInputProps: props.additionalMessageInputProps, autoscrollToBottom: props.autoscrollToBottom, canPin: canPin, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: props.customMessageActions, deliveredTo: props.deliveredTo, disableQuotedMessages: props.disableQuotedMessages, endOfGroup: props.endOfGroup, firstOfGroup: props.firstOfGroup, formatDate: props.formatDate, groupedByUser: props.groupedByUser, groupStyles: props.groupStyles, handleAction: handleAction, handleDelete: handleDelete, handleFetchReactions: handleFetchReactions, handleFlag: handleFlag, handleMarkUnread: handleMarkUnread, handleMute: handleMute, handleOpenThread: handleOpenThread, handlePin: handlePin, handleReaction: handleReaction, handleRetry: handleRetry, highlighted: highlighted, initialMessage: props.initialMessage, lastOwnMessage: props.lastOwnMessage, lastReceivedId: props.lastReceivedId, message: message, Message: props.Message, messageActions: props.messageActions, messageListRect: props.messageListRect, mutes: mutes, onMentionsClickMessage: onMentionsClick, onMentionsHoverMessage: onMentionsHover, onUserClick: props.onUserClick, onUserHover: props.onUserHover, pinPermissions: props.pinPermissions, reactionDetailsSort: reactionDetailsSort, readBy: props.readBy, renderText: props.renderText, returnAllReadData: props.returnAllReadData, sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: props.threadList, unsafeHTML: props.unsafeHTML, userRoles: userRoles }));
|
|
120
120
|
};
|
|
@@ -15,7 +15,7 @@ const UnMemoizedMessageStatus = (props) => {
|
|
|
15
15
|
const { handleEnter, handleLeave, tooltipVisible } = useEnterLeaveHandlers();
|
|
16
16
|
const { client } = useChatContext('MessageStatus');
|
|
17
17
|
const { Avatar: contextAvatar } = useComponentContext('MessageStatus');
|
|
18
|
-
const { deliveredTo, isMyMessage, message, readBy, threadList } = useMessageContext('MessageStatus');
|
|
18
|
+
const { deliveredTo, isMyMessage, lastOwnMessage, message, readBy, returnAllReadData, threadList, } = useMessageContext('MessageStatus');
|
|
19
19
|
const { t } = useTranslationContext('MessageStatus');
|
|
20
20
|
const [referenceElement, setReferenceElement] = useState(null);
|
|
21
21
|
const Avatar = propAvatar || contextAvatar || DefaultAvatar;
|
|
@@ -26,7 +26,11 @@ const UnMemoizedMessageStatus = (props) => {
|
|
|
26
26
|
const sending = message.status === 'sending';
|
|
27
27
|
const read = !!(readBy?.length && !justReadByMe && !threadList);
|
|
28
28
|
const delivered = !!(deliveredTo?.length && !deliveredOnlyToMe && !read && !threadList);
|
|
29
|
-
const sent =
|
|
29
|
+
const sent = (returnAllReadData || lastOwnMessage?.id === message.id) &&
|
|
30
|
+
message.status === 'received' &&
|
|
31
|
+
!delivered &&
|
|
32
|
+
!read &&
|
|
33
|
+
!threadList;
|
|
30
34
|
const readersWithoutOwnUser = read
|
|
31
35
|
? readBy.filter((item) => item.id !== client.user?.id)
|
|
32
36
|
: [];
|
|
@@ -59,6 +59,8 @@ export type MessageProps = {
|
|
|
59
59
|
highlighted?: boolean;
|
|
60
60
|
/** Whether the threaded message is the first in the thread list */
|
|
61
61
|
initialMessage?: boolean;
|
|
62
|
+
/** Latest own message in currently displayed message set. */
|
|
63
|
+
lastOwnMessage?: LocalMessage;
|
|
62
64
|
/** Latest message id on current channel */
|
|
63
65
|
lastReceivedId?: string | null;
|
|
64
66
|
/** UI component to display a Message in MessageList, overrides value in [ComponentContext](https://getstream.io/chat/docs/sdk/react/contexts/component_context/#message) */
|
|
@@ -89,6 +91,8 @@ export type MessageProps = {
|
|
|
89
91
|
renderText?: (text?: string, mentioned_users?: UserResponse[], options?: RenderTextOptions) => ReactNode;
|
|
90
92
|
/** Custom retry send message handler to override default in [ChannelActionContext](https://getstream.io/chat/docs/sdk/react/contexts/channel_action_context/) */
|
|
91
93
|
retrySendMessage?: ChannelActionContextValue['retrySendMessage'];
|
|
94
|
+
/** Keep track of read receipts for each message sent by the user. When disabled, only the last own message delivery / read status is rendered. */
|
|
95
|
+
returnAllReadData?: boolean;
|
|
92
96
|
/** Comparator function to sort the list of reacted users
|
|
93
97
|
* @deprecated use `reactionDetailsSort` instead
|
|
94
98
|
*/
|
|
@@ -20,6 +20,7 @@ import { MessageListMainPanel as DefaultMessageListMainPanel } from './MessageLi
|
|
|
20
20
|
import { defaultRenderMessages } from './renderMessages';
|
|
21
21
|
import { useStableId } from '../UtilityComponents/useStableId';
|
|
22
22
|
import { DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, DEFAULT_NEXT_CHANNEL_PAGE_SIZE, } from '../../constants/limits';
|
|
23
|
+
import { useLastOwnMessage } from './hooks/useLastOwnMessage';
|
|
23
24
|
const MessageListWithContext = (props) => {
|
|
24
25
|
const { channel, channelUnreadUiState, disableDateSeparator = false, groupStyles, hasMoreNewer = false, headerPosition, hideDeletedMessages = false, hideNewMessageSeparator = false, highlightedMessageId, internalInfiniteScrollProps: { threshold: loadMoreScrollThreshold = DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, ...restInternalInfiniteScrollProps } = {}, jumpToLatestMessage = () => Promise.resolve(), loadMore: loadMoreCallback, loadMoreNewer: loadMoreNewerCallback, // @deprecated in favor of `channelCapabilities` - TODO: remove in next major release
|
|
25
26
|
maxTimeBetweenGroupedMessages, messageActions = Object.keys(MESSAGE_ACTIONS), messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, messages = [], noGroupByUser = false, notifications, pinPermissions = defaultPinPermissions, reactionDetailsSort, renderMessages = defaultRenderMessages, returnAllReadData = false, reviewProcessedMessage, showUnreadNotificationAlways, sortReactionDetails, sortReactions, suppressAutoscroll, threadList = false, unsafeHTML = false, } = props;
|
|
@@ -57,6 +58,10 @@ const MessageListWithContext = (props) => {
|
|
|
57
58
|
noGroupByUser,
|
|
58
59
|
reviewProcessedMessage,
|
|
59
60
|
});
|
|
61
|
+
const lastOwnMessage = useLastOwnMessage({
|
|
62
|
+
messages,
|
|
63
|
+
ownUserId: channel.getClient().user?.id,
|
|
64
|
+
});
|
|
60
65
|
const elements = useMessageListElements({
|
|
61
66
|
channelUnreadUiState,
|
|
62
67
|
enrichedMessages,
|
|
@@ -91,6 +96,7 @@ const MessageListWithContext = (props) => {
|
|
|
91
96
|
sortReactions,
|
|
92
97
|
unsafeHTML,
|
|
93
98
|
},
|
|
99
|
+
lastOwnMessage,
|
|
94
100
|
messageGroupStyles,
|
|
95
101
|
messages,
|
|
96
102
|
renderMessages,
|
|
@@ -10,7 +10,7 @@ import type { ComponentContextValue } from '../../context/ComponentContext';
|
|
|
10
10
|
import type { LocalMessage, UserResponse } from 'stream-chat';
|
|
11
11
|
import type { UnknownType } from '../../types/types';
|
|
12
12
|
type PropsDrilledToMessage = 'additionalMessageInputProps' | 'customMessageActions' | 'formatDate' | 'messageActions' | 'openThread' | 'reactionDetailsSort' | 'sortReactions' | 'sortReactionDetails';
|
|
13
|
-
type VirtualizedMessageListPropsForContext = PropsDrilledToMessage | 'closeReactionSelectorOnClick' | 'customMessageRenderer' | 'head' | 'loadingMore' | 'Message' | 'shouldGroupByUser' | 'threadList';
|
|
13
|
+
type VirtualizedMessageListPropsForContext = PropsDrilledToMessage | 'closeReactionSelectorOnClick' | 'customMessageRenderer' | 'head' | 'loadingMore' | 'Message' | 'returnAllReadData' | 'shouldGroupByUser' | 'threadList';
|
|
14
14
|
/**
|
|
15
15
|
* Context object provided to some Virtuoso props that are functions (components rendered by Virtuoso and other functions)
|
|
16
16
|
*/
|
|
@@ -29,6 +29,8 @@ export type VirtuosoContext = Required<Pick<ComponentContextValue, 'DateSeparato
|
|
|
29
29
|
processedMessages: RenderedMessage[];
|
|
30
30
|
/** Instance of VirtuosoHandle object providing the API to navigate in the virtualized list by various scroll actions. */
|
|
31
31
|
virtuosoRef: RefObject<VirtuosoHandle | null>;
|
|
32
|
+
/** Latest own message in currently displayed message set. */
|
|
33
|
+
lastOwnMessage?: LocalMessage;
|
|
32
34
|
/** Message id which was marked as unread. ALl the messages following this message are considered unrea. */
|
|
33
35
|
firstUnreadMessageId?: string;
|
|
34
36
|
lastReadDate?: Date;
|
|
@@ -23,6 +23,7 @@ import { VirtualizedMessageListContextProvider } from '../../context/Virtualized
|
|
|
23
23
|
import { DEFAULT_NEXT_CHANNEL_PAGE_SIZE } from '../../constants/limits';
|
|
24
24
|
import { useStableId } from '../UtilityComponents/useStableId';
|
|
25
25
|
import { useLastDeliveredData } from './hooks/useLastDeliveredData';
|
|
26
|
+
import { useLastOwnMessage } from './hooks/useLastOwnMessage';
|
|
26
27
|
function captureResizeObserverExceededError(e) {
|
|
27
28
|
if (e.message === 'ResizeObserver loop completed with undelivered notifications.' ||
|
|
28
29
|
e.message === 'ResizeObserver loop limit exceeded') {
|
|
@@ -101,14 +102,17 @@ const VirtualizedMessageListWithContext = (props) => {
|
|
|
101
102
|
messages?.length,
|
|
102
103
|
client.userID,
|
|
103
104
|
]);
|
|
105
|
+
const lastOwnMessage = useLastOwnMessage({ messages, ownUserId: client.user?.id });
|
|
104
106
|
// get the mapping of own messages to array of users who read them
|
|
105
107
|
const ownMessagesReadByOthers = useLastReadData({
|
|
106
108
|
channel,
|
|
109
|
+
lastOwnMessage,
|
|
107
110
|
messages: messages || [],
|
|
108
111
|
returnAllReadData,
|
|
109
112
|
});
|
|
110
113
|
const ownMessagesDeliveredToOthers = useLastDeliveredData({
|
|
111
114
|
channel,
|
|
115
|
+
lastOwnMessage,
|
|
112
116
|
messages: messages || [],
|
|
113
117
|
returnAllReadData,
|
|
114
118
|
});
|
|
@@ -226,6 +230,7 @@ const VirtualizedMessageListWithContext = (props) => {
|
|
|
226
230
|
firstUnreadMessageId: channelUnreadUiState?.first_unread_message_id,
|
|
227
231
|
formatDate,
|
|
228
232
|
head,
|
|
233
|
+
lastOwnMessage,
|
|
229
234
|
lastReadDate: channelUnreadUiState?.last_read,
|
|
230
235
|
lastReadMessageId: channelUnreadUiState?.last_read_message_id,
|
|
231
236
|
lastReceivedMessageId,
|
|
@@ -240,6 +245,7 @@ const VirtualizedMessageListWithContext = (props) => {
|
|
|
240
245
|
ownMessagesReadByOthers,
|
|
241
246
|
processedMessages,
|
|
242
247
|
reactionDetailsSort,
|
|
248
|
+
returnAllReadData,
|
|
243
249
|
shouldGroupByUser,
|
|
244
250
|
sortReactionDetails,
|
|
245
251
|
sortReactions,
|
|
@@ -51,7 +51,7 @@ export const EmptyPlaceholder = ({ context }) => {
|
|
|
51
51
|
return (React.createElement(React.Fragment, null, EmptyStateIndicator && (React.createElement(EmptyStateIndicator, { listType: context?.threadList ? 'thread' : 'message' }))));
|
|
52
52
|
};
|
|
53
53
|
export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
|
|
54
|
-
const { additionalMessageInputProps, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, DateSeparator, firstUnreadMessageId, formatDate, lastReadDate, lastReadMessageId, lastReceivedMessageId, Message: MessageUIComponent, messageActions, messageGroupStyles, MessageSystem, numItemsPrepended, openThread, ownMessagesDeliveredToOthers, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, shouldGroupByUser, sortReactionDetails, sortReactions, threadList, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = virtuosoContext;
|
|
54
|
+
const { additionalMessageInputProps, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, DateSeparator, firstUnreadMessageId, formatDate, lastOwnMessage, lastReadDate, lastReadMessageId, lastReceivedMessageId, Message: MessageUIComponent, messageActions, messageGroupStyles, MessageSystem, numItemsPrepended, openThread, ownMessagesDeliveredToOthers, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, returnAllReadData, shouldGroupByUser, sortReactionDetails, sortReactions, threadList, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = virtuosoContext;
|
|
55
55
|
const streamMessageIndex = calculateItemIndex(virtuosoIndex, numItemsPrepended);
|
|
56
56
|
if (customMessageRenderer) {
|
|
57
57
|
return customMessageRenderer(messageList, streamMessageIndex);
|
|
@@ -88,5 +88,5 @@ export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
|
|
|
88
88
|
return (React.createElement(React.Fragment, null,
|
|
89
89
|
isFirstUnreadMessage && (React.createElement("div", { className: 'str-chat__unread-messages-separator-wrapper' },
|
|
90
90
|
React.createElement(UnreadMessagesSeparator, { unreadCount: unreadMessageCount }))),
|
|
91
|
-
React.createElement(Message, { additionalMessageInputProps: additionalMessageInputProps, autoscrollToBottom: virtuosoRef.current?.autoscrollToBottom, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: customMessageActions, deliveredTo: ownMessagesDeliveredToOthers[message.id] || [], 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 })));
|
|
91
|
+
React.createElement(Message, { additionalMessageInputProps: additionalMessageInputProps, autoscrollToBottom: virtuosoRef.current?.autoscrollToBottom, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: customMessageActions, deliveredTo: ownMessagesDeliveredToOthers[message.id] || [], endOfGroup: endOfGroup, firstOfGroup: firstOfGroup, formatDate: formatDate, groupedByUser: groupedByUser, groupStyles: [messageGroupStyles[message.id] ?? ''], lastOwnMessage: lastOwnMessage, lastReceivedId: lastReceivedMessageId, message: message, Message: MessageUIComponent, messageActions: messageActions, openThread: openThread, reactionDetailsSort: reactionDetailsSort, readBy: ownMessagesReadByOthers[message.id] || [], returnAllReadData: returnAllReadData, sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: threadList })));
|
|
92
92
|
};
|
|
@@ -12,6 +12,7 @@ type UseMessageListElementsProps = {
|
|
|
12
12
|
returnAllReadData: boolean;
|
|
13
13
|
threadList: boolean;
|
|
14
14
|
channelUnreadUiState?: ChannelUnreadUiState;
|
|
15
|
+
lastOwnMessage?: LocalMessage;
|
|
15
16
|
};
|
|
16
17
|
export declare const useMessageListElements: (props: UseMessageListElementsProps) => React.ReactNode[];
|
|
17
18
|
export {};
|
|
@@ -6,18 +6,20 @@ import { useComponentContext } from '../../../../context/ComponentContext';
|
|
|
6
6
|
import { useChannelStateContext } from '../../../../context';
|
|
7
7
|
import { useLastDeliveredData } from '../useLastDeliveredData';
|
|
8
8
|
export const useMessageListElements = (props) => {
|
|
9
|
-
const { channelUnreadUiState, enrichedMessages, internalMessageProps, messageGroupStyles, messages, renderMessages, returnAllReadData, threadList, } = props;
|
|
9
|
+
const { channelUnreadUiState, enrichedMessages, internalMessageProps, lastOwnMessage, messageGroupStyles, messages, renderMessages, returnAllReadData, threadList, } = props;
|
|
10
10
|
const { customClasses } = useChatContext('useMessageListElements');
|
|
11
11
|
const { channel } = useChannelStateContext();
|
|
12
12
|
const components = useComponentContext('useMessageListElements');
|
|
13
13
|
// get the readData, but only for messages submitted by the user themselves
|
|
14
14
|
const readData = useLastReadData({
|
|
15
15
|
channel,
|
|
16
|
+
lastOwnMessage,
|
|
16
17
|
messages,
|
|
17
18
|
returnAllReadData,
|
|
18
19
|
});
|
|
19
20
|
const ownMessagesDeliveredToOthers = useLastDeliveredData({
|
|
20
21
|
channel,
|
|
22
|
+
lastOwnMessage,
|
|
21
23
|
messages,
|
|
22
24
|
returnAllReadData,
|
|
23
25
|
});
|
|
@@ -26,22 +28,25 @@ export const useMessageListElements = (props) => {
|
|
|
26
28
|
channelUnreadUiState,
|
|
27
29
|
components,
|
|
28
30
|
customClasses,
|
|
31
|
+
lastOwnMessage,
|
|
29
32
|
lastReceivedMessageId,
|
|
30
33
|
messageGroupStyles,
|
|
31
34
|
messages: enrichedMessages,
|
|
32
35
|
ownMessagesDeliveredToOthers,
|
|
33
36
|
readData,
|
|
34
|
-
sharedMessageProps: { ...internalMessageProps, threadList },
|
|
37
|
+
sharedMessageProps: { ...internalMessageProps, returnAllReadData, threadList },
|
|
35
38
|
}),
|
|
36
39
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
40
|
[
|
|
38
41
|
enrichedMessages,
|
|
39
42
|
internalMessageProps,
|
|
43
|
+
lastOwnMessage,
|
|
40
44
|
lastReceivedMessageId,
|
|
41
45
|
messageGroupStyles,
|
|
42
46
|
channelUnreadUiState,
|
|
43
47
|
readData,
|
|
44
48
|
renderMessages,
|
|
49
|
+
returnAllReadData,
|
|
45
50
|
threadList,
|
|
46
51
|
]);
|
|
47
52
|
return elements;
|
|
@@ -3,6 +3,7 @@ type UseLastDeliveredDataParams = {
|
|
|
3
3
|
channel: Channel;
|
|
4
4
|
messages: LocalMessage[];
|
|
5
5
|
returnAllReadData: boolean;
|
|
6
|
+
lastOwnMessage?: LocalMessage;
|
|
6
7
|
};
|
|
7
8
|
export declare const useLastDeliveredData: (props: UseLastDeliveredDataParams) => Record<string, UserResponse[]>;
|
|
8
9
|
export {};
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
export const useLastDeliveredData = (props) => {
|
|
3
|
-
const { channel, messages, returnAllReadData } = props;
|
|
4
|
-
const calculate = useCallback(() =>
|
|
5
|
-
|
|
6
|
-
acc
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
const { channel, lastOwnMessage, messages, returnAllReadData } = props;
|
|
4
|
+
const calculate = useCallback(() => {
|
|
5
|
+
if (returnAllReadData) {
|
|
6
|
+
return messages.reduce((acc, msg) => {
|
|
7
|
+
acc[msg.id] = channel.messageReceiptsTracker.deliveredForMessage({
|
|
8
|
+
msgId: msg.id,
|
|
9
|
+
timestampMs: msg.created_at.getTime(),
|
|
10
|
+
});
|
|
11
|
+
return acc;
|
|
12
|
+
}, {});
|
|
13
|
+
}
|
|
14
|
+
if (!lastOwnMessage)
|
|
15
|
+
return {};
|
|
16
|
+
return {
|
|
17
|
+
[lastOwnMessage.id]: channel.messageReceiptsTracker.deliveredForMessage({
|
|
18
|
+
msgId: lastOwnMessage.id,
|
|
19
|
+
timestampMs: lastOwnMessage.created_at.getTime(),
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
}, [channel, lastOwnMessage, messages, returnAllReadData]);
|
|
13
23
|
const [deliveredTo, setDeliveredTo] = useState(calculate);
|
|
14
24
|
useEffect(() => channel.on('message.delivered', () => setDeliveredTo(calculate)).unsubscribe, [channel, calculate]);
|
|
15
25
|
return deliveredTo;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { findReverse } from '../../../utils/findReverse';
|
|
3
|
+
// fixme: we should be able to retrieve last own message quickly from the LLC. Should be done when refactoring the LLC Channel state to reactive.
|
|
4
|
+
export const useLastOwnMessage = ({ messages, ownUserId, }) => useMemo(() => messages && findReverse(messages, (msg) => (msg.user && msg.user.id) === ownUserId), [messages, ownUserId]);
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
export const useLastReadData = (props) => {
|
|
3
|
-
const { channel, messages, returnAllReadData } = props;
|
|
4
|
-
return useMemo(() =>
|
|
5
|
-
|
|
6
|
-
acc
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
const { channel, lastOwnMessage, messages, returnAllReadData } = props;
|
|
4
|
+
return useMemo(() => {
|
|
5
|
+
if (returnAllReadData) {
|
|
6
|
+
return messages.reduce((acc, msg) => {
|
|
7
|
+
acc[msg.id] = channel.messageReceiptsTracker.readersForMessage({
|
|
8
|
+
msgId: msg.id,
|
|
9
|
+
timestampMs: msg.created_at.getTime(),
|
|
10
|
+
});
|
|
11
|
+
return acc;
|
|
12
|
+
}, {});
|
|
13
|
+
}
|
|
14
|
+
if (!lastOwnMessage)
|
|
15
|
+
return {};
|
|
16
|
+
return {
|
|
17
|
+
[lastOwnMessage.id]: channel.messageReceiptsTracker.readersForMessage({
|
|
18
|
+
msgId: lastOwnMessage.id,
|
|
19
|
+
timestampMs: lastOwnMessage.created_at.getTime(),
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
}, [channel, lastOwnMessage, messages, returnAllReadData]);
|
|
13
23
|
};
|
|
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import type { GroupStyle, RenderedMessage } from './utils';
|
|
4
4
|
import type { MessageProps } from '../Message';
|
|
5
|
-
import type { UserResponse } from 'stream-chat';
|
|
5
|
+
import type { LocalMessage, UserResponse } from 'stream-chat';
|
|
6
6
|
import type { ComponentContextValue, CustomClasses } from '../../context';
|
|
7
7
|
import type { ChannelUnreadUiState } from '../../types';
|
|
8
8
|
export interface RenderMessagesOptions {
|
|
@@ -19,6 +19,8 @@ export interface RenderMessagesOptions {
|
|
|
19
19
|
* Props forwarded to the Message component.
|
|
20
20
|
*/
|
|
21
21
|
sharedMessageProps: SharedMessageProps;
|
|
22
|
+
/** Latest own message in currently displayed message set. */
|
|
23
|
+
lastOwnMessage?: LocalMessage;
|
|
22
24
|
/**
|
|
23
25
|
* Current user's channel read state used to render components reflecting unread state.
|
|
24
26
|
* It does not reflect the back-end state if a channel is marked read on mount.
|
|
@@ -30,5 +32,5 @@ export interface RenderMessagesOptions {
|
|
|
30
32
|
export type SharedMessageProps = Omit<MessageProps, MessagePropsToOmit>;
|
|
31
33
|
export type MessageRenderer = (options: RenderMessagesOptions) => Array<ReactNode>;
|
|
32
34
|
type MessagePropsToOmit = 'channel' | 'deliveredTo' | 'groupStyles' | 'initialMessage' | 'lastReceivedId' | 'message' | 'readBy';
|
|
33
|
-
export declare function defaultRenderMessages({ channelUnreadUiState, components, customClasses, lastReceivedMessageId: lastReceivedId, messageGroupStyles, messages, ownMessagesDeliveredToOthers, readData, sharedMessageProps: messageProps, }: RenderMessagesOptions): React.JSX.Element[];
|
|
35
|
+
export declare function defaultRenderMessages({ channelUnreadUiState, components, customClasses, lastOwnMessage, lastReceivedMessageId: lastReceivedId, messageGroupStyles, messages, ownMessagesDeliveredToOthers, readData, sharedMessageProps: messageProps, }: RenderMessagesOptions): React.JSX.Element[];
|
|
34
36
|
export {};
|
|
@@ -4,7 +4,7 @@ import { Message } from '../Message';
|
|
|
4
4
|
import { DateSeparator as DefaultDateSeparator } from '../DateSeparator';
|
|
5
5
|
import { EventComponent as DefaultMessageSystem } from '../EventComponent';
|
|
6
6
|
import { UnreadMessagesSeparator as DefaultUnreadMessagesSeparator } from './UnreadMessagesSeparator';
|
|
7
|
-
export function defaultRenderMessages({ channelUnreadUiState, components, customClasses, lastReceivedMessageId: lastReceivedId, messageGroupStyles, messages, ownMessagesDeliveredToOthers, readData, sharedMessageProps: messageProps, }) {
|
|
7
|
+
export function defaultRenderMessages({ channelUnreadUiState, components, customClasses, lastOwnMessage, lastReceivedMessageId: lastReceivedId, messageGroupStyles, messages, ownMessagesDeliveredToOthers, readData, sharedMessageProps: messageProps, }) {
|
|
8
8
|
const { DateSeparator = DefaultDateSeparator, HeaderComponent, MessageSystem = DefaultMessageSystem, UnreadMessagesSeparator = DefaultUnreadMessagesSeparator, } = components;
|
|
9
9
|
const renderedMessages = [];
|
|
10
10
|
let firstMessage;
|
|
@@ -44,7 +44,7 @@ export function defaultRenderMessages({ channelUnreadUiState, components, custom
|
|
|
44
44
|
isFirstUnreadMessage && UnreadMessagesSeparator && (React.createElement("li", { className: 'str-chat__li str-chat__unread-messages-separator-wrapper' },
|
|
45
45
|
React.createElement(UnreadMessagesSeparator, { unreadCount: channelUnreadUiState?.unread_messages }))),
|
|
46
46
|
React.createElement("li", { className: messageClass, "data-message-id": message.id, "data-testid": messageClass },
|
|
47
|
-
React.createElement(Message, { deliveredTo: ownMessagesDeliveredToOthers[message.id] || [], groupStyles: [groupStyles], lastReceivedId: lastReceivedId, message: message, readBy: readData[message.id] || [], ...messageProps }))));
|
|
47
|
+
React.createElement(Message, { deliveredTo: ownMessagesDeliveredToOthers[message.id] || [], groupStyles: [groupStyles], lastOwnMessage: lastOwnMessage, lastReceivedId: lastReceivedId, message: message, readBy: readData[message.id] || [], ...messageProps }))));
|
|
48
48
|
previousMessage = message;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -92,6 +92,8 @@ export type MessageContextValue = {
|
|
|
92
92
|
* A factory function that determines whether a message is AI generated or not.
|
|
93
93
|
*/
|
|
94
94
|
isMessageAIGenerated?: (message: LocalMessage) => boolean;
|
|
95
|
+
/** Latest own message in currently displayed message set. */
|
|
96
|
+
lastOwnMessage?: LocalMessage;
|
|
95
97
|
/** Latest message id on current channel */
|
|
96
98
|
lastReceivedId?: string | null;
|
|
97
99
|
/** DOMRect object for parent MessageList component */
|
|
@@ -106,6 +108,8 @@ export type MessageContextValue = {
|
|
|
106
108
|
readBy?: UserResponse[];
|
|
107
109
|
/** Custom function to render message text content, defaults to the renderText function: [utils](https://github.com/GetStream/stream-chat-react/blob/master/src/utils.tsx) */
|
|
108
110
|
renderText?: (text?: string, mentioned_users?: UserResponse[], options?: RenderTextOptions) => ReactNode;
|
|
111
|
+
/** Keep track of read receipts for each message sent by the user. When disabled, only the last own message delivery / read status is rendered. */
|
|
112
|
+
returnAllReadData?: boolean;
|
|
109
113
|
/** Comparator function to sort the list of reacted users
|
|
110
114
|
* @deprecated use `reactionDetailsSort` instead
|
|
111
115
|
*/
|