stream-chat-react 12.0.0-rc.1 → 12.0.0-rc.3

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 (104) hide show
  1. package/README.md +10 -0
  2. package/dist/components/Attachment/components/WaveProgressBar.d.ts +3 -1
  3. package/dist/components/Attachment/components/WaveProgressBar.js +44 -9
  4. package/dist/components/Channel/channelState.js +1 -0
  5. package/dist/components/DateSeparator/DateSeparator.js +1 -1
  6. package/dist/components/EventComponent/EventComponent.js +1 -1
  7. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +9 -3
  8. package/dist/components/MediaRecorder/classes/MediaRecorderController.d.ts +6 -7
  9. package/dist/components/MediaRecorder/classes/MediaRecorderController.js +0 -5
  10. package/dist/components/MediaRecorder/hooks/index.d.ts +1 -1
  11. package/dist/components/MediaRecorder/hooks/useMediaRecorder.d.ts +1 -2
  12. package/dist/components/MediaRecorder/hooks/useMediaRecorder.js +1 -1
  13. package/dist/components/MediaRecorder/index.d.ts +1 -0
  14. package/dist/components/MediaRecorder/transcode/index.d.ts +6 -5
  15. package/dist/components/MediaRecorder/transcode/index.js +5 -15
  16. package/dist/components/Message/MessageSimple.js +1 -1
  17. package/dist/components/Message/MessageTimestamp.d.ts +0 -1
  18. package/dist/components/Message/MessageTimestamp.js +0 -1
  19. package/dist/components/Message/Timestamp.d.ts +0 -1
  20. package/dist/components/Message/Timestamp.js +2 -3
  21. package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.js +1 -1
  22. package/dist/components/Message/utils.js +2 -0
  23. package/dist/components/MessageInput/AttachmentPreviewList/AttachmentPreviewList.js +23 -27
  24. package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.d.ts +1 -0
  25. package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.js +1 -1
  26. package/dist/components/MessageInput/AttachmentPreviewList/ImageAttachmentPreview.js +2 -1
  27. package/dist/components/MessageInput/MessageInput.d.ts +4 -6
  28. package/dist/components/MessageInput/MessageInputFlat.js +4 -7
  29. package/dist/components/MessageInput/hooks/useAttachments.d.ts +1 -5
  30. package/dist/components/MessageInput/hooks/useAttachments.js +65 -52
  31. package/dist/components/MessageInput/hooks/useCreateMessageInputContext.js +2 -19
  32. package/dist/components/MessageInput/hooks/useMessageInputState.d.ts +2 -35
  33. package/dist/components/MessageInput/hooks/useMessageInputState.js +2 -107
  34. package/dist/components/MessageInput/hooks/usePasteHandler.js +1 -3
  35. package/dist/components/MessageInput/hooks/useSubmitHandler.js +19 -71
  36. package/dist/components/MessageInput/hooks/utils.d.ts +1 -2
  37. package/dist/components/MessageInput/icons.d.ts +0 -1
  38. package/dist/components/MessageInput/icons.js +0 -3
  39. package/dist/components/MessageInput/types.d.ts +3 -30
  40. package/dist/components/MessageList/MessageList.d.ts +3 -1
  41. package/dist/components/MessageList/MessageList.js +2 -1
  42. package/dist/components/MessageList/VirtualizedMessageList.d.ts +3 -1
  43. package/dist/components/MessageList/VirtualizedMessageList.js +3 -3
  44. package/dist/components/MessageList/VirtualizedMessageListComponents.js +3 -2
  45. package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.d.ts +2 -1
  46. package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.js +3 -3
  47. package/dist/components/MessageList/utils.d.ts +1 -1
  48. package/dist/components/MessageList/utils.js +16 -6
  49. package/dist/components/ReactFileUtilities/types.d.ts +0 -29
  50. package/dist/components/ReactFileUtilities/utils.d.ts +2 -0
  51. package/dist/components/ReactFileUtilities/utils.js +2 -0
  52. package/dist/context/ChannelActionContext.d.ts +2 -2
  53. package/dist/context/MessageInputContext.d.ts +1 -5
  54. package/dist/css/v2/emoji-replacement.css +1 -1
  55. package/dist/css/v2/index.css +2 -2
  56. package/dist/css/v2/index.layout.css +2 -2
  57. package/dist/i18n/Streami18n.d.ts +2 -0
  58. package/dist/i18n/de.json +3 -1
  59. package/dist/i18n/en.json +3 -1
  60. package/dist/i18n/es.json +3 -1
  61. package/dist/i18n/fr.json +3 -1
  62. package/dist/i18n/hi.json +3 -1
  63. package/dist/i18n/it.json +3 -1
  64. package/dist/i18n/ja.json +3 -1
  65. package/dist/i18n/ko.json +3 -1
  66. package/dist/i18n/nl.json +3 -1
  67. package/dist/i18n/pt.json +3 -1
  68. package/dist/i18n/ru.json +3 -1
  69. package/dist/i18n/tr.json +3 -1
  70. package/dist/i18n/utils.d.ts +3 -3
  71. package/dist/index.cjs.js +1987 -12143
  72. package/dist/index.cjs.js.map +4 -4
  73. package/dist/{components → plugins}/Emojis/EmojiPicker.js +1 -1
  74. package/dist/plugins/Emojis/icons.d.ts +2 -0
  75. package/dist/plugins/Emojis/icons.js +4 -0
  76. package/dist/{components → plugins}/Emojis/index.cjs.js +23 -22
  77. package/dist/plugins/Emojis/index.cjs.js.map +7 -0
  78. package/dist/plugins/Emojis/index.d.ts +2 -0
  79. package/dist/plugins/Emojis/index.js +2 -0
  80. package/dist/plugins/encoders/mp3.cjs.js +111 -0
  81. package/dist/plugins/encoders/mp3.cjs.js.map +7 -0
  82. package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.js +3 -3
  83. package/dist/scss/v2/Autocomplete/Autocomplete-layout.scss +1 -1
  84. package/dist/scss/v2/Autocomplete/Autocomplete-theme.scss +4 -2
  85. package/dist/scss/v2/Avatar/Avatar-layout.scss +31 -23
  86. package/dist/scss/v2/ChannelList/ChannelList-layout.scss +0 -5
  87. package/dist/scss/v2/ChannelSearch/ChannelSearch-layout.scss +1 -0
  88. package/dist/scss/v2/EditMessageForm/EditMessageForm-theme.scss +9 -9
  89. package/dist/scss/v2/Message/Message-layout.scss +37 -6
  90. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +11 -0
  91. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +5 -0
  92. package/dist/scss/v2/_emoji-replacement.scss +4 -2
  93. package/package.json +17 -7
  94. package/dist/components/Emojis/index.cjs.js.map +0 -7
  95. package/dist/components/Emojis/index.d.ts +0 -1
  96. package/dist/components/Emojis/index.js +0 -1
  97. package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.d.ts +0 -11
  98. package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.js +0 -51
  99. package/dist/components/MessageInput/hooks/useFileUploads.d.ts +0 -7
  100. package/dist/components/MessageInput/hooks/useFileUploads.js +0 -85
  101. package/dist/components/MessageInput/hooks/useImageUploads.d.ts +0 -8
  102. package/dist/components/MessageInput/hooks/useImageUploads.js +0 -94
  103. /package/dist/{components → plugins}/Emojis/EmojiPicker.d.ts +0 -0
  104. /package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.d.ts +0 -0
