stream-chat-react 13.8.1 → 13.9.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 (66) hide show
  1. package/dist/components/Channel/Channel.d.ts +2 -2
  2. package/dist/components/Channel/Channel.js +3 -3
  3. package/dist/components/Channel/hooks/useCreateChannelStateContext.js +3 -1
  4. package/dist/components/ChannelPreview/hooks/useMessageDeliveryStatus.d.ts +1 -0
  5. package/dist/components/ChannelPreview/hooks/useMessageDeliveryStatus.js +26 -14
  6. package/dist/components/Chat/hooks/useChat.js +1 -1
  7. package/dist/components/Message/Message.js +1 -1
  8. package/dist/components/Message/MessageStatus.d.ts +1 -0
  9. package/dist/components/Message/MessageStatus.js +22 -12
  10. package/dist/components/Message/hooks/useDeleteHandler.js +2 -2
  11. package/dist/components/Message/icons.d.ts +1 -0
  12. package/dist/components/Message/icons.js +6 -2
  13. package/dist/components/Message/types.d.ts +2 -0
  14. package/dist/components/Message/utils.d.ts +4 -1
  15. package/dist/components/Message/utils.js +9 -0
  16. package/dist/components/MessageActions/MessageActionsBox.js +3 -1
  17. package/dist/components/MessageList/MessageList.js +2 -2
  18. package/dist/components/MessageList/VirtualizedMessageList.d.ts +2 -0
  19. package/dist/components/MessageList/VirtualizedMessageList.js +10 -4
  20. package/dist/components/MessageList/VirtualizedMessageListComponents.js +2 -2
  21. package/dist/components/MessageList/hooks/MessageList/useMessageListElements.d.ts +2 -2
  22. package/dist/components/MessageList/hooks/MessageList/useMessageListElements.js +13 -5
  23. package/dist/components/MessageList/hooks/useLastDeliveredData.d.ts +8 -0
  24. package/dist/components/MessageList/hooks/useLastDeliveredData.js +13 -0
  25. package/dist/components/MessageList/hooks/useLastReadData.d.ts +3 -8
  26. package/dist/components/MessageList/hooks/useLastReadData.js +10 -7
  27. package/dist/components/MessageList/renderMessages.d.ts +5 -4
  28. package/dist/components/MessageList/renderMessages.js +2 -2
  29. package/dist/components/MessageList/utils.d.ts +1 -5
  30. package/dist/components/MessageList/utils.js +0 -30
  31. package/dist/context/ChannelActionContext.d.ts +2 -2
  32. package/dist/context/MessageContext.d.ts +5 -3
  33. package/dist/css/v2/index.css +1 -1
  34. package/dist/css/v2/index.layout.css +1 -1
  35. package/dist/experimental/index.browser.cjs +19 -13
  36. package/dist/experimental/index.browser.cjs.map +2 -2
  37. package/dist/experimental/index.node.cjs +19 -13
  38. package/dist/experimental/index.node.cjs.map +2 -2
  39. package/dist/i18n/Streami18n.d.ts +2 -0
  40. package/dist/i18n/de.json +2 -0
  41. package/dist/i18n/en.json +2 -0
  42. package/dist/i18n/es.json +2 -0
  43. package/dist/i18n/fr.json +2 -0
  44. package/dist/i18n/hi.json +2 -0
  45. package/dist/i18n/it.json +2 -0
  46. package/dist/i18n/ja.json +2 -0
  47. package/dist/i18n/ko.json +2 -0
  48. package/dist/i18n/nl.json +2 -0
  49. package/dist/i18n/pt.json +2 -0
  50. package/dist/i18n/ru.json +2 -0
  51. package/dist/i18n/tr.json +2 -0
  52. package/dist/index.browser.cjs +984 -885
  53. package/dist/index.browser.cjs.map +4 -4
  54. package/dist/index.node.cjs +986 -886
  55. package/dist/index.node.cjs.map +4 -4
  56. package/dist/plugins/Emojis/index.browser.cjs.map +2 -2
  57. package/dist/plugins/Emojis/index.node.cjs.map +2 -2
  58. package/dist/scss/v2/Dialog/Dialog-layout.scss +12 -3
  59. package/dist/scss/v2/Icon/Icon-layout.scss +6 -0
  60. package/dist/scss/v2/Icon/Icon-theme.scss +4 -0
  61. package/dist/scss/v2/Message/Message-layout.scss +30 -3
  62. package/dist/scss/v2/Message/Message-theme.scss +9 -0
  63. package/dist/scss/v2/MessageInput/MessageInput-layout.scss +8 -0
  64. package/dist/scss/v2/MessageInput/MessageInput-theme.scss +2 -1
  65. package/dist/scss/v2/Poll/Poll-layout.scss +29 -7
  66. package/package.json +4 -4
@@ -1,6 +1,6 @@
1
1
  import type { PropsWithChildren } from 'react';
2
2
  import React from 'react';
3
- import type { ChannelQueryOptions, EventAPIResponse, LocalMessage, Message, MessageResponse, SendMessageOptions, Channel as StreamChannel, StreamChat, UpdateMessageOptions } from 'stream-chat';
3
+ import type { ChannelQueryOptions, DeleteMessageOptions, EventAPIResponse, LocalMessage, Message, MessageResponse, SendMessageOptions, Channel as StreamChannel, StreamChat, UpdateMessageOptions } from 'stream-chat';
4
4
  import type { OnMentionAction } from './hooks/useMentionsHandlers';
