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.
- package/README.md +10 -0
- package/dist/components/Attachment/components/WaveProgressBar.d.ts +3 -1
- package/dist/components/Attachment/components/WaveProgressBar.js +44 -9
- package/dist/components/Channel/channelState.js +1 -0
- package/dist/components/DateSeparator/DateSeparator.js +1 -1
- package/dist/components/EventComponent/EventComponent.js +1 -1
- package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +9 -3
- package/dist/components/MediaRecorder/classes/MediaRecorderController.d.ts +6 -7
- package/dist/components/MediaRecorder/classes/MediaRecorderController.js +0 -5
- package/dist/components/MediaRecorder/hooks/index.d.ts +1 -1
- package/dist/components/MediaRecorder/hooks/useMediaRecorder.d.ts +1 -2
- package/dist/components/MediaRecorder/hooks/useMediaRecorder.js +1 -1
- package/dist/components/MediaRecorder/index.d.ts +1 -0
- package/dist/components/MediaRecorder/transcode/index.d.ts +6 -5
- package/dist/components/MediaRecorder/transcode/index.js +5 -15
- package/dist/components/Message/MessageSimple.js +1 -1
- package/dist/components/Message/MessageTimestamp.d.ts +0 -1
- package/dist/components/Message/MessageTimestamp.js +0 -1
- package/dist/components/Message/Timestamp.d.ts +0 -1
- package/dist/components/Message/Timestamp.js +2 -3
- package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.js +1 -1
- package/dist/components/Message/utils.js +2 -0
- package/dist/components/MessageInput/AttachmentPreviewList/AttachmentPreviewList.js +23 -27
- package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.d.ts +1 -0
- package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.js +1 -1
- package/dist/components/MessageInput/AttachmentPreviewList/ImageAttachmentPreview.js +2 -1
- package/dist/components/MessageInput/MessageInput.d.ts +4 -6
- package/dist/components/MessageInput/MessageInputFlat.js +4 -7
- package/dist/components/MessageInput/hooks/useAttachments.d.ts +1 -5
- package/dist/components/MessageInput/hooks/useAttachments.js +65 -52
- package/dist/components/MessageInput/hooks/useCreateMessageInputContext.js +2 -19
- package/dist/components/MessageInput/hooks/useMessageInputState.d.ts +2 -35
- package/dist/components/MessageInput/hooks/useMessageInputState.js +2 -107
- package/dist/components/MessageInput/hooks/usePasteHandler.js +1 -3
- package/dist/components/MessageInput/hooks/useSubmitHandler.js +19 -71
- package/dist/components/MessageInput/hooks/utils.d.ts +1 -2
- package/dist/components/MessageInput/icons.d.ts +0 -1
- package/dist/components/MessageInput/icons.js +0 -3
- package/dist/components/MessageInput/types.d.ts +3 -30
- package/dist/components/MessageList/MessageList.d.ts +3 -1
- package/dist/components/MessageList/MessageList.js +2 -1
- package/dist/components/MessageList/VirtualizedMessageList.d.ts +3 -1
- package/dist/components/MessageList/VirtualizedMessageList.js +3 -3
- package/dist/components/MessageList/VirtualizedMessageListComponents.js +3 -2
- package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.d.ts +2 -1
- package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.js +3 -3
- package/dist/components/MessageList/utils.d.ts +1 -1
- package/dist/components/MessageList/utils.js +16 -6
- package/dist/components/ReactFileUtilities/types.d.ts +0 -29
- package/dist/components/ReactFileUtilities/utils.d.ts +2 -0
- package/dist/components/ReactFileUtilities/utils.js +2 -0
- package/dist/context/ChannelActionContext.d.ts +2 -2
- package/dist/context/MessageInputContext.d.ts +1 -5
- package/dist/css/v2/emoji-replacement.css +1 -1
- package/dist/css/v2/index.css +2 -2
- package/dist/css/v2/index.layout.css +2 -2
- package/dist/i18n/Streami18n.d.ts +2 -0
- package/dist/i18n/de.json +3 -1
- package/dist/i18n/en.json +3 -1
- package/dist/i18n/es.json +3 -1
- package/dist/i18n/fr.json +3 -1
- package/dist/i18n/hi.json +3 -1
- package/dist/i18n/it.json +3 -1
- package/dist/i18n/ja.json +3 -1
- package/dist/i18n/ko.json +3 -1
- package/dist/i18n/nl.json +3 -1
- package/dist/i18n/pt.json +3 -1
- package/dist/i18n/ru.json +3 -1
- package/dist/i18n/tr.json +3 -1
- package/dist/i18n/utils.d.ts +3 -3
- package/dist/index.cjs.js +1987 -12143
- package/dist/index.cjs.js.map +4 -4
- package/dist/{components → plugins}/Emojis/EmojiPicker.js +1 -1
- package/dist/plugins/Emojis/icons.d.ts +2 -0
- package/dist/plugins/Emojis/icons.js +4 -0
- package/dist/{components → plugins}/Emojis/index.cjs.js +23 -22
- package/dist/plugins/Emojis/index.cjs.js.map +7 -0
- package/dist/plugins/Emojis/index.d.ts +2 -0
- package/dist/plugins/Emojis/index.js +2 -0
- package/dist/plugins/encoders/mp3.cjs.js +111 -0
- package/dist/plugins/encoders/mp3.cjs.js.map +7 -0
- package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.js +3 -3
- package/dist/scss/v2/Autocomplete/Autocomplete-layout.scss +1 -1
- package/dist/scss/v2/Autocomplete/Autocomplete-theme.scss +4 -2
- package/dist/scss/v2/Avatar/Avatar-layout.scss +31 -23
- package/dist/scss/v2/ChannelList/ChannelList-layout.scss +0 -5
- package/dist/scss/v2/ChannelSearch/ChannelSearch-layout.scss +1 -0
- package/dist/scss/v2/EditMessageForm/EditMessageForm-theme.scss +9 -9
- package/dist/scss/v2/Message/Message-layout.scss +37 -6
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +11 -0
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +5 -0
- package/dist/scss/v2/_emoji-replacement.scss +4 -2
- package/package.json +17 -7
- package/dist/components/Emojis/index.cjs.js.map +0 -7
- package/dist/components/Emojis/index.d.ts +0 -1
- package/dist/components/Emojis/index.js +0 -1
- package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.d.ts +0 -11
- package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.js +0 -51
- package/dist/components/MessageInput/hooks/useFileUploads.d.ts +0 -7
- package/dist/components/MessageInput/hooks/useFileUploads.js +0 -85
- package/dist/components/MessageInput/hooks/useImageUploads.d.ts +0 -8
- package/dist/components/MessageInput/hooks/useImageUploads.js +0 -94
- /package/dist/{components → plugins}/Emojis/EmojiPicker.d.ts +0 -0
- /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:
|
|
21
|
+
file: File;
|
|
23
22
|
getAppSettings: ChatContextValue<StreamChatGenerics>['getAppSettings'];
|
|
24
23
|
t: TranslationContextValue['t'];
|
|
25
24
|
uploadType: 'image' | 'file';
|
|
@@ -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.
|
|
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("
|
|
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}}
|