@@ -1,4 +1,3 @@
1
- import type { ImageUpload } from '../../ReactFileUtilities';
2
1
  import type { UserResponse } from 'stream-chat';
3
2
  import type { ChannelActionContextValue } from '../../../context/ChannelActionContext';
4
3
  import type { ChatContextValue } from '../../../context/ChatContext';
@@ -19,7 +18,7 @@ export type SearchLocalUserParams<StreamChatGenerics extends DefaultStreamChatGe
19
18
  export declare const searchLocalUsers: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(params: SearchLocalUserParams<StreamChatGenerics>) => UserResponse<StreamChatGenerics>[];
20
19
  type CheckUploadPermissionsParams<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
21
20
  addNotification: ChannelActionContextValue<StreamChatGenerics>['addNotification'];
22
- file: ImageUpload['file'];
21
+ file: File;
23
22
  getAppSettings: ChatContextValue<StreamChatGenerics>['getAppSettings'];
24
23
  t: TranslationContextValue['t'];
25
24
  uploadType: 'image' | 'file';
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- export declare const EmojiPickerIcon: () => React.JSX.Element;
3
2
  export declare const LoadingIndicatorIcon: ({ size }: {
4
3
  size?: number;
5
4
  }) => React.JSX.Element;
@@ -1,9 +1,6 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { nanoid } from 'nanoid';
3
3
  import { useTranslationContext } from '../../context/TranslationContext';
