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

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 (148) hide show
  1. package/README.md +11 -1
  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/Channel.js +19 -32
  5. package/dist/components/Channel/channelState.js +1 -0
  6. package/dist/components/Channel/hooks/useCreateChannelStateContext.js +1 -1
  7. package/dist/components/ChannelList/ChannelList.js +1 -1
  8. package/dist/components/Chat/Chat.d.ts +1 -1
  9. package/dist/components/Chat/hooks/useChat.d.ts +2 -2
  10. package/dist/components/Chat/hooks/useChat.js +1 -2
  11. package/dist/components/Chat/hooks/useCreateChatClient.d.ts +4 -2
  12. package/dist/components/Chat/hooks/useCreateChatClient.js +5 -4
  13. package/dist/components/DateSeparator/DateSeparator.d.ts +1 -1
  14. package/dist/components/DateSeparator/DateSeparator.js +1 -1
  15. package/dist/components/EventComponent/EventComponent.d.ts +1 -1
  16. package/dist/components/EventComponent/EventComponent.js +1 -1
  17. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +9 -3
  18. package/dist/components/MediaRecorder/AudioRecorder/AudioRecordingInProgress.js +3 -0
  19. package/dist/components/MediaRecorder/classes/MediaRecorderController.d.ts +6 -7
  20. package/dist/components/MediaRecorder/classes/MediaRecorderController.js +0 -5
  21. package/dist/components/MediaRecorder/hooks/index.d.ts +1 -1
  22. package/dist/components/MediaRecorder/hooks/useMediaRecorder.d.ts +1 -2
  23. package/dist/components/MediaRecorder/hooks/useMediaRecorder.js +1 -1
  24. package/dist/components/MediaRecorder/index.d.ts +1 -0
  25. package/dist/components/MediaRecorder/transcode/index.d.ts +6 -5
  26. package/dist/components/MediaRecorder/transcode/index.js +5 -15
  27. package/dist/components/Message/MessageSimple.js +1 -1
  28. package/dist/components/Message/MessageStatus.js +3 -2
  29. package/dist/components/Message/MessageTimestamp.d.ts +1 -2
  30. package/dist/components/Message/MessageTimestamp.js +0 -1
  31. package/dist/components/Message/Timestamp.d.ts +1 -2
  32. package/dist/components/Message/Timestamp.js +4 -5
  33. package/dist/components/Message/renderText/rehypePlugins/mentionsMarkdownPlugin.d.ts +1 -1
  34. package/dist/components/Message/renderText/remarkPlugins/htmlToTextPlugin.d.ts +1 -1
  35. package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.d.ts +1 -1
  36. package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.js +2 -6
  37. package/dist/components/Message/renderText/renderText.d.ts +2 -2
  38. package/dist/components/Message/renderText/renderText.js +8 -6
  39. package/dist/components/Message/utils.js +2 -0
  40. package/dist/components/MessageInput/AttachmentPreviewList/AttachmentPreviewList.js +23 -27
  41. package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.d.ts +1 -0
  42. package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.js +1 -1
  43. package/dist/components/MessageInput/AttachmentPreviewList/ImageAttachmentPreview.js +2 -1
  44. package/dist/components/MessageInput/MessageInput.d.ts +4 -6
  45. package/dist/components/MessageInput/MessageInputFlat.js +4 -7
  46. package/dist/components/MessageInput/hooks/useAttachments.d.ts +1 -5
  47. package/dist/components/MessageInput/hooks/useAttachments.js +65 -52
  48. package/dist/components/MessageInput/hooks/useCreateMessageInputContext.js +2 -19
  49. package/dist/components/MessageInput/hooks/useMessageInputState.d.ts +2 -35
  50. package/dist/components/MessageInput/hooks/useMessageInputState.js +2 -107
  51. package/dist/components/MessageInput/hooks/usePasteHandler.js +1 -3
  52. package/dist/components/MessageInput/hooks/useSubmitHandler.js +19 -71
  53. package/dist/components/MessageInput/hooks/useTimeElapsed.js +5 -4
  54. package/dist/components/MessageInput/hooks/utils.d.ts +1 -2
  55. package/dist/components/MessageInput/icons.d.ts +0 -1
  56. package/dist/components/MessageInput/icons.js +0 -3
  57. package/dist/components/MessageInput/types.d.ts +3 -30
  58. package/dist/components/MessageList/MessageList.d.ts +3 -1
  59. package/dist/components/MessageList/MessageList.js +2 -1
  60. package/dist/components/MessageList/VirtualizedMessageList.d.ts +3 -1
  61. package/dist/components/MessageList/VirtualizedMessageList.js +3 -3
  62. package/dist/components/MessageList/VirtualizedMessageListComponents.js +3 -2
  63. package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.d.ts +2 -1
  64. package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.js +3 -3
  65. package/dist/components/MessageList/utils.d.ts +1 -1
  66. package/dist/components/MessageList/utils.js +17 -7
  67. package/dist/components/ReactFileUtilities/types.d.ts +0 -29
  68. package/dist/components/ReactFileUtilities/utils.d.ts +2 -0
  69. package/dist/components/ReactFileUtilities/utils.js +2 -0
  70. package/dist/components/Thread/Thread.d.ts +0 -2
  71. package/dist/components/Thread/Thread.js +1 -2
  72. package/dist/components/UtilityComponents/ErrorBoundary.d.ts +16 -0
  73. package/dist/components/UtilityComponents/ErrorBoundary.js +19 -0
  74. package/dist/components/UtilityComponents/index.d.ts +1 -0
  75. package/dist/components/UtilityComponents/index.js +1 -0
  76. package/dist/components/Window/Window.d.ts +1 -3
  77. package/dist/components/Window/Window.js +2 -2
  78. package/dist/context/ChannelActionContext.d.ts +2 -2
  79. package/dist/context/MessageInputContext.d.ts +1 -5
  80. package/dist/context/TranslationContext.d.ts +1 -11
  81. package/dist/context/TranslationContext.js +1 -9
  82. package/dist/css/v2/emoji-replacement.css +1 -1
  83. package/dist/css/v2/index.css +2 -2
  84. package/dist/css/v2/index.layout.css +2 -2
  85. package/dist/i18n/Streami18n.d.ts +3 -3
  86. package/dist/i18n/Streami18n.js +1 -2
  87. package/dist/i18n/de.json +3 -1
  88. package/dist/i18n/en.json +3 -1
  89. package/dist/i18n/es.json +3 -1
  90. package/dist/i18n/fr.json +3 -1
  91. package/dist/i18n/hi.json +3 -1
  92. package/dist/i18n/index.d.ts +2 -1
  93. package/dist/i18n/index.js +2 -0
  94. package/dist/i18n/it.json +3 -1
  95. package/dist/i18n/ja.json +3 -1
  96. package/dist/i18n/ko.json +3 -1
  97. package/dist/i18n/nl.json +3 -1
  98. package/dist/i18n/pt.json +3 -1
  99. package/dist/i18n/ru.json +3 -1
  100. package/dist/i18n/tr.json +3 -1
  101. package/dist/i18n/types.d.ts +26 -0
  102. package/dist/i18n/types.js +1 -0
  103. package/dist/i18n/utils.d.ts +9 -20
  104. package/dist/i18n/utils.js +10 -1
  105. package/dist/index.browser.cjs +47221 -0
  106. package/dist/index.browser.cjs.map +7 -0
  107. package/dist/{index.cjs.js → index.node.cjs} +17604 -29018
  108. package/dist/index.node.cjs.map +7 -0
  109. package/dist/{components → plugins}/Emojis/EmojiPicker.js +1 -1
  110. package/dist/plugins/Emojis/icons.d.ts +2 -0
  111. package/dist/plugins/Emojis/icons.js +4 -0
  112. package/dist/plugins/Emojis/index.browser.cjs +167 -0
  113. package/dist/plugins/Emojis/index.browser.cjs.map +7 -0
  114. package/dist/plugins/Emojis/index.d.ts +2 -0
  115. package/dist/plugins/Emojis/index.js +2 -0
  116. package/dist/{components/Emojis/index.cjs.js → plugins/Emojis/index.node.cjs} +31 -192
  117. package/dist/plugins/Emojis/index.node.cjs.map +7 -0
  118. package/dist/plugins/encoders/mp3.browser.cjs +105 -0
  119. package/dist/plugins/encoders/mp3.browser.cjs.map +7 -0
  120. package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.js +3 -3
  121. package/dist/plugins/encoders/mp3.node.cjs +109 -0
  122. package/dist/plugins/encoders/mp3.node.cjs.map +7 -0
  123. package/dist/scss/v2/Autocomplete/Autocomplete-layout.scss +1 -1
  124. package/dist/scss/v2/Autocomplete/Autocomplete-theme.scss +4 -2
  125. package/dist/scss/v2/Avatar/Avatar-layout.scss +31 -23
  126. package/dist/scss/v2/Channel/Channel-layout.scss +0 -4
  127. package/dist/scss/v2/ChannelList/ChannelList-layout.scss +0 -5
  128. package/dist/scss/v2/ChannelSearch/ChannelSearch-layout.scss +1 -0
  129. package/dist/scss/v2/EditMessageForm/EditMessageForm-theme.scss +9 -9
  130. package/dist/scss/v2/Message/Message-layout.scss +39 -6
  131. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +18 -0
  132. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +5 -0
  133. package/dist/scss/v2/Thread/Thread-layout.scss +0 -5
  134. package/dist/scss/v2/_base.scss +1 -0
  135. package/dist/scss/v2/_emoji-replacement.scss +4 -2
  136. package/package.json +50 -18
  137. package/dist/components/Emojis/index.cjs.js.map +0 -7
  138. package/dist/components/Emojis/index.d.ts +0 -1
  139. package/dist/components/Emojis/index.js +0 -1
  140. package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.d.ts +0 -11
  141. package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.js +0 -51
  142. package/dist/components/MessageInput/hooks/useFileUploads.d.ts +0 -7
  143. package/dist/components/MessageInput/hooks/useFileUploads.js +0 -85
  144. package/dist/components/MessageInput/hooks/useImageUploads.d.ts +0 -8
  145. package/dist/components/MessageInput/hooks/useImageUploads.js +0 -94
  146. package/dist/index.cjs.js.map +0 -7
  147. /package/dist/{components → plugins}/Emojis/EmojiPicker.d.ts +0 -0
  148. /package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.d.ts +0 -0