5
5
  import type { LoadingErrorIndicatorProps } from '../Loading';
6
6
  import type { ComponentContextValue } from '../../context';
@@ -19,7 +19,7 @@ export type ChannelProps = ChannelPropsForwardedToComponentContext & {
19
19
  */
20
20
  channelQueryOptions?: ChannelQueryOptions;
21
21
  /** Custom action handler to override the default `client.deleteMessage(message.id)` function */
22
- doDeleteMessageRequest?: (message: LocalMessage) => Promise<MessageResponse>;
22
+ doDeleteMessageRequest?: (message: LocalMessage, options?: DeleteMessageOptions) => Promise<MessageResponse>;
23
23
  /** Custom action handler to override the default `channel.markRead` request function (advanced usage only) */
24
24
  doMarkReadRequest?: (channel: StreamChannel, setChannelUnreadUiState?: (state: ChannelUnreadUiState) => void) => Promise<EventAPIResponse> | void;
25
25
  /** Custom action handler to override the default `channel.sendMessage` request function (advanced usage only) */
@@ -500,16 +500,16 @@ const ChannelInner = (props) => {
500
500
  t,
501
501
  channelUnreadUiState,
502
502
  ]);
503
- const deleteMessage = useCallback(async (message) => {
503
+ const deleteMessage = useCallback(async (message, options) => {
504
504
  if (!message?.id) {
505
505
  throw new Error('Cannot delete a message - missing message ID.');
506
506
  }
507
507
  let deletedMessage;
508
508
  if (doDeleteMessageRequest) {
509
- deletedMessage = await doDeleteMessageRequest(message);
509
+ deletedMessage = await doDeleteMessageRequest(message, options);
510
510
  }
511
511
  else {
512
- const result = await client.deleteMessage(message.id);
512
+ const result = await client.deleteMessage(message.id, options);
513
513
  deletedMessage = result.message;
514
514
  }
515
515
  return deletedMessage;
@@ -16,10 +16,12 @@ export const useCreateChannelStateContext = (value) => {
16
16
  channelCapabilitiesArray.forEach((capability) => {
17
17
  channelCapabilities[capability] = true;
18
18
  });
19
+ // FIXME: this is crazy - I could not find out why the messages were not getting updated when only message properties that are not part
20
+ // of this serialization has been changed. A great example of memoization gone wrong.
19
21
  const memoizedMessageData = skipMessageDataMemoization
20
22
  ? messages
21
23
  : messages
22
- .map(({ deleted_at, latest_reactions, pinned, reply_count, status, updated_at, user, }) => `${deleted_at}${latest_reactions ? latest_reactions.map(({ type }) => type).join() : ''}${pinned}${reply_count}${status}${updated_at && (isDayOrMoment(updated_at) || isDate(updated_at))
24
+ .map(({ deleted_at, latest_reactions, pinned, reply_count, status, type, updated_at, user, }) => `${type}${deleted_at}${latest_reactions ? latest_reactions.map(({ type }) => type).join() : ''}${pinned}${reply_count}${status}${updated_at && (isDayOrMoment(updated_at) || isDate(updated_at))
23
25
  ? updated_at.toISOString()
24
26
  : updated_at || ''}${user?.updated_at}`)
25
27
  .join();
@@ -1,5 +1,6 @@
1
1
  import type { Channel, LocalMessage } from 'stream-chat';
2
2
  export declare enum MessageDeliveryStatus {
3
+ SENT = "sent",
3
4
  DELIVERED = "delivered",
4
5
  READ = "read"
5
6
  }
@@ -2,53 +2,65 @@ import { useCallback, useEffect, useState } from 'react';
2
2
  import { useChatContext } from '../../../context';
3
3
  export var MessageDeliveryStatus;
4
4
  (function (MessageDeliveryStatus) {
5
+ MessageDeliveryStatus["SENT"] = "sent";
5
6
  MessageDeliveryStatus["DELIVERED"] = "delivered";
6
7
  MessageDeliveryStatus["READ"] = "read";
7
8
  })(MessageDeliveryStatus || (MessageDeliveryStatus = {}));
8
9
  export const useMessageDeliveryStatus = ({ channel, lastMessage, }) => {
9
10
  const { client } = useChatContext();
10
11
  const [messageDeliveryStatus, setMessageDeliveryStatus] = useState();
11
- const isOwnMessage = useCallback((message) => client.user && message?.user?.id === client.user.id, [client]);
12
+ const isOwnMessage = useCallback((message) => client.user && message && message.user?.id === client.user.id, [client]);
12
13
  useEffect(() => {
14
+ // empty channel
15
+ if (!lastMessage) {
16
+ setMessageDeliveryStatus(undefined);
17
+ }
13
18
  const lastMessageIsOwn = isOwnMessage(lastMessage);
14
19
  if (!lastMessage?.created_at || !lastMessageIsOwn)
15
20
  return;
16
- const lastMessageCreatedAtDate = typeof lastMessage.created_at === 'string'
17
- ? new Date(lastMessage.created_at)
18
- : lastMessage.created_at;
19
- const channelReadByOthersAfterLastMessageUpdate = Object.values(channel.state.read).some(({ last_read: channelLastMarkedReadDate, user }) => {
20
- const ignoreOwnReadStatus = client.user && user.id !== client.user.id;
21
- return ignoreOwnReadStatus && lastMessageCreatedAtDate < channelLastMarkedReadDate;
22
- });
23
- setMessageDeliveryStatus(channelReadByOthersAfterLastMessageUpdate
21
+ const msgRef = {
22
+ msgId: lastMessage.id,
23
+ timestampMs: lastMessage.created_at.getTime(),
24
+ };
25
+ setMessageDeliveryStatus(channel.messageReceiptsTracker.readersForMessage(msgRef).length > 0
24
26
  ? MessageDeliveryStatus.READ
25
- : MessageDeliveryStatus.DELIVERED);
26
- }, [channel.state.read, client, isOwnMessage, lastMessage]);
27
+ : channel.messageReceiptsTracker.deliveredForMessage(msgRef).length > 0
28
+ ? MessageDeliveryStatus.DELIVERED
29
+ : MessageDeliveryStatus.SENT);
30
+ }, [channel, isOwnMessage, lastMessage]);
27
31
  useEffect(() => {
28
32
  const handleMessageNew = (event) => {
29
33
  // the last message is not mine, so do not show the delivery status
30
34
  if (!isOwnMessage(event.message)) {
31
35
  return setMessageDeliveryStatus(undefined);
32
36
  }
33
- return setMessageDeliveryStatus(MessageDeliveryStatus.DELIVERED);
37
+ return setMessageDeliveryStatus(MessageDeliveryStatus.SENT);
34
38
  };
35
39
  channel.on('message.new', handleMessageNew);
36
40
  return () => {
37
41
  channel.off('message.new', handleMessageNew);
38
42
  };
39
- }, [channel, client, isOwnMessage]);
43
+ }, [channel, isOwnMessage]);
40
44
  useEffect(() => {
41
45
  if (!isOwnMessage(lastMessage))
42
46
  return;
47
+ const handleMessageDelivered = (event) => {
48
+ if (event.user?.id !== client.user?.id &&
49
+ lastMessage &&
50
+ lastMessage.id === event.last_delivered_message_id)
51
+ setMessageDeliveryStatus(MessageDeliveryStatus.DELIVERED);
52
+ };
43
53
  const handleMarkRead = (event) => {
44
54
  if (event.user?.id !== client.user?.id)
45
55
  setMessageDeliveryStatus(MessageDeliveryStatus.READ);
46
56
  };
57
+ channel.on('message.delivered', handleMessageDelivered);
47
58
  channel.on('message.read', handleMarkRead);
48
59
  return () => {
60
+ channel.off('message.delivered', handleMessageDelivered);
49
61
  channel.off('message.read', handleMarkRead);
50
62
  };
51
- }, [channel, client, lastMessage, isOwnMessage]);
63
+ }, [channel, client, isOwnMessage, lastMessage]);
52
64
  return {
53
65
  messageDeliveryStatus,
54
66
  };
@@ -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.8.1";
27
+ const version = "13.9.0";
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, 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, 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 }));
120
120
  };
@@ -6,6 +6,7 @@ export type MessageStatusProps = {
6
6
  MessageDeliveredStatus?: React.ComponentType;
7
7
  MessageReadStatus?: React.ComponentType;
8
8
  MessageSendingStatus?: React.ComponentType;
9
+ MessageSentStatus?: React.ComponentType;
9
10
  messageType?: string;
10
11
  tooltipUserNameMapper?: TooltipUsernameMapper;
11
12
  };
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from 'react';
2
2
  import clsx from 'clsx';
3
- import { MessageDeliveredIcon } from './icons';
3
+ import { MessageDeliveredIcon, MessageSentIcon } from './icons';
4
4
  import { getReadByTooltipText, mapToUserNameOrId } from './utils';
5
5
  import { Avatar as DefaultAvatar } from '../Avatar';
6
6
  import { LoadingIndicator } from '../Loading';
@@ -11,40 +11,50 @@ import { useComponentContext } from '../../context/ComponentContext';
11
11
  import { useMessageContext } from '../../context/MessageContext';
12
12
  import { useTranslationContext } from '../../context/TranslationContext';
13
13
  const UnMemoizedMessageStatus = (props) => {
14
- const { Avatar: propAvatar, MessageDeliveredStatus, MessageReadStatus, MessageSendingStatus, messageType = 'simple', tooltipUserNameMapper = mapToUserNameOrId, } = props;
14
+ const { Avatar: propAvatar, MessageDeliveredStatus, MessageReadStatus, MessageSendingStatus, MessageSentStatus, messageType = 'simple', tooltipUserNameMapper = mapToUserNameOrId, } = props;
15
15
  const { handleEnter, handleLeave, tooltipVisible } = useEnterLeaveHandlers();
16
16
  const { client } = useChatContext('MessageStatus');
17
17
  const { Avatar: contextAvatar } = useComponentContext('MessageStatus');
18
- const { isMyMessage, lastReceivedId, message, readBy, threadList } = useMessageContext('MessageStatus');
18
+ const { deliveredTo, isMyMessage, message, readBy, threadList } = useMessageContext('MessageStatus');
19
19
  const { t } = useTranslationContext('MessageStatus');
20
20
  const [referenceElement, setReferenceElement] = useState(null);
21
21
  const Avatar = propAvatar || contextAvatar || DefaultAvatar;
22
22
  if (!isMyMessage() || message.type === 'error')
23
23
  return null;
24
24
  const justReadByMe = readBy?.length === 1 && readBy[0].id === client.user?.id;
25
- const rootClassName = `str-chat__message-${messageType}-status str-chat__message-status`;
25
+ const deliveredOnlyToMe = deliveredTo?.length === 1 && deliveredTo[0].id === client.user?.id;
26
26
  const sending = message.status === 'sending';
27
- const delivered = message.status === 'received' && message.id === lastReceivedId && !threadList;
28
- const deliveredAndRead = !!(readBy?.length && !threadList && !justReadByMe);
29
- const readersWithoutOwnUser = deliveredAndRead
27
+ const read = !!(readBy?.length && !justReadByMe && !threadList);
28
+ const delivered = !!(deliveredTo?.length && !deliveredOnlyToMe && !read && !threadList);
29
+ const sent = message.status === 'received' && !delivered && !read && !threadList;
30
+ const readersWithoutOwnUser = read
30
31
  ? readBy.filter((item) => item.id !== client.user?.id)
31
32
  : [];
32
33
  const [lastReadUser] = readersWithoutOwnUser;
33
- return (React.createElement("span", { className: rootClassName, "data-testid": clsx({
34
- 'message-status-read-by': deliveredAndRead,
35
- 'message-status-received': delivered && !deliveredAndRead,
34
+ return (React.createElement("span", { className: clsx(`str-chat__message-${messageType}-status str-chat__message-status`, {
35
+ 'str-chat__message-status-delivered': delivered,
36
+ 'str-chat__message-status-read-by': read,
37
+ 'str-chat__message-status-sending': sending,
38
+ 'str-chat__message-status-sent': sent,
39
+ }), "data-testid": clsx({
40
+ 'message-status-delivered': delivered,
41
+ 'message-status-read-by': read,
36
42
  'message-status-sending': sending,
43
+ 'message-status-sent': sent,
37
44
  }), onMouseEnter: handleEnter, onMouseLeave: handleLeave, ref: setReferenceElement },
38
45
  sending &&
39
46
  (MessageSendingStatus ? (React.createElement(MessageSendingStatus, null)) : (React.createElement(React.Fragment, null,
40
47
  React.createElement(PopperTooltip, { offset: [0, 5], referenceElement: referenceElement, visible: tooltipVisible }, t('Sending...')),
41
48
  React.createElement(LoadingIndicator, null)))),
49
+ sent &&
50
+ (MessageSentStatus ? (React.createElement(MessageSentStatus, null)) : (React.createElement(React.Fragment, null,
51
+ React.createElement(PopperTooltip, { offset: [0, 5], referenceElement: referenceElement, visible: tooltipVisible }, t('Sent')),
52
+ React.createElement(MessageSentIcon, null)))),
42
53
  delivered &&
43
- !deliveredAndRead &&
44
54
  (MessageDeliveredStatus ? (React.createElement(MessageDeliveredStatus, null)) : (React.createElement(React.Fragment, null,
45
55
  React.createElement(PopperTooltip, { offset: [0, 5], referenceElement: referenceElement, visible: tooltipVisible }, t('Delivered')),
46
56
  React.createElement(MessageDeliveredIcon, null)))),
47
- deliveredAndRead &&
57
+ read &&
48
58
  (MessageReadStatus ? (React.createElement(MessageReadStatus, null)) : (React.createElement(React.Fragment, null,
49
59
  React.createElement(PopperTooltip, { offset: [0, 5], referenceElement: referenceElement, visible: tooltipVisible }, getReadByTooltipText(readBy, t, client, tooltipUserNameMapper)),
50
60
  React.createElement(Avatar, { className: 'str-chat__avatar--message-status', image: lastReadUser.image, name: lastReadUser.name || lastReadUser.id, user: lastReadUser }),
@@ -7,13 +7,13 @@ export const useDeleteHandler = (message, notifications = {}) => {
7
7
  const { deleteMessage, updateMessage } = useChannelActionContext('useDeleteHandler');
8
8
  const { client } = useChatContext('useDeleteHandler');
9
9
  const { t } = useTranslationContext('useDeleteHandler');
10
- return async (event) => {
10
+ return async (event, options) => {
11
11
  event.preventDefault();
12
12
  if (!message?.id || !client || !updateMessage) {
13
13
  return;
14
14
  }
15
15
  try {
16
- const deletedMessage = await deleteMessage(message);
16
+ const deletedMessage = await deleteMessage(message, options);
17
17
  updateMessage(deletedMessage);
18
18
  }
19
19
  catch (e) {
@@ -6,5 +6,6 @@ export declare const ReactionIcon: ({ className }: IconProps) => React.JSX.Eleme
6
6
  export declare const ThreadIcon: ({ className }: IconProps) => React.JSX.Element;
7
7
  export declare const PinIcon: () => React.JSX.Element;
8
8
  export declare const PinIndicator: ({ message, t }: PinIndicatorProps) => React.JSX.Element | null;
9
+ export declare const MessageSentIcon: () => React.JSX.Element;
9
10
  export declare const MessageDeliveredIcon: () => React.JSX.Element;
10
11
  export declare const MessageErrorIcon: () => React.JSX.Element;
@@ -23,8 +23,12 @@ export const PinIndicator = ({ message, t }) => {
23
23
  ? `${t('Pinned by')} ${message.pinned_by?.name || message.pinned_by?.id}`
24
24
  : t('Message pinned'))));
25
25
  };
26
- export const MessageDeliveredIcon = () => (React.createElement("svg", { "data-testid": 'delivered-icon', fill: 'none', height: '24', viewBox: '0 0 24 24', width: '24', xmlns: 'http://www.w3.org/2000/svg' },
27
- React.createElement("path", { clipRule: 'evenodd', d: 'M8.9999 16.2L4.7999 12L3.3999 13.4L8.9999 19L20.9999 6.99998L19.5999 5.59998L8.9999 16.2Z', fill: 'black', fillRule: 'evenodd' })));
26
+ export const MessageSentIcon = () => (React.createElement("svg", { "data-testid": 'message-sent-icon', fill: 'currentColor', viewBox: '0 0 10 8', xmlns: 'http://www.w3.org/2000/svg' },
27
+ React.createElement("path", { clipRule: 'evenodd', d: 'M9.47116 1.80482C9.73151 1.54447 9.73151 1.12236 9.47116 0.862011C9.21081 0.601661 8.7887 0.601661 8.52835 0.862011L3.66646 5.7239L1.47108 3.52851C1.21073 3.26816 0.788619 3.26816 0.52827 3.52851C0.26792 3.78886 0.26792 4.21097 0.52827 4.47132L3.18877 7.13182C3.19083 7.13394 3.19292 7.13605 3.19502 7.13815C3.45537 7.3985 3.87748 7.3985 4.13783 7.13815L9.47116 1.80482Z', fillRule: 'evenodd' })));
28
+ export const MessageDeliveredIcon = () => (React.createElement("svg", { "data-testid": 'delivered-icon', fill: 'currentColor', viewBox: '0 0 14 8', xmlns: 'http://www.w3.org/2000/svg' },
29
+ React.createElement("path", { clipRule: 'evenodd', d: 'M9.50041 0.862011C9.76149 1.12236 9.76149 1.54447 9.50041 1.80482L6.63046 4.66681L7.69051 5.72392L12.566 0.862011C12.827 0.601661 13.2503 0.601661 13.5114 0.862011C13.7725 1.12236 13.7725 1.54447 13.5114 1.80482L8.16321 7.13815C7.90214 7.3985 7.47885 7.3985 7.21778 7.13815C7.2164 7.13678 7.21502 7.13539 7.21366 7.13401L5.68502 5.60962L4.15223 7.13815C3.89115 7.3985 3.46787 7.3985 3.20679 7.13815L3.19746 7.12866L0.53272 4.47132C0.271645 4.21097 0.271645 3.78886 0.53272 3.52851C0.793794 3.26816 1.21708 3.26816 1.47815 3.52851L3.6796 5.72385L5.20067 4.207L5.21216 4.19526L5.22393 4.1838L8.55498 0.862011C8.81605 0.601661 9.23934 0.601661 9.50041 0.862011Z',
30
+ // fill='#005DFF'
31
+ fillRule: 'evenodd' })));
28
32
  export const MessageErrorIcon = () => (React.createElement("div", { className: 'str-chat__message-error-icon' },
29
33
  React.createElement("svg", { "data-testid": 'error', fill: 'none', height: '24', viewBox: '0 0 24 24', width: '24', xmlns: 'http://www.w3.org/2000/svg' },
30
34
  React.createElement("path", { d: 'M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2Z', fill: 'black', id: 'background' }),
@@ -23,6 +23,8 @@ export type MessageProps = {
23
23
  closeReactionSelectorOnClick?: boolean;
24
24
  /** Object containing custom message actions and function handlers */
25
25
  customMessageActions?: MessageContextValue['customMessageActions'];
26
+ /** An array of user IDs that have confirmed the message delivery to their device */
27
+ deliveredTo?: UserResponse[];
26
28
  /** If true, disables the ability for users to quote messages, defaults to false */
27
29
  disableQuotedMessages?: boolean;
28
30
  /** When true, the message is the last one in a group sent by a specific user (only used in the `VirtualizedMessageList`) */
@@ -12,6 +12,9 @@ export declare const validateAndGetMessage: <T extends unknown[]>(func: (...args
12
12
  * Tell if the owner of the current message is muted
13
13
  */
14
14
  export declare const isUserMuted: (message: LocalMessage, mutes?: Mute[]) => boolean;
15
+ export declare const OPTIONAL_MESSAGE_ACTIONS: {
16
+ deleteForMe: string;
17
+ };
15
18
  export declare const MESSAGE_ACTIONS: {
16
19
  delete: string;
17
20
  edit: string;
@@ -25,7 +28,7 @@ export declare const MESSAGE_ACTIONS: {
25
28
  reply: string;
26
29
  saveForLater: string;
27
30
  };
28
- export type MessageActionsArray<T extends string = string> = Array<keyof typeof MESSAGE_ACTIONS | T>;
31
+ export type MessageActionsArray<T extends string = string> = Array<keyof typeof MESSAGE_ACTIONS | keyof typeof OPTIONAL_MESSAGE_ACTIONS | T>;
29
32
  export declare const defaultPinPermissions: PinPermissions;
30
33
  export type Capabilities = {
31
34
  canDelete?: boolean;
@@ -26,6 +26,9 @@ export const isUserMuted = (message, mutes) => {
26
26
  const userMuted = mutes.filter((el) => el.target.id === message.user?.id);
27
27
  return !!userMuted.length;
28
28
  };
29
+ export const OPTIONAL_MESSAGE_ACTIONS = {
30
+ deleteForMe: 'deleteForMe',
31
+ };
29
32
  export const MESSAGE_ACTIONS = {
30
33
  delete: 'delete',
31
34
  edit: 'edit',
@@ -113,6 +116,9 @@ export const getMessageActions = (actions, { canDelete, canEdit, canFlag, canMar
113
116
  if (canDelete && messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1) {
114
117
  messageActionsAfterPermission.push(MESSAGE_ACTIONS.delete);
115
118
  }
119
+ if (canDelete && messageActions.indexOf(OPTIONAL_MESSAGE_ACTIONS.deleteForMe) > -1) {
120
+ messageActionsAfterPermission.push(OPTIONAL_MESSAGE_ACTIONS.deleteForMe);
121
+ }
116
122
  if (canEdit && messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1) {
117
123
  messageActionsAfterPermission.push(MESSAGE_ACTIONS.edit);
118
124
  }
@@ -213,6 +219,7 @@ export const areMessagePropsEqual = (prevProps, nextProps) => {
213
219
  return false;
214
220
  const deepEqualProps = deepequal(nextProps.messageActions, prevProps.messageActions) &&
215
221
  deepequal(nextProps.readBy, prevProps.readBy) &&
222
+ deepequal(nextProps.deliveredTo, prevProps.deliveredTo) &&
216
223
  deepequal(nextProps.highlighted, prevProps.highlighted) &&
217
224
  deepequal(nextProps.groupStyles, prevProps.groupStyles) && // last 3 messages can have different group styles
218
225
  deepequal(nextProps.mutes, prevProps.mutes) &&
@@ -235,6 +242,8 @@ export const areMessageUIPropsEqual = (prevProps, nextProps) => {
235
242
  return false;
236
243
  if (prevProps.readBy?.length !== nextProps.readBy?.length)
237
244
  return false;
245
+ if (prevProps.deliveredTo?.length !== nextProps.deliveredTo?.length)
246
+ return false;
238
247
  if (prevProps.groupStyles !== nextProps.groupStyles)
239
248
  return false;
240
249
  if (prevProps.showDetailedReactions !== nextProps.showDetailedReactions) {
@@ -2,7 +2,7 @@ import clsx from 'clsx';
2
2
  import React from 'react';
3
3
  import { CustomMessageActionsList as DefaultCustomMessageActionsList } from './CustomMessageActionsList';
4
4
  import { RemindMeActionButton } from './RemindMeSubmenu';
5
- import { useMessageReminder } from '../Message';
5
+ import { OPTIONAL_MESSAGE_ACTIONS, useMessageReminder } from '../Message';
6
6
  import { useMessageComposer } from '../MessageInput';
7
7
  import { useChatContext, useComponentContext, useMessageContext, useTranslationContext, } from '../../context';
8
8
  import { MESSAGE_ACTIONS } from '../Message/utils';
@@ -41,6 +41,8 @@ const UnMemoizedMessageActionsBox = (props) => {
41
41
  messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleMute, role: 'option' }, isUserMuted() ? t('Unmute') : t('Mute'))),
42
42
  messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleEdit, role: 'option' }, t('Edit Message'))),
43
43
  messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleDelete, role: 'option' }, t('Delete'))),
44
+ messageActions.indexOf(OPTIONAL_MESSAGE_ACTIONS.deleteForMe) > -1 &&
45
+ !message.deleted_for_me && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: (e) => handleDelete(e, { deleteForMe: true }), role: 'option' }, t('Delete for me'))),
44
46
  messageActions.indexOf(MESSAGE_ACTIONS.remindMe) > -1 && (React.createElement(RemindMeActionButton, { className: buttonClassName, isMine: mine })),
45
47
  messageActions.indexOf(MESSAGE_ACTIONS.saveForLater) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: () => reminder
46
48
  ? client.reminders.deleteReminder(reminder.id)
@@ -22,7 +22,7 @@ import { useStableId } from '../UtilityComponents/useStableId';
22
22
  import { DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, DEFAULT_NEXT_CHANNEL_PAGE_SIZE, } from '../../constants/limits';
23
23
  const MessageListWithContext = (props) => {
24
24
  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
- maxTimeBetweenGroupedMessages, messageActions = Object.keys(MESSAGE_ACTIONS), messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, messages = [], noGroupByUser = false, notifications, pinPermissions = defaultPinPermissions, reactionDetailsSort, read, renderMessages = defaultRenderMessages, returnAllReadData = false, reviewProcessedMessage, showUnreadNotificationAlways, sortReactionDetails, sortReactions, suppressAutoscroll, threadList = false, unsafeHTML = false, } = props;
25
+ 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;
26
26
  const [listElement, setListElement] = React.useState(null);
27
27
  const [ulElement, setUlElement] = React.useState(null);
28
28
  const { customClasses } = useChatContext('MessageList');
@@ -92,7 +92,7 @@ const MessageListWithContext = (props) => {
92
92
  unsafeHTML,
93
93
  },
94
94
  messageGroupStyles,
95
- read,
95
+ messages,
96
96
  renderMessages,
97
97
  returnAllReadData,
98
98
  threadList,
@@ -21,6 +21,8 @@ export type VirtuosoContext = Required<Pick<ComponentContextValue, 'DateSeparato
21
21
  messageGroupStyles: Record<string, GroupStyle>;
22
22
  /** Number of messages prepended before the first page of messages. This is needed to calculate the virtual position in the virtual list. */
23
23
  numItemsPrepended: number;
24
+ /** Mapping of message ID of own messages to the array of users, who were delivered the given message */
25
+ ownMessagesDeliveredToOthers: Record<string, UserResponse[]>;
24
26
  /** Mapping of message ID of own messages to the array of users, who read the given message */
25
27
  ownMessagesReadByOthers: Record<string, UserResponse[]>;
26
28
  /** The original message list enriched with date separators, omitted deleted messages or giphy previews. */
@@ -22,6 +22,7 @@ import { useComponentContext } from '../../context/ComponentContext';
22
22
  import { VirtualizedMessageListContextProvider } from '../../context/VirtualizedMessageListContext';
23
23
  import { DEFAULT_NEXT_CHANNEL_PAGE_SIZE } from '../../constants/limits';
24
24
  import { useStableId } from '../UtilityComponents/useStableId';
25
+ import { useLastDeliveredData } from './hooks/useLastDeliveredData';
25
26
  function captureResizeObserverExceededError(e) {
26
27
  if (e.message === 'ResizeObserver loop completed with undelivered notifications.' ||
27
28
  e.message === 'ResizeObserver loop limit exceeded') {
@@ -54,7 +55,7 @@ function calculateInitialTopMostItemIndex(messages, highlightedMessageId) {
54
55
  const VirtualizedMessageListWithContext = (props) => {
55
56
  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,
56
57
  // TODO: refactor to scrollSeekPlaceHolderConfiguration and components.ScrollSeekPlaceholder, like the Virtuoso Component
57
- overscan = 0, reactionDetailsSort, read, returnAllReadData = false, reviewProcessedMessage, scrollSeekPlaceHolder, scrollToLatestMessageOnFocus = false, separateGiphyPreview = false, shouldGroupByUser = false, showUnreadNotificationAlways, sortReactionDetails, sortReactions, stickToBottomScrollBehavior = 'smooth', suppressAutoscroll, threadList, } = props;
58
+ overscan = 0, reactionDetailsSort, returnAllReadData = false, reviewProcessedMessage, scrollSeekPlaceHolder, scrollToLatestMessageOnFocus = false, separateGiphyPreview = false, shouldGroupByUser = false, showUnreadNotificationAlways, sortReactionDetails, sortReactions, stickToBottomScrollBehavior = 'smooth', suppressAutoscroll, threadList, } = props;
58
59
  const { components: virtuosoComponentsFromProps, ...overridingVirtuosoProps } = additionalVirtuosoProps;
59
60
  // Stops errors generated from react-virtuoso to bubble up
60
61
  // to Sentry or other tracking tools.
@@ -102,10 +103,14 @@ const VirtualizedMessageListWithContext = (props) => {
102
103
  ]);
103
104
  // get the mapping of own messages to array of users who read them
104
105
  const ownMessagesReadByOthers = useLastReadData({
105
- messages: processedMessages,
106
- read,
106
+ channel,
107
+ messages: messages || [],
108
+ returnAllReadData,
109
+ });
110
+ const ownMessagesDeliveredToOthers = useLastDeliveredData({
111
+ channel,
112
+ messages: messages || [],
107
113
  returnAllReadData,
108
- userID: client.userID,
109
114
  });
110
115
  const lastReceivedMessageId = useMemo(() => getLastReceived(processedMessages), [processedMessages]);
111
116
  const groupStylesFn = groupStyles || getGroupStyles;
@@ -231,6 +236,7 @@ const VirtualizedMessageListWithContext = (props) => {
231
236
  MessageSystem,
232
237
  numItemsPrepended,
233
238
  openThread,
239
+ ownMessagesDeliveredToOthers,
234
240
  ownMessagesReadByOthers,
235
241
  processedMessages,
236
242
  reactionDetailsSort,
@@ -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, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, shouldGroupByUser, sortReactionDetails, sortReactions, threadList, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = 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;
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, 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] ?? ''], lastReceivedId: lastReceivedMessageId, message: message, Message: MessageUIComponent, messageActions: messageActions, openThread: openThread, reactionDetailsSort: reactionDetailsSort, readBy: ownMessagesReadByOthers[message.id] || [], sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: threadList })));
92
92
  };
@@ -1,9 +1,10 @@
1
1
  import type React from 'react';
2
- import type { ChannelState as StreamChannelState } from 'stream-chat';
3
2
  import type { GroupStyle, RenderedMessage } from '../../utils';
3
+ import type { LocalMessage } from 'stream-chat';
4
4
  import type { ChannelUnreadUiState } from '../../../../types/types';
5
5
  import type { MessageRenderer, SharedMessageProps } from '../../renderMessages';
6
6
  type UseMessageListElementsProps = {
7
+ messages: LocalMessage[];
7
8
  enrichedMessages: RenderedMessage[];
8
9
  internalMessageProps: SharedMessageProps;
9
10
  messageGroupStyles: Record<string, GroupStyle>;
@@ -11,7 +12,6 @@ type UseMessageListElementsProps = {
11
12
  returnAllReadData: boolean;
12
13
  threadList: boolean;
13
14
  channelUnreadUiState?: ChannelUnreadUiState;
14
- read?: StreamChannelState['read'];
15
15
  };
16
16
  export declare const useMessageListElements: (props: UseMessageListElementsProps) => React.ReactNode[];
17
17
  export {};
@@ -3,16 +3,23 @@ import { useLastReadData } from '../useLastReadData';
3
3
  import { getLastReceived } from '../../utils';
4
4
  import { useChatContext } from '../../../../context/ChatContext';
5
5
  import { useComponentContext } from '../../../../context/ComponentContext';
6
+ import { useChannelStateContext } from '../../../../context';
7
+ import { useLastDeliveredData } from '../useLastDeliveredData';
6
8
  export const useMessageListElements = (props) => {
7
- const { channelUnreadUiState, enrichedMessages, internalMessageProps, messageGroupStyles, read, renderMessages, returnAllReadData, threadList, } = props;
8
- const { client, customClasses } = useChatContext('useMessageListElements');
9
+ const { channelUnreadUiState, enrichedMessages, internalMessageProps, messageGroupStyles, messages, renderMessages, returnAllReadData, threadList, } = props;
10
+ const { customClasses } = useChatContext('useMessageListElements');
11
+ const { channel } = useChannelStateContext();
9
12
  const components = useComponentContext('useMessageListElements');
10
13
  // get the readData, but only for messages submitted by the user themselves
11
14
  const readData = useLastReadData({
12
- messages: enrichedMessages,
13
- read,
15
+ channel,
16
+ messages,
17
+ returnAllReadData,
18
+ });
19
+ const ownMessagesDeliveredToOthers = useLastDeliveredData({
20
+ channel,
21
+ messages,
14
22
  returnAllReadData,
15
- userID: client.userID,
16
23
  });
17
24
  const lastReceivedMessageId = useMemo(() => getLastReceived(enrichedMessages), [enrichedMessages]);
18
25
  const elements = useMemo(() => renderMessages({
@@ -22,6 +29,7 @@ export const useMessageListElements = (props) => {
22
29
  lastReceivedMessageId,
23
30
  messageGroupStyles,
24
31
  messages: enrichedMessages,
32
+ ownMessagesDeliveredToOthers,
25
33
  readData,
26
34
  sharedMessageProps: { ...internalMessageProps, threadList },
27
35
  }),
@@ -0,0 +1,8 @@
1
+ import type { Channel, LocalMessage, UserResponse } from 'stream-chat';
2
+ type UseLastDeliveredDataParams = {
3
+ channel: Channel;
4
+ messages: LocalMessage[];
5
+ returnAllReadData: boolean;
6
+ };
7
+ export declare const useLastDeliveredData: (props: UseLastDeliveredDataParams) => Record<string, UserResponse[]>;
8
+ export {};
@@ -0,0 +1,13 @@
1
+ import { useMemo } from 'react';
2
+ export const useLastDeliveredData = (props) => {
3
+ const { channel, messages, returnAllReadData } = props;
4
+ return useMemo(() => returnAllReadData
5
+ ? messages.reduce((acc, msg) => {
6
+ acc[msg.id] = channel.messageReceiptsTracker.deliveredForMessage({
7
+ msgId: msg.id,
8
+ timestampMs: msg.created_at.getTime(),
9
+ });
10
+ return acc;
11
+ }, {})
12
+ : channel.messageReceiptsTracker.groupUsersByLastDeliveredMessage(), [channel, messages, returnAllReadData]);
13
+ };
@@ -1,13 +1,8 @@
1
- import type { UserResponse } from 'stream-chat';
2
- import type { RenderedMessage } from '../utils';
1
+ import type { Channel, LocalMessage, UserResponse } from 'stream-chat';
3
2
  type UseLastReadDataParams = {
4
- messages: RenderedMessage[];
3
+ channel: Channel;
4
+ messages: LocalMessage[];
5
5
  returnAllReadData: boolean;
6
- userID: string | undefined;
7
- read?: Record<string, {
8
- last_read: Date;
9
- user: UserResponse;
10
- }>;
11
6
  };
12
7
  export declare const useLastReadData: (props: UseLastReadDataParams) => Record<string, UserResponse[]>;
13
8
  export {};
@@ -1,10 +1,13 @@
1
1
  import { useMemo } from 'react';
2
- import { isLocalMessage } from '../utils';
3
- import { getReadStates } from '../utils';
4
2
  export const useLastReadData = (props) => {
5
- const { messages, read, returnAllReadData, userID } = props;
6
- return useMemo(() => {
7
- const ownLocalMessages = messages.filter((msg) => isLocalMessage(msg) && msg.user?.id === userID);
8
- return getReadStates(ownLocalMessages, read, returnAllReadData);
9
- }, [messages, read, returnAllReadData, userID]);
3
+ const { channel, messages, returnAllReadData } = props;
4
+ return useMemo(() => returnAllReadData
5
+ ? messages.reduce((acc, msg) => {
6
+ acc[msg.id] = channel.messageReceiptsTracker.readersForMessage({
7
+ msgId: msg.id,
8
+ timestampMs: msg.created_at.getTime(),
9
+ });
10
+ return acc;
11
+ }, {})
12
+ : channel.messageReceiptsTracker.groupUsersByLastReadMessage(), [channel, messages, returnAllReadData]);
10
13
  };