4
- export const EmojiPickerIcon = () => (React.createElement("svg", { preserveAspectRatio: 'xMinYMin', viewBox: '0 0 28 28', width: '100%', xmlns: 'http://www.w3.org/2000/svg' },
5
- React.createElement("g", { clipRule: 'evenodd', fillRule: 'evenodd' },
6
- React.createElement("path", { d: 'M14 4.4C8.6 4.4 4.4 8.6 4.4 14c0 5.4 4.2 9.6 9.6 9.6c5.4 0 9.6-4.2 9.6-9.6c0-5.4-4.2-9.6-9.6-9.6zM2 14c0-6.6 5.4-12 12-12s12 5.4 12 12s-5.4 12-12 12s-12-5.4-12-12zM12.8 11c0 1-.8 1.8-1.8 1.8s-1.8-.8-1.8-1.8s.8-1.8 1.8-1.8s1.8.8 1.8 1.8zM18.8 11c0 1-.8 1.8-1.8 1.8s-1.8-.8-1.8-1.8s.8-1.8 1.8-1.8s1.8.8 1.8 1.8zM8.6 15.4c.6-.4 1.2-.2 1.6.2c.6.8 1.6 1.8 3 2c1.2.4 2.8.2 4.8-2c.4-.4 1.2-.6 1.6 0c.4.4.6 1.2 0 1.6c-2.2 2.6-4.8 3.4-7 3c-2-.4-3.6-1.8-4.4-3c-.4-.6-.2-1.2.4-1.8z' }))));
7
4
  export const LoadingIndicatorIcon = ({ size = 20 }) => {
8
5
  const id = useMemo(() => nanoid(), []);
9
6
  return (React.createElement("div", { className: 'str-chat__loading-indicator' },
@@ -1,36 +1,6 @@
1
1
  import type { Attachment, DefaultGenerics, ExtendableGenerics, OGAttachment } from 'stream-chat';
2
2
  import type { DefaultStreamChatGenerics } from '../../types/types';
3
3
  export type AttachmentLoadingState = 'uploading' | 'finished' | 'failed';
4
- export type FileUpload = {
5
- file: {
6
- name: string;
7
- lastModified?: number;
8
- lastModifiedDate?: Date;
9
- size?: number;
10
- type?: string;
11
- uri?: string;
12
- };
13
- id: string;
14
- state: AttachmentLoadingState;
15
- thumb_url?: string;
16
- url?: string;
17
- };
18
- export type ImageUpload<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
19
- file: {
20
- name: string;
21
- height?: number;
22
- lastModified?: number;
23
- lastModifiedDate?: Date;
24
- size?: number;
25
- type?: string;
26
- uri?: string;
27
- width?: number;
28
- };
29
- id: string;
30
- state: AttachmentLoadingState;
31
- previewUri?: string;
32
- url?: string;
33
- } & Pick<Attachment<StreamChatGenerics>, 'og_scrape_url' | 'title' | 'title_link' | 'author_name' | 'text'>;
34
4
  export declare enum LinkPreviewState {
35
5
  /** Link preview has been dismissed using MessageInputContextValue.dismissLinkPreview **/
36
6
  DISMISSED = "dismissed",
@@ -111,4 +81,7 @@ export type LocalImageAttachment<StreamChatGenerics extends DefaultStreamChatGen
111
81
  export type LocalFileAttachment<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, CustomLocalMetadata = Record<string, unknown>> = LocalAttachmentCast<FileAttachment<StreamChatGenerics>, LocalAttachmentUploadMetadata & CustomLocalMetadata>;
112
82
  export type AnyLocalAttachment<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, CustomLocalMetadata = Record<string, unknown>> = LocalAttachmentCast<Attachment<StreamChatGenerics>, LocalAttachmentMetadata<CustomLocalMetadata>>;
113
83
  export type LocalAttachment<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = AnyLocalAttachment<StreamChatGenerics> | LocalFileAttachment<StreamChatGenerics> | LocalImageAttachment<StreamChatGenerics> | LocalAudioAttachment<StreamChatGenerics> | LocalVideoAttachment<StreamChatGenerics> | LocalVoiceRecordingAttachment<StreamChatGenerics>;
84
+ export type LocalAttachmentToUpload<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, CustomLocalMetadata = Record<string, unknown>> = Partial<Attachment<StreamChatGenerics>> & {
85
+ localMetadata: Partial<BaseLocalAttachmentMetadata> & LocalAttachmentUploadMetadata & CustomLocalMetadata;
86
+ };
114
87
  export {};
@@ -11,7 +11,7 @@ export type MessageListProps<StreamChatGenerics extends DefaultStreamChatGeneric
11
11
  /** Disables the injection of date separator components in MessageList, defaults to `false` */
12
12
  disableDateSeparator?: boolean;
13
13
  /** Callback function to set group styles for each message */
14
- groupStyles?: (message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean) => GroupStyle;
14
+ groupStyles?: (message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean, maxTimeBetweenGroupedMessages?: number) => GroupStyle;
15
15
  /** Whether the list has more items to load */
16
16
  hasMore?: boolean;
17
17
  /** Element to be rendered at the top of the thread message list. By default, these are the Message and ThreadStart components */
@@ -34,6 +34,8 @@ export type MessageListProps<StreamChatGenerics extends DefaultStreamChatGeneric
34
34
  loadMore?: ChannelActionContextValue['loadMore'] | (() => Promise<void>);
35
35
  /** Function called when newer messages are to be loaded, defaults to function stored in [ChannelActionContext](https://getstream.io/chat/docs/sdk/react/contexts/channel_action_context/) */
36
36
  loadMoreNewer?: ChannelActionContextValue['loadMoreNewer'] | (() => Promise<void>);
37
+ /** Maximum time in milliseconds that should occur between messages to still consider them grouped together */
38
+ maxTimeBetweenGroupedMessages?: number;
37
39
  /** The limit to use when paginating messages */
38
40
  messageLimit?: number;
39
41
  /** The messages to render in the list, defaults to messages stored in [ChannelStateContext](https://getstream.io/chat/docs/sdk/react/contexts/channel_state_context/) */
@@ -19,7 +19,7 @@ import { MessageListMainPanel } from './MessageListMainPanel';
19
19
  import { defaultRenderMessages } from './renderMessages';
20
20
  import { DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, DEFAULT_NEXT_CHANNEL_PAGE_SIZE, } from '../../constants/limits';
21
21
  const MessageListWithContext = (props) => {
22
- const { channel, channelUnreadUiState, disableDateSeparator = false, groupStyles, hideDeletedMessages = false, hideNewMessageSeparator = false, internalInfiniteScrollProps: { threshold: loadMoreScrollThreshold = DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, ...restInternalInfiniteScrollProps } = {}, messageActions = Object.keys(MESSAGE_ACTIONS), messages = [], notifications, noGroupByUser = false, pinPermissions = defaultPinPermissions, // @deprecated in favor of `channelCapabilities` - TODO: remove in next major release
22
+ const { channel, channelUnreadUiState, disableDateSeparator = false, groupStyles, hideDeletedMessages = false, hideNewMessageSeparator = false, internalInfiniteScrollProps: { threshold: loadMoreScrollThreshold = DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, ...restInternalInfiniteScrollProps } = {}, maxTimeBetweenGroupedMessages, messageActions = Object.keys(MESSAGE_ACTIONS), messages = [], notifications, noGroupByUser = false, pinPermissions = defaultPinPermissions, // @deprecated in favor of `channelCapabilities` - TODO: remove in next major release
23
23
  returnAllReadData = false, threadList = false, unsafeHTML = false, headerPosition, read, renderMessages = defaultRenderMessages, reviewProcessedMessage, messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, loadMore: loadMoreCallback, loadMoreNewer: loadMoreNewerCallback, hasMoreNewer = false, reactionDetailsSort, showUnreadNotificationAlways, sortReactionDetails, sortReactions, suppressAutoscroll, highlightedMessageId, jumpToLatestMessage = () => Promise.resolve(), } = props;
24
24
  const [listElement, setListElement] = React.useState(null);
25
25
  const [ulElement, setUlElement] = React.useState(null);
@@ -51,6 +51,7 @@ const MessageListWithContext = (props) => {
51
51
  headerPosition,
52
52
  hideDeletedMessages,
53
53
  hideNewMessageSeparator,
54
+ maxTimeBetweenGroupedMessages,
54
55
  messages,
55
56
  noGroupByUser,
56
57
  reviewProcessedMessage,
@@ -51,7 +51,7 @@ export type VirtualizedMessageListProps<StreamChatGenerics extends DefaultStream
51
51
  /** Disables the injection of date separator components in MessageList, defaults to `true` */
52
52
  disableDateSeparator?: boolean;
53
53
  /** Callback function to set group styles for each message */
54
- groupStyles?: (message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean) => GroupStyle;
54
+ groupStyles?: (message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean, maxTimeBetweenGroupedMessages?: number) => GroupStyle;
55
55
  /** Whether or not the list has more items to load */
56
56
  hasMore?: boolean;
57
57
  /** Whether or not the list has newer items to load */
@@ -75,6 +75,8 @@ export type VirtualizedMessageListProps<StreamChatGenerics extends DefaultStream
75
75
  loadMore?: ChannelActionContextValue['loadMore'] | (() => Promise<void>);
76
76
  /** Function called when new messages are to be loaded, defaults to function stored in [ChannelActionContext](https://getstream.io/chat/docs/sdk/react/contexts/channel_action_context/) */
77
77
  loadMoreNewer?: ChannelActionContextValue['loadMore'] | (() => Promise<void>);
78
+ /** Maximum time in milliseconds that should occur between messages to still consider them grouped together */
79
+ maxTimeBetweenGroupedMessages?: number;
78
80
  /** Custom UI component to display a message, defaults to and accepts same props as [MessageSimple](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageSimple.tsx) */
79
81
  Message?: React.ComponentType<MessageUIComponentProps<StreamChatGenerics>>;
80
82
  /** The limit to use when paginating messages */
@@ -46,7 +46,7 @@ function calculateInitialTopMostItemIndex(messages, highlightedMessageId) {
46
46
  return messages.length - 1;
47
47
  }
48
48
  const VirtualizedMessageListWithContext = (props) => {
49
- const { additionalMessageInputProps, additionalVirtuosoProps = {}, channel, channelUnreadUiState, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, defaultItemHeight, disableDateSeparator = true, formatDate, groupStyles, hasMoreNewer, head, hideDeletedMessages = false, hideNewMessageSeparator = false, highlightedMessageId, jumpToLatestMessage, loadingMore, loadMore, loadMoreNewer, Message: MessageUIComponentFromProps, messageActions, messageLimit = DEFAULT_NEXT_CHANNEL_PAGE_SIZE, messages, notifications,
49
+ 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,
50
50
  // TODO: refactor to scrollSeekPlaceHolderConfiguration and components.ScrollSeekPlaceholder, like the Virtuoso Component
51
51
  overscan = 0, read, returnAllReadData = false, reviewProcessedMessage, scrollSeekPlaceHolder, scrollToLatestMessageOnFocus = false, separateGiphyPreview = false, shouldGroupByUser = false, showUnreadNotificationAlways, reactionDetailsSort, sortReactionDetails, sortReactions, stickToBottomScrollBehavior = 'smooth', suppressAutoscroll, threadList, } = props;
52
52
  const { components: virtuosoComponentsFromProps, ...overridingVirtuosoProps } = additionalVirtuosoProps;
@@ -106,14 +106,14 @@ const VirtualizedMessageListWithContext = (props) => {
106
106
  ]);
107
107
  const groupStylesFn = groupStyles || getGroupStyles;
108
108
  const messageGroupStyles = useMemo(() => processedMessages.reduce((acc, message, i) => {
109
- const style = groupStylesFn(message, processedMessages[i - 1], processedMessages[i + 1], !shouldGroupByUser);
109
+ const style = groupStylesFn(message, processedMessages[i - 1], processedMessages[i + 1], !shouldGroupByUser, maxTimeBetweenGroupedMessages);
110
110
  if (style)
111
111
  acc[message.id] = style;
112
112
  return acc;
113
113
  }, {}),
114
114
  // processedMessages were incorrectly rebuilt with a new object identity at some point, hence the .length usage
115
115
  // eslint-disable-next-line react-hooks/exhaustive-deps
116
- [processedMessages.length, shouldGroupByUser, groupStylesFn]);
116
+ [maxTimeBetweenGroupedMessages, processedMessages.length, shouldGroupByUser, groupStylesFn]);
117
117
  const { atBottom, isMessageListScrolledToBottom, newMessagesNotification, setIsMessageListScrolledToBottom, setNewMessagesNotification, } = useNewMessageNotification(processedMessages, client.userID, hasMoreNewer);
118
118
  useMarkRead({
119
119
  isMessageListScrolledToBottom,
@@ -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, MessageSystem, numItemsPrepended, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, shouldGroupByUser, sortReactionDetails, sortReactions, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = 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, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = virtuosoContext;
51
51
  const streamMessageIndex = calculateItemIndex(virtuosoIndex, numItemsPrepended);
52
52
  if (customMessageRenderer) {
53
53
  return customMessageRenderer(messageList, streamMessageIndex);
@@ -66,6 +66,7 @@ export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
66
66
  message.user?.id === messageList[streamMessageIndex - 1].user?.id;
67
67
  const maybePrevMessage = messageList[streamMessageIndex - 1];
68
68
  const maybeNextMessage = messageList[streamMessageIndex + 1];
69
+ // FIXME: firstOfGroup & endOfGroup should be derived from groupStyles which apply a more complex logic
69
70
  const firstOfGroup = shouldGroupByUser &&
70
71
  (message.user?.id !== maybePrevMessage?.user?.id ||
71
72
  (maybePrevMessage && isMessageEdited(maybePrevMessage)));
@@ -88,7 +89,7 @@ export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
88
89
  return (React.createElement(React.Fragment, null,
89
90
  showUnreadSeparatorAbove && (React.createElement("div", { className: 'str-chat__unread-messages-separator-wrapper' },
90
91
  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, lastReceivedId: lastReceivedMessageId, message: message, Message: MessageUIComponent, messageActions: messageActions, reactionDetailsSort: reactionDetailsSort, readBy: ownMessagesReadByOthers[message.id] || [], sortReactionDetails: sortReactionDetails, sortReactions: sortReactions }),
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 }),
92
93
  showUnreadSeparatorBelow && (React.createElement("div", { className: 'str-chat__unread-messages-separator-wrapper' },
93
94
  React.createElement(UnreadMessagesSeparator, { unreadCount: unreadMessageCount })))));
94
95
  };
@@ -9,8 +9,9 @@ export declare const useEnrichedMessages: <StreamChatGenerics extends DefaultStr
9
9
  hideNewMessageSeparator: boolean;
10
10
  messages: StreamMessage<StreamChatGenerics>[];
11
11
  noGroupByUser: boolean;
12
- groupStyles?: (message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean) => GroupStyle;
12
+ groupStyles?: (message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean, maxTimeBetweenGroupedMessages?: number) => GroupStyle;
13
13
  headerPosition?: number;
14
+ maxTimeBetweenGroupedMessages?: number;
14
15
  reviewProcessedMessage?: ProcessMessagesParams<StreamChatGenerics>['reviewProcessedMessage'];
15
16
  }) => {
16
17
  messageGroupStyles: Record<string, GroupStyle>;
@@ -3,7 +3,7 @@ import { getGroupStyles, insertIntro, processMessages, } from '../../utils';
3
3
  import { useChatContext } from '../../../../context/ChatContext';
4
4
  import { useComponentContext } from '../../../../context/ComponentContext';
5
5
  export const useEnrichedMessages = (args) => {
6
- const { channel, disableDateSeparator, groupStyles, headerPosition, hideDeletedMessages, hideNewMessageSeparator, messages, noGroupByUser, reviewProcessedMessage, } = args;
6
+ const { channel, disableDateSeparator, groupStyles, headerPosition, hideDeletedMessages, hideNewMessageSeparator, maxTimeBetweenGroupedMessages, messages, noGroupByUser, reviewProcessedMessage, } = args;
7
7
  const { client } = useChatContext('useEnrichedMessages');
8
8
  const { HeaderComponent } = useComponentContext('useEnrichedMessages');
9
9
  const lastRead = useMemo(() => channel.lastRead?.(), [channel]);
@@ -24,12 +24,12 @@ export const useEnrichedMessages = (args) => {
24
24
  }
25
25
  const groupStylesFn = groupStyles || getGroupStyles;
26
26
  const messageGroupStyles = useMemo(() => messagesWithDates.reduce((acc, message, i) => {
27
- const style = groupStylesFn(message, messagesWithDates[i - 1], messagesWithDates[i + 1], noGroupByUser);
27
+ const style = groupStylesFn(message, messagesWithDates[i - 1], messagesWithDates[i + 1], noGroupByUser, maxTimeBetweenGroupedMessages);
28
28
  if (style)
29
29
  acc[message.id] = style;
30
30
  return acc;
31
31
  }, {}),
32
32
  // eslint-disable-next-line react-hooks/exhaustive-deps
33
- [messagesWithDates, noGroupByUser]);
33
+ [maxTimeBetweenGroupedMessages, messagesWithDates, noGroupByUser]);
34
34
  return { messageGroupStyles, messages: messagesWithDates };
35
35
  };
@@ -57,7 +57,7 @@ export declare const getReadStates: <StreamChatGenerics extends DefaultStreamCha
57
57
  }> | undefined, returnAllReadData: boolean) => Record<string, UserResponse<StreamChatGenerics>[]>;
58
58
  export declare const insertIntro: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(messages: StreamMessage<StreamChatGenerics>[], headerPosition?: number) => StreamMessage<StreamChatGenerics>[];
59
59
  export type GroupStyle = '' | 'middle' | 'top' | 'bottom' | 'single';
60
- export declare const getGroupStyles: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean) => GroupStyle;
60
+ export declare const getGroupStyles: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: StreamMessage<StreamChatGenerics>, previousMessage: StreamMessage<StreamChatGenerics>, nextMessage: StreamMessage<StreamChatGenerics>, noGroupByUser: boolean, maxTimeBetweenGroupedMessages?: number) => GroupStyle;
61
61
  export declare const hasMoreMessagesProbably: (returnedCountMessages: number, limit: number) => boolean;