@@ -1,8 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { StreamMessage } from '../../context/ChannelStateContext';
3
+ import type { TimestampFormatterOptions } from '../../i18n/types';
3
4
  import type { DefaultStreamChatGenerics } from '../../types/types';
4
- import type { TimestampFormatterOptions } from '../../i18n/utils';
5
- export declare const defaultTimestampFormat = "h:mmA";
6
5
  export type MessageTimestampProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = TimestampFormatterOptions & {
7
6
  customClass?: string;
8
7
  message?: StreamMessage<StreamChatGenerics>;
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import { useMessageContext } from '../../context/MessageContext';
3
3
  import { Timestamp as DefaultTimestamp } from './Timestamp';
4
4
  import { useComponentContext } from '../../context';
5
- export const defaultTimestampFormat = 'h:mmA';
6
5
  const UnMemoizedMessageTimestamp = (props) => {
7
6
  const { message: propMessage, ...timestampProps } = props;
8
7
  const { message: contextMessage } = useMessageContext('MessageTimestamp');
@@ -1,8 +1,7 @@
1
1
  import React from 'react';
2
- import { TimestampFormatterOptions } from '../../i18n/utils';
2
+ import type { TimestampFormatterOptions } from '../../i18n/types';
3
3
  export interface TimestampProps extends TimestampFormatterOptions {
4
4
  customClass?: string;
5
5
  timestamp?: Date | string;
6
6
  }
7
- export declare const defaultTimestampFormat = "h:mmA";
8
7
  export declare function Timestamp(props: TimestampProps): React.JSX.Element | null;
@@ -1,10 +1,9 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { useMessageContext } from '../../context/MessageContext';
3
- import { isDate, useTranslationContext } from '../../context/TranslationContext';
4
- import { getDateString } from '../../i18n/utils';
5
- export const defaultTimestampFormat = 'h:mmA';
3
+ import { useTranslationContext } from '../../context/TranslationContext';
4
+ import { getDateString, isDate } from '../../i18n/utils';
6
5
  export function Timestamp(props) {
7
- const { calendar, calendarFormats, customClass, format = defaultTimestampFormat, timestamp, } = props;
6
+ const { calendar, calendarFormats, customClass, format, timestamp } = props;
8
7
  const { formatDate } = useMessageContext('MessageTimestamp');
9
8
  const { t, tDateTimeParser } = useTranslationContext('MessageTimestamp');
10
9
  const normalizedTimestamp = timestamp && isDate(timestamp) ? timestamp.toISOString() : timestamp;
@@ -16,7 +15,7 @@ export function Timestamp(props) {
16
15
  messageCreatedAt: normalizedTimestamp,
17
16
  t,
18
17
  tDateTimeParser,
19
- timestampTranslationKey: 'timestamp/Timestamp',
18
+ timestampTranslationKey: 'timestamp/MessageTimestamp',
20
19
  }), [calendar, calendarFormats, format, formatDate, normalizedTimestamp, t, tDateTimeParser]);
21
20
  if (!when) {
22
21
  return null;
@@ -1,4 +1,4 @@
1
1
  import type { Nodes } from 'hast-util-find-and-replace/lib';
2
2
  import type { UserResponse } from 'stream-chat';
3
- import type { DefaultStreamChatGenerics } from '../../../../types/types';
3
+ import type { DefaultStreamChatGenerics } from '../../../../types';
4
4
  export declare const mentionsMarkdownPlugin: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(mentioned_users: UserResponse<StreamChatGenerics>[]) => () => (tree: Nodes) => void;
@@ -1,2 +1,2 @@
1
- import type { Nodes } from 'react-markdown/lib';
1
+ import type { Nodes } from 'hast-util-find-and-replace/lib';
2
2
  export declare const htmlToTextPlugin: () => (tree: Nodes) => void;
@@ -1,2 +1,2 @@
1
- import type { Nodes } from 'react-markdown/lib';
1
+ import type { Nodes } from 'hast-util-find-and-replace/lib';
2
2
  export declare const keepLineBreaksPlugin: () => (tree: Nodes) => void;
@@ -1,17 +1,13 @@
1
1
  import { visit } from 'unist-util-visit';
2
2
  import { u } from 'unist-builder';
3
3
  const visitor = (node, index, parent) => {
4
- if (typeof index === 'undefined' || index === 0)
5
- return;
6
- if (typeof parent === 'undefined')
7
- return;
8
- if (!node.position)
4
+ if (!(index && parent && node.position))
9
5
  return;
10
6
  const prevSibling = parent.children.at(index - 1);
11
7
  if (!prevSibling?.position)
12
8
  return;
13
9
  if (node.position.start.line === prevSibling.position.start.line)
14
- return false;
10
+ return;
15
11
  const ownStartLine = node.position.start.line;
16
12
  const prevEndLine = prevSibling.position.end.line;
17
13
  // the -1 is adjustment for the single line break into which multiple line breaks are converted
@@ -1,8 +1,8 @@
1
1
  import React, { ComponentType } from 'react';
2
2
  import { Options } from 'react-markdown';
3
- import { MentionProps } from './componentRenderers';
4
- import type { PluggableList } from 'react-markdown/lib';
3
+ import type { PluggableList } from 'react-markdown/lib/react-markdown';
5
4
  import type { UserResponse } from 'stream-chat';
5
+ import { MentionProps } from './componentRenderers';
6
6
  import type { DefaultStreamChatGenerics } from '../../../types/types';
7
7
  export type RenderTextPluginConfigurator = (defaultPlugins: PluggableList) => PluggableList;
8
8
  export declare const defaultAllowedTagNames: Array<keyof JSX.IntrinsicElements | 'emoji' | 'mention'>;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import ReactMarkdown, { defaultUrlTransform } from 'react-markdown';
2
+ import ReactMarkdown, { uriTransformer } from 'react-markdown';
3
3
  import { find } from 'linkifyjs';
4
4
  import uniqBy from 'lodash.uniqby';
5
5
  import remarkGfm from 'remark-gfm';
@@ -7,6 +7,7 @@ import { Anchor, Emoji, Mention } from './componentRenderers';
7
7
  import { detectHttp, escapeRegExp, matchMarkdownLinks, messageCodeBlocks } from './regex';
8
8
  import { emojiMarkdownPlugin, mentionsMarkdownPlugin } from './rehypePlugins';
9
9
  import { htmlToTextPlugin, keepLineBreaksPlugin } from './remarkPlugins';
10
+ import { ErrorBoundary } from '../../UtilityComponents';
10
11
  export const defaultAllowedTagNames = [
11
12
  'html',
12
13
  'text',
@@ -42,7 +43,7 @@ function encodeDecode(url) {
42
43
  return url;
43
44
  }
44
45
  }
45
- const urlTransform = (uri) => (uri.startsWith('app://') ? uri : defaultUrlTransform(uri));
46
+ const urlTransform = (uri) => (uri.startsWith('app://') ? uri : uriTransformer(uri));
46
47
  const getPluginsForward = (plugins) => plugins;
47
48
  export const markDownRenderers = {
48
49
  a: Anchor,
@@ -106,8 +107,9 @@ export const renderText = (text, mentionedUsers, { allowedTagNames = defaultAllo
106
107
  if (mentionedUsers?.length) {
107
108
  rehypePlugins.push(mentionsMarkdownPlugin(mentionedUsers));
108
109
  }
109
- return (React.createElement(ReactMarkdown, { allowedElements: allowedTagNames, components: {
110
- ...markDownRenderers,
111
- ...customMarkDownRenderers,
112
- }, rehypePlugins: getRehypePlugins(rehypePlugins), remarkPlugins: getRemarkPlugins(remarkPlugins), skipHtml: true, unwrapDisallowed: true, urlTransform: urlTransform }, newText));
110
+ return (React.createElement(ErrorBoundary, { fallback: React.createElement(React.Fragment, null, text) },
111
+ React.createElement(ReactMarkdown, { allowedElements: allowedTagNames, components: {
112
+ ...markDownRenderers,
113
+ ...customMarkDownRenderers,
114
+ }, rehypePlugins: getRehypePlugins(rehypePlugins), remarkPlugins: getRemarkPlugins(remarkPlugins), skipHtml: true, transformLinkUri: urlTransform, unwrapDisallowed: true }, newText)));
113
115
  };
@@ -212,6 +212,8 @@ export const areMessageUIPropsEqual = (prevProps, nextProps) => {
212
212
  return false;
213
213
  if (prevProps.readBy?.length !== nextProps.readBy?.length)
214
214
  return false;
215
+ if (prevProps.groupStyles !== nextProps.groupStyles)
216
+ return false;
215
217
  if (prevProps.showDetailedReactions !== nextProps.showDetailedReactions) {
216
218
  return false;
217
219
  }
@@ -2,37 +2,33 @@ import React from 'react';
2
2
  import { UnsupportedAttachmentPreview as DefaultUnknownAttachmentPreview, } from './UnsupportedAttachmentPreview';
3
3
  import { VoiceRecordingPreview as DefaultVoiceRecordingPreview, } from './VoiceRecordingPreview';
4
4
  import { FileAttachmentPreview as DefaultFilePreview, } from './FileAttachmentPreview';
5
- import { FileUploadPreviewAdapter, ImageUploadPreviewAdapter } from './UploadPreviewItem';
6
5
  import { ImageAttachmentPreview as DefaultImagePreview, } from './ImageAttachmentPreview';
7
6
  import { isLocalAttachment, isLocalAudioAttachment, isLocalFileAttachment, isLocalImageAttachment, isLocalMediaAttachment, isLocalVoiceRecordingAttachment, isScrapedContent, } from '../../Attachment';
8
7
  import { useMessageInputContext } from '../../../context';
9
8
  export const AttachmentPreviewList = ({ AudioAttachmentPreview = DefaultFilePreview, FileAttachmentPreview = DefaultFilePreview, ImageAttachmentPreview = DefaultImagePreview, UnsupportedAttachmentPreview = DefaultUnknownAttachmentPreview, VideoAttachmentPreview = DefaultFilePreview, VoiceRecordingPreview = DefaultVoiceRecordingPreview, }) => {
10
- const { attachments, fileOrder, imageOrder, removeAttachments, uploadAttachment, } = useMessageInputContext('AttachmentPreviewList');
9
+ const { attachments, removeAttachments, uploadAttachment, } = useMessageInputContext('AttachmentPreviewList');
11
10
  return (React.createElement("div", { className: 'str-chat__attachment-preview-list' },
12
- React.createElement("div", { className: 'str-chat__attachment-list-scroll-container', "data-testid": 'attachment-list-scroll-container' },
13
- attachments.map((attachment) => {
14
- if (isScrapedContent(attachment))
15
- return null;
16
- if (isLocalVoiceRecordingAttachment(attachment)) {
17
- return (React.createElement(VoiceRecordingPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
18
- }
19
- else if (isLocalAudioAttachment(attachment)) {
20
- return (React.createElement(AudioAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
21
- }
22
- else if (isLocalMediaAttachment(attachment)) {
23
- return (React.createElement(VideoAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
24
- }
25
- else if (isLocalImageAttachment(attachment)) {
26
- return (React.createElement(ImageAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.image_url, removeAttachments: removeAttachments }));
27
- }
28
- else if (isLocalFileAttachment(attachment)) {
29
- return (React.createElement(FileAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
30
- }
31
- else if (isLocalAttachment(attachment)) {
32
- return (React.createElement(UnsupportedAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id, removeAttachments: removeAttachments }));
33
- }
11
+ React.createElement("div", { className: 'str-chat__attachment-list-scroll-container', "data-testid": 'attachment-list-scroll-container' }, attachments.map((attachment) => {
12
+ if (isScrapedContent(attachment))
34
13
  return null;
35
- }),
36
- imageOrder.map((id) => (React.createElement(ImageUploadPreviewAdapter, { id: id, key: id, Preview: ImageAttachmentPreview }))),
37
- fileOrder.map((id) => (React.createElement(FileUploadPreviewAdapter, { id: id, key: id, Preview: FileAttachmentPreview }))))));
14
+ if (isLocalVoiceRecordingAttachment(attachment)) {
15
+ return (React.createElement(VoiceRecordingPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
16
+ }
17
+ else if (isLocalAudioAttachment(attachment)) {
18
+ return (React.createElement(AudioAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
19
+ }
20
+ else if (isLocalMediaAttachment(attachment)) {
21
+ return (React.createElement(VideoAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
22
+ }
23
+ else if (isLocalImageAttachment(attachment)) {
24
+ return (React.createElement(ImageAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.image_url, removeAttachments: removeAttachments }));
25
+ }
26
+ else if (isLocalFileAttachment(attachment)) {
27
+ return (React.createElement(FileAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id || attachment.asset_url, removeAttachments: removeAttachments }));
28
+ }
29
+ else if (isLocalAttachment(attachment)) {
30
+ return (React.createElement(UnsupportedAttachmentPreview, { attachment: attachment, handleRetry: uploadAttachment, key: attachment.localMetadata.id, removeAttachments: removeAttachments }));
31
+ }
32
+ return null;
33
+ }))));
38
34
  };
@@ -4,6 +4,7 @@ import { LocalAttachmentCast, LocalAttachmentUploadMetadata } from '../types';
4
4
  import type { DefaultStreamChatGenerics } from '../../../types';
5
5
  type FileLikeAttachment = {
6
6
  asset_url?: string;
7
+ file_size?: number;
7
8
  mime_type?: string;
8
9
  title?: string;
9
10
  };
@@ -13,7 +13,7 @@ export const FileAttachmentPreview = ({ attachment, handleRetry, removeAttachmen
13
13
  React.createElement(RetryIcon, null))),
14
14
  React.createElement("div", { className: 'str-chat__attachment-preview-file-end' },
15
15
  React.createElement("div", { className: 'str-chat__attachment-preview-file-name', title: attachment.title }, attachment.title),
16
- attachment.localMetadata?.uploadState === 'finished' && !!attachment.asset_url && (React.createElement("a", { className: 'str-chat__attachment-preview-file-download', download: true, href: attachment.asset_url, rel: 'noreferrer', target: '_blank' },
16
+ attachment.localMetadata?.uploadState === 'finished' && !!attachment.asset_url && (React.createElement("a", { "aria-label": t('aria/Download attachment'), className: 'str-chat__attachment-preview-file-download', download: true, href: attachment.asset_url, rel: 'noreferrer', target: '_blank', title: t('Download attachment {{ name }}', { name: attachment.title }) },
17
17
  React.createElement(DownloadIcon, null))),
18
18
  attachment.localMetadata?.uploadState === 'uploading' && (React.createElement(LoadingIndicatorIcon, { size: 17 })))));
19
19
  };
@@ -9,6 +9,7 @@ export const ImageAttachmentPreview = ({ attachment, handleRetry, removeAttachme
9
9
  const [previewError, setPreviewError] = useState(false);
10
10
  const { id, uploadState } = attachment.localMetadata ?? {};
11
11
  const handleLoadError = useCallback(() => setPreviewError(true), []);
12
+ const assetUrl = attachment.image_url || attachment.localMetadata.previewUri;
12
13
  return (React.createElement("div", { className: clsx('str-chat__attachment-preview-image', {
13
14
  'str-chat__attachment-preview-image--error': previewError,
14
15
  }), "data-testid": 'attachment-preview-image' },
@@ -18,5 +19,5 @@ export const ImageAttachmentPreview = ({ attachment, handleRetry, removeAttachme
18
19
  React.createElement(RetryIcon, null))),
19
20
  uploadState === 'uploading' && (React.createElement("div", { className: 'str-chat__attachment-preview-image-loading' },
20
21
  React.createElement(LoadingIndicatorIcon, { size: 17 }))),
21
- attachment.image_url && (React.createElement(BaseImage, { alt: attachment.title, className: 'str-chat__attachment-preview-thumbnail', onError: handleLoadError, src: attachment.image_url, title: attachment.title }))));
22
+ assetUrl && (React.createElement(BaseImage, { alt: attachment.fallback, className: 'str-chat__attachment-preview-thumbnail', onError: handleLoadError, src: assetUrl, title: attachment.fallback }))));
22
23
  };
@@ -2,11 +2,11 @@ import React from 'react';
2
2
  import { StreamMessage } from '../../context/ChannelStateContext';
3
3
  import { ComponentContextValue } from '../../context/ComponentContext';
4
4
  import type { Channel, Message, SendFileAPIResponse } from 'stream-chat';
5
+ import type { BaseLocalAttachmentMetadata, LocalAttachmentUploadMetadata } from './types';
5
6
  import type { SearchQueryParams } from '../ChannelSearch/hooks/useChannelSearch';
6
7
  import type { MessageToSend } from '../../context/ChannelActionContext';
7
8
  import type { CustomTrigger, DefaultStreamChatGenerics, SendMessageOptions, UnknownType } from '../../types/types';
8
9
  import type { URLEnrichmentConfig } from './hooks/useLinkPreviews';
9
- import type { FileUpload, ImageUpload } from './types';
10
10
  import type { CustomAudioRecordingConfig } from '../MediaRecorder';
11
11
  export type EmojiSearchIndexResult = {
12
12
  id: string;
@@ -39,15 +39,13 @@ export type MessageInputProps<StreamChatGenerics extends DefaultStreamChatGeneri
39
39
  /** If true, the suggestion list will not display and autocomplete @mentions. Default: false. */
40
40
  disableMentions?: boolean;
41
41
  /** Function to override the default file upload request */
42
- doFileUploadRequest?: (file: FileUpload['file'], channel: Channel<StreamChatGenerics>) => Promise<SendFileAPIResponse>;
42
+ doFileUploadRequest?: (file: LocalAttachmentUploadMetadata['file'], channel: Channel<StreamChatGenerics>) => Promise<SendFileAPIResponse>;
43
43
  /** Function to override the default image upload request */
44
- doImageUploadRequest?: (file: ImageUpload['file'], channel: Channel<StreamChatGenerics>) => Promise<SendFileAPIResponse>;
44
+ doImageUploadRequest?: (file: LocalAttachmentUploadMetadata['file'], channel: Channel<StreamChatGenerics>) => Promise<SendFileAPIResponse>;
45
45
  /** Mechanism to be used with autocomplete and text replace features of the `MessageInput` component, see [emoji-mart `SearchIndex`](https://github.com/missive/emoji-mart#%EF%B8%8F%EF%B8%8F-headless-search) */
46
46
  emojiSearchIndex?: ComponentContextValue['emojiSearchIndex'];
47
47
  /** Custom error handler function to be called with a file/image upload fails */
48
- errorHandler?: (error: Error, type: string, file: (FileUpload | ImageUpload)['file'] & {
49
- id?: string;
50
- }) => void;
48
+ errorHandler?: (error: Error, type: string, file: LocalAttachmentUploadMetadata['file'] & BaseLocalAttachmentMetadata) => void;
51
49
  /** If true, focuses the text input on component mount */
52
50
  focus?: boolean;
53
51
  /** Generates the default value for the underlying textarea element. The function's return value takes precedence before additionalTextareaProps.defaultValue. */
@@ -20,7 +20,7 @@ import { useMessageInputContext } from '../../context/MessageInputContext';
20
20
  import { useComponentContext } from '../../context/ComponentContext';
21
21
  export const MessageInputFlat = () => {
22
22
  const { t } = useTranslationContext('MessageInputFlat');
23
- const { asyncMessagesMultiSendEnabled, attachments, cooldownRemaining, fileUploads, findAndEnqueueURLsToEnrich, handleSubmit, hideSendButton, imageUploads, isUploadEnabled, linkPreviews, maxFilesLeft, message, numberOfUploads, recordingController, setCooldownRemaining, text, uploadNewFiles, } = useMessageInputContext('MessageInputFlat');
23
+ const { asyncMessagesMultiSendEnabled, attachments, cooldownRemaining, findAndEnqueueURLsToEnrich, handleSubmit, hideSendButton, isUploadEnabled, linkPreviews, maxFilesLeft, message, numberOfUploads, recordingController, setCooldownRemaining, text, uploadNewFiles, } = useMessageInputContext('MessageInputFlat');
24
24
  const { AudioRecorder = DefaultAudioRecorder, AttachmentPreviewList = DefaultAttachmentPreviewList, CooldownTimer = DefaultCooldownTimer, FileUploadIcon = DefaultUploadIcon, LinkPreviewList = DefaultLinkPreviewList, QuotedMessagePreview = DefaultQuotedMessagePreview, RecordingPermissionDeniedNotification = DefaultRecordingPermissionDeniedNotification, SendButton = DefaultSendButton, StartRecordingAudioButton = DefaultStartRecordingAudioButton, EmojiPicker, } = useComponentContext('MessageInputFlat');
25
25
  const { acceptedFiles = [], multipleUploads, quotedMessage, } = useChannelStateContext('MessageInputFlat');
26
26
  const { setQuotedMessage } = useChannelActionContext('MessageInputFlat');
@@ -30,9 +30,7 @@ export const MessageInputFlat = () => {
30
30
  setShowRecordingPermissionDeniedNotification(false);
31
31
  }, []);
32
32
  const id = useMemo(() => nanoid(), []);
33
- const failedAttachmentsCount = useMemo(() => attachments.filter((a) => a.localMetadata?.uploadState === 'failed').length, [attachments]);
34
- const failedUploadsCount = useMemo(() => Object.values(fileUploads).filter((upload) => upload.state === 'failed').length +
35
- Object.values(imageUploads).filter((upload) => upload.state === 'failed').length, [fileUploads, imageUploads]);
33
+ const failedUploadsCount = useMemo(() => attachments.filter((a) => a.localMetadata?.uploadState === 'failed').length, [attachments]);
36
34
  const accept = useMemo(() => acceptedFiles.reduce((mediaTypeMap, mediaType) => {
37
35
  mediaTypeMap[mediaType] ?? (mediaTypeMap[mediaType] = []);
38
36
  return mediaTypeMap;
@@ -89,15 +87,14 @@ export const MessageInputFlat = () => {
89
87
  React.createElement("div", { className: 'str-chat__message-textarea-container' },
90
88
  displayQuotedMessage && React.createElement(QuotedMessagePreview, { quotedMessage: quotedMessage }),
91
89
  isUploadEnabled &&
92
- !!(numberOfUploads + failedUploadsCount ||
93
- (attachments.length && attachments.length !== linkPreviews.size)) && React.createElement(AttachmentPreviewList, null),
90
+ !!(numberOfUploads + failedUploadsCount || attachments.length > 0) && (React.createElement(AttachmentPreviewList, null)),
94
91
  React.createElement("div", { className: 'str-chat__message-textarea-with-emoji-picker' },
95
92
  React.createElement(ChatAutoComplete, null),
96
93
  EmojiPicker && React.createElement(EmojiPicker, null))),
97
94
  !hideSendButton && (React.createElement(React.Fragment, null, cooldownRemaining ? (React.createElement(CooldownTimer, { cooldownInterval: cooldownRemaining, setCooldownRemaining: setCooldownRemaining })) : (React.createElement(React.Fragment, null,
98
95
  React.createElement(SendButton, { disabled: !numberOfUploads &&
99
96
  !text.length &&
100
- attachments.length - failedAttachmentsCount === 0, sendMessage: handleSubmit }),
97
+ attachments.length - failedUploadsCount === 0, sendMessage: handleSubmit }),
101
98
  recordingEnabled && (React.createElement(StartRecordingAudioButton, { disabled: isRecording ||
102
99
  (!asyncMessagesMultiSendEnabled &&
103
100
  attachments.some((a) => a.type === RecordingAttachmentType.VOICE_RECORDING)), onClick: () => {
@@ -1,18 +1,14 @@
1
+ import type { FileLike } from '../../ReactFileUtilities';
1
2
  import type { Attachment } from 'stream-chat';
2
3
  import type { MessageInputReducerAction, MessageInputState } from './useMessageInputState';
3
4
  import type { MessageInputProps } from '../MessageInput';
4
5
  import type { LocalAttachment } from '../types';
5
- import type { FileLike } from '../../ReactFileUtilities';
6
6
  import type { CustomTrigger, DefaultStreamChatGenerics } from '../../../types/types';
7
7
  export declare const useAttachments: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger>(props: MessageInputProps<StreamChatGenerics, V>, state: MessageInputState<StreamChatGenerics>, dispatch: React.Dispatch<MessageInputReducerAction<StreamChatGenerics>>, textareaRef: React.MutableRefObject<HTMLTextAreaElement | undefined>) => {
8
8
  maxFilesLeft: number;
9
9
  numberOfUploads: number;
10
10
  removeAttachments: (ids: string[]) => void;
11
- removeFile: (id: string) => void;
12
- removeImage: (id: string) => void;
13
11
  uploadAttachment: (att: LocalAttachment<StreamChatGenerics>) => Promise<LocalAttachment<StreamChatGenerics> | undefined>;
14
- uploadFile: (id: string) => void;
15
- uploadImage: (id: string) => Promise<void>;
16
12
  uploadNewFiles: (files: FileList | File[] | FileLike[]) => void;
17
13
  upsertAttachments: (attachments: (Attachment<StreamChatGenerics> | LocalAttachment<StreamChatGenerics>)[]) => void;
18
14
  };
@@ -1,11 +1,22 @@
1
1
  import { useCallback } from 'react';
2
2
  import { nanoid } from 'nanoid';
3
- import { useImageUploads } from './useImageUploads';
4
- import { useFileUploads } from './useFileUploads';
5
3
  import { checkUploadPermissions } from './utils';
6
- import { isLocalAttachment, isLocalImageAttachment, isUploadedImage } from '../../Attachment';
4
+ import { isLocalAttachment, isLocalImageAttachment } from '../../Attachment';
5
+ import { createFileFromBlobs, generateFileName, isBlobButNotFile } from '../../ReactFileUtilities';
7
6
  import { useChannelActionContext, useChannelStateContext, useChatContext, useTranslationContext, } from '../../../context';
8
7
  const apiMaxNumberOfFiles = 10;
8
+ // const isAudioFile = (file: FileLike) => file.type.includes('audio/');
9
+ const isImageFile = (file) => file.type.startsWith('image/') && !file.type.endsWith('.photoshop'); // photoshop files begin with 'image/'
10
+ // const isVideoFile = (file: FileLike) => file.type.includes('video/');
11
+ const getAttachmentTypeFromMime = (mimeType) => {
12
+ if (mimeType.startsWith('image/') && !mimeType.endsWith('.photoshop'))
13
+ return 'image';
14
+ if (mimeType.includes('video/'))
15
+ return 'video';
16
+ if (mimeType.includes('audio/'))
17
+ return 'audio';
18
+ return 'file';
19
+ };
9
20
  const ensureIsLocalAttachment = (attachment) => {
10
21
  if (isLocalAttachment(attachment)) {
11
22
  return attachment;
@@ -21,45 +32,15 @@ const ensureIsLocalAttachment = (attachment) => {
21
32
  };
22
33
  export const useAttachments = (props, state, dispatch, textareaRef) => {
23
34
  const { doFileUploadRequest, doImageUploadRequest, errorHandler, noFiles } = props;
24
- const { fileUploads, imageUploads } = state;
25
35
  const { getAppSettings } = useChatContext('useAttachments');
26
36
  const { t } = useTranslationContext('useAttachments');
27
37
  const { addNotification } = useChannelActionContext('useAttachments');
28
38
  const { channel, maxNumberOfFiles, multipleUploads } = useChannelStateContext('useAttachments');
29
- const { removeFile, uploadFile } = useFileUploads(props, state, dispatch);
30
- const { removeImage, uploadImage } = useImageUploads(props, state, dispatch);
31
39
  // Number of files that the user can still add. Should never be more than the amount allowed by the API.
32
40
  // If multipleUploads is false, we only want to allow a single upload.
33
41
  const maxFilesAllowed = !multipleUploads ? 1 : maxNumberOfFiles || apiMaxNumberOfFiles;
34
- // OG attachments should not be counted towards "numberOfImages"
35
- const numberOfImages = Object.values(imageUploads).filter(({ og_scrape_url, state }) => state !== 'failed' && !og_scrape_url).length;
36
- const numberOfFiles = Object.values(fileUploads).filter(({ state }) => state !== 'failed').length;
37
- const numberOfUploads = numberOfImages + numberOfFiles;
42
+ const numberOfUploads = Object.values(state.attachments).filter(({ localMetadata }) => localMetadata.uploadState && localMetadata.uploadState !== 'failed').length;
38
43
  const maxFilesLeft = maxFilesAllowed - numberOfUploads;
39
- const uploadNewFiles = useCallback((files) => {
40
- Array.from(files)
41
- .slice(0, maxFilesLeft)
42
- .forEach((file) => {
43
- const id = nanoid();
44
- if (file.type.startsWith('image/') &&
45
- !file.type.endsWith('.photoshop') // photoshop files begin with 'image/'
46
- ) {
47
- dispatch({
48
- file,
49
- id,
50
- previewUri: URL.createObjectURL?.(file),
51
- state: 'uploading',
52
- type: 'setImageUpload',
53
- });
54
- }
55
- else if (file instanceof File && !noFiles) {
56
- dispatch({ file, id, state: 'uploading', type: 'setFileUpload' });
57
- }
58
- });
59
- textareaRef?.current?.focus();
60
- },
61
- // eslint-disable-next-line react-hooks/exhaustive-deps
62
- [maxFilesLeft, noFiles]);
63
44
  const removeAttachments = useCallback((ids) => {
64
45
  if (!ids.length)
65
46
  return;
@@ -74,12 +55,13 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
74
55
  });
75
56
  }, [dispatch]);
76
57
  const uploadAttachment = useCallback(async (att) => {
77
- const { localMetadata, ...attachment } = att;
58
+ const { localMetadata, ...providedAttachmentData } = att;
78
59
  if (!localMetadata?.file)
79
60
  return att;
80
- const isImage = isUploadedImage(attachment);
81
- const id = localMetadata?.id ?? nanoid();
82
61
  const { file } = localMetadata;
62
+ const isImage = isImageFile(file);
63
+ if (noFiles && !isImage)
64
+ return att;
83
65
  const canUpload = await checkUploadPermissions({
84
66
  addNotification,
85
67
  file,
@@ -87,18 +69,31 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
87
69
  t,
88
70
  uploadType: isImage ? 'image' : 'file',
89
71
  });
90
- if (!canUpload) {
91
- const notificationText = t('Missing permissions to upload the attachment');
92
- console.error(new Error(notificationText));
93
- addNotification(notificationText, 'error');
72
+ if (!canUpload)
94
73
  return att;
74
+ localMetadata.id = localMetadata?.id ?? nanoid();
75
+ const finalAttachment = {
76
+ type: getAttachmentTypeFromMime(file.type),
77
+ };
78
+ if (isImage) {
79
+ localMetadata.previewUri = URL.createObjectURL?.(file);
80
+ if (file instanceof File) {
81
+ finalAttachment.fallback = file.name;
82
+ }
95
83
  }
84
+ else {
85
+ finalAttachment.file_size = file.size;
86
+ finalAttachment.mime_type = file.type;
87
+ if (file instanceof File) {
88
+ finalAttachment.title = file.name;
89
+ }
90
+ }
91
+ Object.assign(finalAttachment, providedAttachmentData);
96
92
  upsertAttachments([
97
93
  {
98
- ...attachment,
94
+ ...finalAttachment,
99
95
  localMetadata: {
100
96
  ...localMetadata,
101
- id,
102
97
  uploadState: 'uploading',
103
98
  },
104
99
  },
@@ -124,7 +119,7 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
124
119
  console.error(finalError);
125
120
  addNotification(finalError.message, 'error');
126
121
  const failedAttachment = {
127
- ...attachment,
122
+ ...finalAttachment,
128
123
  localMetadata: {
129
124
  ...localMetadata,
130
125
  uploadState: 'failed',
@@ -132,19 +127,19 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
132
127
  };
133
128
  upsertAttachments([failedAttachment]);
134
129
  if (errorHandler) {
135
- errorHandler(finalError, 'upload-attachment', file);
130
+ errorHandler(finalError, 'upload-attachment', { ...file, id: localMetadata.id });
136
131
  }
137
132
  return failedAttachment;
138
133
  }
139
134
  if (!response) {
140
- // Copied this from useImageUpload / useFileUpload. Not sure how failure could be handled on app level.
135
+ // Copied this from useImageUpload / useFileUpload.
141
136
  // If doUploadRequest returns any falsy value, then don't create the upload preview.
142
137
  // This is for the case if someone wants to handle failure on app level.
143
- removeAttachments([id]);
138
+ removeAttachments([localMetadata.id]);
144
139
  return;
145
140
  }
146
141
  const uploadedAttachment = {
147
- ...attachment,
142
+ ...finalAttachment,
148
143
  localMetadata: {
149
144
  ...localMetadata,
150
145
  uploadState: 'finished',
@@ -160,6 +155,9 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
160
155
  else {
161
156
  uploadedAttachment.asset_url = response.file;
162
157
  }
158
+ if (response.thumb_url) {
159
+ uploadedAttachment.thumb_url = response.thumb_url;
160
+ }
163
161
  upsertAttachments([uploadedAttachment]);
164
162
  return uploadedAttachment;
165
163
  }, [
@@ -169,19 +167,34 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
169
167
  doImageUploadRequest,
170
168
  errorHandler,
171
169
  getAppSettings,
170
+ noFiles,
172
171
  removeAttachments,
173
172
  t,
174
173
  upsertAttachments,
175
174
  ]);
175
+ const uploadNewFiles = useCallback((files) => {
176
+ const filesToBeUploaded = noFiles ? Array.from(files).filter(isImageFile) : Array.from(files);
177
+ filesToBeUploaded.slice(0, maxFilesLeft).forEach((fileLike) => {
178
+ uploadAttachment({
179
+ localMetadata: {
180
+ file: isBlobButNotFile(fileLike)
181
+ ? createFileFromBlobs({
182
+ blobsArray: [fileLike],
183
+ fileName: generateFileName(fileLike.type),
184
+ mimeType: fileLike.type,
185
+ })
186
+ : fileLike,
187
+ id: nanoid(),
188
+ },
189
+ });
190
+ });
191
+ textareaRef.current?.focus();
192
+ }, [maxFilesLeft, noFiles, textareaRef, uploadAttachment]);
176
193
  return {
177
194
  maxFilesLeft,
178
195
  numberOfUploads,
179
196
  removeAttachments,
180
- removeFile,
181
- removeImage,
182
197
  uploadAttachment,
183
- uploadFile,
184
- uploadImage,
185
198
  uploadNewFiles,
186
199
  upsertAttachments,
187
200
  };
@@ -1,15 +1,7 @@
1
1
  import { useMemo } from 'react';
2
2
  export const useCreateMessageInputContext = (value) => {
3
- const { additionalTextareaProps, asyncMessagesMultiSendEnabled, attachments, audioRecordingEnabled, autocompleteTriggers, cancelURLEnrichment, clearEditingState, closeCommandsList, closeMentionsList, cooldownInterval, cooldownRemaining, disabled, disableMentions, dismissLinkPreview, doFileUploadRequest, doImageUploadRequest, emojiSearchIndex, errorHandler, fileOrder, fileUploads, findAndEnqueueURLsToEnrich, focus, grow, handleChange, handleSubmit, hideSendButton, imageOrder, imageUploads, insertText, isUploadEnabled, linkPreviews, maxFilesLeft, maxRows, mentionAllAppUsers, mentioned_users, mentionQueryParams, message, minRows, noFiles, numberOfUploads, onPaste, onSelectUser, openCommandsList, openMentionsList, overrideSubmitHandler, parent, publishTypingEvent, recordingController, removeAttachments, removeFile, removeImage, setCooldownRemaining, setText, shouldSubmit, showCommandsList, showMentionsList, text, textareaRef, uploadAttachment, uploadFile, uploadImage, uploadNewFiles, upsertAttachments, useMentionsTransliteration, } = value;
3
+ const { additionalTextareaProps, asyncMessagesMultiSendEnabled, attachments, audioRecordingEnabled, autocompleteTriggers, cancelURLEnrichment, clearEditingState, closeCommandsList, closeMentionsList, cooldownInterval, cooldownRemaining, disabled, disableMentions, dismissLinkPreview, doFileUploadRequest, doImageUploadRequest, emojiSearchIndex, errorHandler, findAndEnqueueURLsToEnrich, focus, grow, handleChange, handleSubmit, hideSendButton, insertText, isUploadEnabled, linkPreviews, maxFilesLeft, maxRows, mentionAllAppUsers, mentioned_users, mentionQueryParams, message, minRows, noFiles, numberOfUploads, onPaste, onSelectUser, openCommandsList, openMentionsList, overrideSubmitHandler, parent, publishTypingEvent, recordingController, removeAttachments, setCooldownRemaining, setText, shouldSubmit, showCommandsList, showMentionsList, text, textareaRef, uploadAttachment, uploadNewFiles, upsertAttachments, useMentionsTransliteration, } = value;
4
4
  const editing = message?.editing;
5
- const fileUploadsValue = Object.entries(fileUploads)
6
- // eslint-disable-next-line
7
- .map(([_, value]) => value.state)
8
- .join();
9
- const imageUploadsValue = Object.entries(imageUploads)
10
- // eslint-disable-next-line
11
- .map(([_, value]) => value.state)
12
- .join();
13
5
  const linkPreviewsValue = Array.from(linkPreviews.values()).join();
14
6
  const mentionedUsersLength = mentioned_users.length;
15
7
  const parentId = parent?.id;
@@ -32,16 +24,12 @@ export const useCreateMessageInputContext = (value) => {
32
24
  doImageUploadRequest,
33
25
  emojiSearchIndex,
34
26
  errorHandler,
35
- fileOrder,
36
- fileUploads,
37
27
  findAndEnqueueURLsToEnrich,
38
28
  focus,
39
29
  grow,
40
30
  handleChange,
41
31
  handleSubmit,
42
32
  hideSendButton,
43
- imageOrder,
44
- imageUploads,
45
33
  insertText,
46
34
  isUploadEnabled,
47
35
  linkPreviews,
@@ -63,8 +51,6 @@ export const useCreateMessageInputContext = (value) => {
63
51
  publishTypingEvent,
64
52
  recordingController,
65
53
  removeAttachments,
66
- removeFile,
67
- removeImage,
68
54
  setCooldownRemaining,
69
55
  setText,
70
56
  shouldSubmit,
@@ -73,8 +59,6 @@ export const useCreateMessageInputContext = (value) => {
73
59
  text,
74
60
  textareaRef,
75
61
  uploadAttachment,
76
- uploadFile,
77
- uploadImage,
78
62
  uploadNewFiles,
79
63
  upsertAttachments,
80
64
  useMentionsTransliteration,
@@ -82,6 +66,7 @@ export const useCreateMessageInputContext = (value) => {
82
66
  // eslint-disable-next-line react-hooks/exhaustive-deps
83
67
  [
84
68
  asyncMessagesMultiSendEnabled,
69
+ attachments,
85
70
  audioRecordingEnabled,
86
71
  cancelURLEnrichment,
87
72
  cooldownInterval,
@@ -89,11 +74,9 @@ export const useCreateMessageInputContext = (value) => {
89
74
  dismissLinkPreview,
90
75
  editing,
91
76
  emojiSearchIndex,
92
- fileUploadsValue,
93
77
  findAndEnqueueURLsToEnrich,
94
78
  handleSubmit,
95
79
  hideSendButton,
96
- imageUploadsValue,
97
80
  isUploadEnabled,
98
81
  linkPreviewsValue,
99
82
  mentionedUsersLength,