62
62
  export declare const hasNotMoreMessages: (returnedCountMessages: number, limit: number) => boolean;
63
63
  type DateSeparatorMessage = {
@@ -179,7 +179,7 @@ export const insertIntro = (messages, headerPosition) => {
179
179
  }
180
180
  return newMessages;
181
181
  };
182
- export const getGroupStyles = (message, previousMessage, nextMessage, noGroupByUser) => {
182
+ export const getGroupStyles = (message, previousMessage, nextMessage, noGroupByUser, maxTimeBetweenGroupedMessages) => {
183
183
  if (message.customType === CUSTOM_MESSAGE_TYPE.date)
184
184
  return '';
185
185
  if (message.customType === CUSTOM_MESSAGE_TYPE.intro)
@@ -190,22 +190,32 @@ export const getGroupStyles = (message, previousMessage, nextMessage, noGroupByU
190
190
  previousMessage.customType === CUSTOM_MESSAGE_TYPE.intro ||
191
191
  previousMessage.customType === CUSTOM_MESSAGE_TYPE.date ||
192
192
  previousMessage.type === 'system' ||
193
+ previousMessage.type === 'error' ||
193
194
  previousMessage.attachments?.length !== 0 ||
194
195
  message.user?.id !== previousMessage.user?.id ||
195
- previousMessage.type === 'error' ||
196
196
  previousMessage.deleted_at ||
197
197
  (message.reaction_groups && Object.keys(message.reaction_groups).length > 0) ||
198
- isMessageEdited(previousMessage);
198
+ isMessageEdited(previousMessage) ||
199
+ (maxTimeBetweenGroupedMessages !== undefined &&
200
+ previousMessage.created_at &&
201
+ message.created_at &&
202
+ new Date(message.created_at).getTime() - new Date(previousMessage.created_at).getTime() >
203
+ maxTimeBetweenGroupedMessages);
199
204
  const isBottomMessage = !nextMessage ||
205
+ nextMessage.customType === CUSTOM_MESSAGE_TYPE.intro ||
200
206
  nextMessage.customType === CUSTOM_MESSAGE_TYPE.date ||
201
207
  nextMessage.type === 'system' ||
202
- nextMessage.customType === CUSTOM_MESSAGE_TYPE.intro ||
208
+ nextMessage.type === 'error' ||
203
209
  nextMessage.attachments?.length !== 0 ||
204
210
  message.user?.id !== nextMessage.user?.id ||
205
- nextMessage.type === 'error' ||
206
211
  nextMessage.deleted_at ||
207
212
  (nextMessage.reaction_groups && Object.keys(nextMessage.reaction_groups).length > 0) ||
208
- isMessageEdited(message);
213
+ isMessageEdited(message) ||
214
+ (maxTimeBetweenGroupedMessages !== undefined &&
215
+ nextMessage.created_at &&
216
+ message.created_at &&
217
+ new Date(nextMessage.created_at).getTime() - new Date(message.created_at).getTime() >
218
+ maxTimeBetweenGroupedMessages);
209
219
  if (!isTopMessage && !isBottomMessage) {
210
220
  if (message.deleted_at || message.type === 'error')
211
221
  return 'single';
@@ -1,31 +1,2 @@
1
1
  export type RecordedMediaType = 'audio' | 'video';
2
- export type UploadState = 'uploading' | 'finished' | 'failed';
3
2
  export type FileLike = Blob | File;
4
- export type UploadInfo = {
5
- id: string;
6
- state: UploadState;
7
- url?: string;
8
- };
9
- export type FileUpload = {
10
- file: {
11
- name: string;
12
- lastModified?: number;
13
- lastModifiedDate?: Date;
14
- size?: number;
15
- type?: string;
16
- uri?: string;
17
- };
18
- } & UploadInfo;
19
- export type ImageUpload = {
20
- file: {
21
- name: string;
22
- height?: number;
23
- lastModified?: number;
24
- lastModifiedDate?: Date;
25
- size?: number;
26
- type?: string;
27
- uri?: string;
28
- width?: number;
29
- };
30
- previewUri?: string;
31
- } & UploadInfo;
@@ -3,6 +3,7 @@ import { ChangeEvent } from 'react';
3
3
  export declare const useHandleFileChangeWrapper: (resetOnChange?: boolean, handler?: (files: Array<File>) => void) => ({ currentTarget }: ChangeEvent<HTMLInputElement>) => void;
4
4
  export declare function dataTransferItemsHaveFiles(items?: DataTransferItem[]): boolean;
5
5
  export declare function dataTransferItemsToFiles(items?: DataTransferItem[]): Promise<FileLike[]>;
6
+ export declare const isBlobButNotFile: (obj: unknown) => obj is Blob;
6
7
  export declare const createFileFromBlobs: ({ blobsArray, fileName, mimeType, }: {
7
8
  blobsArray: Blob[];
8
9
  fileName: string;
@@ -11,3 +12,4 @@ export declare const createFileFromBlobs: ({ blobsArray, fileName, mimeType, }:
11
12
  export declare const getExtensionFromMimeType: (mimeType: string) => string | null;
12
13
  export declare const getRecordedMediaTypeFromMimeType: (mimeType: string) => RecordedMediaType | null;
13
14
  export declare const readFileAsArrayBuffer: (file: File) => Promise<ArrayBuffer>;
15
+ export declare const generateFileName: (mimeType: string) => string;
@@ -77,6 +77,7 @@ const extractImageSources = (s) => {
77
77
  const imageTags = new DOMParser().parseFromString(s, 'text/html').getElementsByTagName('img');
78
78
  return Array.from(imageTags, (tag) => tag.src).filter((tag) => tag);
79
79
  };
80
+ export const isBlobButNotFile = (obj) => obj instanceof Blob && !(obj instanceof File);
80
81
  export const createFileFromBlobs = ({ blobsArray, fileName, mimeType, }) => {
81
82
  const concatenatedBlob = new Blob(blobsArray, { type: mimeType });
82
83
  return new File([concatenatedBlob], fileName, { type: concatenatedBlob.type });
@@ -99,3 +100,4 @@ export const readFileAsArrayBuffer = (file) => new Promise((resolve, reject) =>
99
100
  };
100
101
  fileReader.readAsArrayBuffer(file);
101
102
  });
103
+ export const generateFileName = (mimeType) => `file_${new Date().toISOString()}.${getExtensionFromMimeType(mimeType)}`;
@@ -31,9 +31,9 @@ export type ChannelActionContextValue<StreamChatGenerics extends DefaultStreamCh
31
31
  deleteMessage: (message: StreamMessage<StreamChatGenerics>) => Promise<MessageResponse<StreamChatGenerics>>;
32
32
  dispatch: React.Dispatch<ChannelStateReducerAction<StreamChatGenerics>>;
33
33
  editMessage: (message: UpdatedMessage<StreamChatGenerics>, options?: UpdateMessageOptions) => Promise<UpdateMessageAPIResponse<StreamChatGenerics> | void>;
34
- jumpToFirstUnreadMessage: (queryMessageLimit?: number) => Promise<void>;
34
+ jumpToFirstUnreadMessage: (queryMessageLimit?: number, highlightDuration?: number) => Promise<void>;
35
35
  jumpToLatestMessage: () => Promise<void>;
36
- jumpToMessage: (messageId: string, limit?: number) => Promise<void>;
36
+ jumpToMessage: (messageId: string, limit?: number, highlightDuration?: number) => Promise<void>;
37
37
  loadMore: (limit?: number) => Promise<number>;
38
38
  loadMoreNewer: (limit?: number) => Promise<number>;
39
39
  loadMoreThread: () => Promise<void>;
@@ -17,14 +17,10 @@ export declare const MessageInputContext: React.Context<(MessageInputState & imp
17
17
  onSelectUser: (item: import("stream-chat").UserResponse<DefaultStreamChatGenerics>) => void;
18
18
  recordingController: import("..").RecordingController<DefaultStreamChatGenerics>;
19
19
  removeAttachments: (ids: string[]) => void;
20
- removeFile: (id: string) => void;
21
- removeImage: (id: string) => void;
22
20
  textareaRef: React.MutableRefObject<HTMLTextAreaElement | null | undefined>;
23
21
  uploadAttachment: (attachment: import("../components/MessageInput").LocalAttachment<DefaultStreamChatGenerics>) => Promise<import("../components/MessageInput").LocalAttachment<DefaultStreamChatGenerics> | undefined>;
24
- uploadFile: (id: string) => void;
25
- uploadImage: (id: string) => void;
26
22
  uploadNewFiles: (files: FileList | File[]) => void;
27
- upsertAttachments: (attachments: (import("stream-chat").Attachment<DefaultStreamChatGenerics> | import("../components/MessageInput").LocalAttachment<DefaultStreamChatGenerics>)[]) => void;
23
+ upsertAttachments: (attachments: (import("../components/MessageInput").LocalAttachment<DefaultStreamChatGenerics> | import("stream-chat").Attachment<DefaultStreamChatGenerics>)[]) => void;
28
24
  }) | undefined>;
29
25
  export declare const MessageInputContextProvider: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger>({ children, value, }: PropsWithChildren<{
30
26
  value: MessageInputContextValue<StreamChatGenerics, V>;
@@ -1 +1 @@
1
- @font-face{font-family:ReplaceFlagEmojiPNG;src:url("../../assets/NotoColorEmoji-flags.woff2") format("woff2");unicode-range:U+1F1E6-1F1FF}@font-face{font-family:ReplaceFlagEmojiSVG;src:url("../../assets/EmojiOneColor.woff2") format("woff2");unicode-range:U+1F1E6-1F1FF}.str-chat--windows-flags .str-chat__textarea__textarea,.str-chat--windows-flags .str-chat__message-text-inner *,.str-chat--windows-flags .str-chat__emoji-item--entity,.str-chat--windows-flags .emoji-mart-emoji-native *{font-family:ReplaceFlagEmojiPNG,var(--str-chat__font-family),sans-serif;font-display:swap}@-moz-document url-prefix(""){.str-chat--windows-flags .str-chat__textarea__textarea,.str-chat--windows-flags .str-chat__message-text-inner *,.str-chat--windows-flags .str-chat__emoji-item--entity,.str-chat--windows-flags .emoji-mart-emoji-native *{font-family:ReplaceFlagEmojiSVG,var(--str-chat__font-family),sans-serif;font-display:swap}}
1
+ @font-face{font-family:ReplaceFlagEmojiPNG;src:url("../../assets/NotoColorEmoji-flags.woff2") format("woff2");unicode-range:U+1F1E6-1F1FF}@font-face{font-family:ReplaceFlagEmojiSVG;src:url("../../assets/EmojiOneColor.woff2") format("woff2");unicode-range:U+1F1E6-1F1FF}.str-chat--windows-flags .str-chat__textarea__textarea,.str-chat--windows-flags .str-chat__message-textarea,.str-chat--windows-flags .str-chat__message-text-inner *,.str-chat--windows-flags .str-chat__emoji-item--entity,.str-chat--windows-flags .emoji-mart-emoji-native *{font-family:ReplaceFlagEmojiPNG,var(--str-chat__font-family),sans-serif;font-display:swap}@-moz-document url-prefix(""){.str-chat--windows-flags .str-chat__textarea__textarea,.str-chat--windows-flags .str-chat__message-textarea,.str-chat--windows-flags .str-chat__message-text-inner *,.str-chat--windows-flags .str-chat__emoji-item--entity,.str-chat--windows-flags .emoji-mart-emoji-native *{font-family:ReplaceFlagEmojiSVG,var(--str-chat__font-family),sans-serif;font-display:swap}}