stream-chat-react 12.10.0 → 12.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Attachment/Attachment.js +2 -1
- package/dist/components/Attachment/AttachmentContainer.d.ts +1 -1
- package/dist/components/Attachment/AttachmentContainer.js +2 -2
- package/dist/components/Attachment/Card.js +3 -3
- package/dist/components/Attachment/VoiceRecording.d.ts +1 -1
- package/dist/components/Attachment/VoiceRecording.js +2 -2
- package/dist/components/Attachment/attachment-sizing.js +2 -1
- package/dist/components/Attachment/components/FileSizeIndicator.d.ts +1 -1
- package/dist/components/Attachment/components/FileSizeIndicator.js +1 -1
- package/dist/components/Attachment/hooks/useAudioController.js +3 -1
- package/dist/components/Attachment/utils.d.ts +1 -1
- package/dist/components/Attachment/utils.js +12 -3
- package/dist/components/AutoCompleteTextarea/List.js +3 -2
- package/dist/components/AutoCompleteTextarea/Textarea.js +3 -3
- package/dist/components/AutoCompleteTextarea/utils.js +2 -2
- package/dist/components/Avatar/ChannelAvatar.js +1 -1
- package/dist/components/Channel/Channel.d.ts +1 -1
- package/dist/components/Channel/Channel.js +27 -12
- package/dist/components/Channel/channelState.js +9 -3
- package/dist/components/Channel/hooks/useCreateChannelStateContext.js +5 -3
- package/dist/components/Channel/hooks/useMentionsHandlers.js +5 -2
- package/dist/components/Channel/utils.js +2 -1
- package/dist/components/ChannelHeader/ChannelHeader.js +1 -1
- package/dist/components/ChannelList/ChannelList.js +13 -8
- package/dist/components/ChannelList/hooks/useChannelListShape.d.ts +2 -2
- package/dist/components/ChannelList/hooks/useChannelListShape.js +44 -17
- package/dist/components/ChannelList/hooks/useChannelUpdatedListener.js +2 -1
- package/dist/components/ChannelList/hooks/useMessageNewListener.js +3 -1
- package/dist/components/ChannelList/hooks/usePaginatedChannels.js +4 -2
- package/dist/components/ChannelList/hooks/useSelectedChannelState.js +1 -1
- package/dist/components/ChannelPreview/ChannelPreview.js +2 -2
- package/dist/components/ChannelPreview/ChannelPreviewMessenger.js +1 -1
- package/dist/components/ChannelPreview/icons.js +0 -1
- package/dist/components/ChannelPreview/utils.js +3 -3
- package/dist/components/ChannelSearch/ChannelSearch.js +5 -3
- package/dist/components/ChannelSearch/SearchResults.js +1 -1
- package/dist/components/ChannelSearch/hooks/useChannelSearch.d.ts +4 -2
- package/dist/components/ChannelSearch/hooks/useChannelSearch.js +61 -36
- package/dist/components/ChannelSearch/index.d.ts +1 -1
- package/dist/components/Chat/hooks/useChat.js +4 -2
- package/dist/components/Chat/hooks/useCreateChatClient.js +3 -1
- package/dist/components/ChatAutoComplete/ChatAutoComplete.js +1 -1
- package/dist/components/ChatView/ChatView.js +0 -1
- package/dist/components/Dialog/PromptDialog.d.ts +1 -1
- package/dist/components/Dialog/PromptDialog.js +1 -1
- package/dist/components/EventComponent/EventComponent.js +1 -1
- package/dist/components/Gallery/Image.js +1 -1
- package/dist/components/Gallery/ModalGallery.js +1 -2
- package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +5 -2
- package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.js +1 -1
- package/dist/components/LoadMore/LoadMorePaginator.js +1 -1
- package/dist/components/MML/MML.js +1 -0
- package/dist/components/MediaRecorder/transcode/wav.js +6 -3
- package/dist/components/Message/FixedHeightMessage.js +6 -8
- package/dist/components/Message/Message.js +2 -1
- package/dist/components/Message/MessageEditedTimestamp.js +1 -1
- package/dist/components/Message/MessageSimple.js +2 -5
- package/dist/components/Message/MessageStatus.js +1 -1
- package/dist/components/Message/MessageText.js +7 -7
- package/dist/components/Message/QuotedMessage.js +2 -2
- package/dist/components/Message/Timestamp.js +9 -1
- package/dist/components/Message/hooks/useActionHandler.js +1 -1
- package/dist/components/Message/hooks/useFlagHandler.js +2 -1
- package/dist/components/Message/hooks/useMarkUnreadHandler.js +2 -1
- package/dist/components/Message/hooks/useMessageTextStreaming.d.ts +1 -1
- package/dist/components/Message/hooks/useMessageTextStreaming.js +1 -1
- package/dist/components/Message/hooks/useMuteHandler.js +7 -4
- package/dist/components/Message/hooks/usePinHandler.js +1 -1
- package/dist/components/Message/hooks/useReactionHandler.js +10 -5
- package/dist/components/Message/hooks/useRetryHandler.js +1 -1
- package/dist/components/Message/hooks/useUserRole.js +1 -1
- package/dist/components/Message/renderText/renderText.js +2 -2
- package/dist/components/Message/utils.js +3 -3
- package/dist/components/MessageActions/MessageActionsBox.js +7 -3
- package/dist/components/MessageInput/AttachmentPreviewList/AttachmentPreviewList.js +1 -1
- package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.js +4 -2
- package/dist/components/MessageInput/AttachmentPreviewList/UnsupportedAttachmentPreview.js +4 -2
- package/dist/components/MessageInput/AttachmentSelector.js +1 -1
- package/dist/components/MessageInput/DefaultTriggerProvider.js +1 -1
- package/dist/components/MessageInput/DropzoneProvider.js +1 -1
- package/dist/components/MessageInput/MessageInput.js +1 -1
- package/dist/components/MessageInput/MessageInputFlat.js +4 -3
- package/dist/components/MessageInput/QuotedMessagePreview.js +1 -1
- package/dist/components/MessageInput/hooks/useAttachments.js +12 -4
- package/dist/components/MessageInput/hooks/useLinkPreviews.js +3 -1
- package/dist/components/MessageInput/hooks/useMessageInputState.js +4 -3
- package/dist/components/MessageInput/hooks/useMessageInputText.js +3 -1
- package/dist/components/MessageInput/hooks/useSubmitHandler.js +5 -3
- package/dist/components/MessageInput/hooks/useUserTrigger.js +2 -2
- package/dist/components/MessageInput/hooks/utils.js +6 -2
- package/dist/components/MessageList/ConnectionStatus.js +1 -1
- package/dist/components/MessageList/MessageList.js +5 -5
- package/dist/components/MessageList/MessageListNotifications.js +3 -1
- package/dist/components/MessageList/VirtualizedMessageList.js +17 -10
- package/dist/components/MessageList/VirtualizedMessageListComponents.js +2 -2
- package/dist/components/MessageList/hooks/MessageList/useMessageListElements.js +1 -3
- package/dist/components/MessageList/hooks/MessageList/useMessageListScrollManager.js +2 -1
- package/dist/components/MessageList/hooks/MessageList/useScrollLocationLogic.js +3 -2
- package/dist/components/MessageList/hooks/MessageList/useUnreadMessagesNotification.js +6 -2
- package/dist/components/MessageList/hooks/VirtualizedMessageList/usePrependMessagesCount.js +2 -1
- package/dist/components/MessageList/hooks/VirtualizedMessageList/useUnreadMessagesNotificationVirtualized.js +7 -3
- package/dist/components/MessageList/utils.js +28 -11
- package/dist/components/Modal/Modal.d.ts +1 -1
- package/dist/components/Modal/Modal.js +1 -1
- package/dist/components/Modal/ModalHeader.js +1 -1
- package/dist/components/Poll/Poll.js +1 -1
- package/dist/components/Poll/PollActions/PollActions.js +6 -4
- package/dist/components/Poll/PollActions/PollAnswerList.js +1 -1
- package/dist/components/Poll/PollActions/PollResults/PollOptionVotesList.js +1 -1
- package/dist/components/Poll/PollActions/PollResults/PollOptionWithLatestVotes.js +4 -2
- package/dist/components/Poll/PollActions/PollResults/PollResults.js +2 -1
- package/dist/components/Poll/PollContent.js +1 -1
- package/dist/components/Poll/PollCreationDialog/OptionFieldSet.d.ts +1 -1
- package/dist/components/Poll/PollCreationDialog/OptionFieldSet.js +11 -4
- package/dist/components/Poll/PollCreationDialog/PollCreationDialog.js +13 -4
- package/dist/components/Poll/PollCreationDialog/PollCreationDialogControls.js +6 -3
- package/dist/components/Poll/PollOptionList.js +1 -1
- package/dist/components/Poll/PollOptionSelector.js +12 -5
- package/dist/components/Poll/hooks/useManagePollVotesRealtime.js +9 -4
- package/dist/components/Poll/hooks/usePollAnswerPagination.js +8 -2
- package/dist/components/Poll/hooks/usePollOptionVotesPagination.js +8 -2
- package/dist/components/ReactFileUtilities/FileIcon/mimeTypes.d.ts +1 -1
- package/dist/components/ReactFileUtilities/ImageDropzone.js +3 -1
- package/dist/components/ReactFileUtilities/UploadButton.js +1 -1
- package/dist/components/ReactFileUtilities/utils.js +3 -1
- package/dist/components/Reactions/ReactionSelector.js +3 -1
- package/dist/components/Reactions/ReactionSelectorWithButton.js +1 -1
- package/dist/components/Reactions/ReactionsList.d.ts +2 -2
- package/dist/components/Reactions/ReactionsList.js +7 -4
- package/dist/components/Reactions/ReactionsListModal.d.ts +1 -2
- package/dist/components/Reactions/ReactionsListModal.js +1 -1
- package/dist/components/Reactions/SpriteImage.js +6 -2
- package/dist/components/Reactions/hooks/useFetchReactions.js +1 -1
- package/dist/components/Reactions/hooks/useProcessReactions.js +1 -1
- package/dist/components/Reactions/index.d.ts +1 -0
- package/dist/components/Reactions/index.js +1 -0
- package/dist/components/Reactions/reactionOptions.js +20 -5
- package/dist/components/Thread/Thread.js +4 -3
- package/dist/components/Threads/ThreadContext.d.ts +1 -1
- package/dist/components/Threads/ThreadContext.js +1 -1
- package/dist/components/Threads/ThreadList/ThreadList.js +1 -1
- package/dist/components/Threads/ThreadList/ThreadListItem.d.ts +1 -1
- package/dist/components/Threads/ThreadList/ThreadListItem.js +1 -1
- package/dist/components/Threads/ThreadList/ThreadListItemUI.js +5 -3
- package/dist/components/Threads/icons.js +0 -1
- package/dist/components/Tooltip/Tooltip.d.ts +1 -1
- package/dist/components/Tooltip/Tooltip.js +1 -1
- package/dist/context/ComponentContext.d.ts +3 -1
- package/dist/context/ComponentContext.js +1 -1
- package/dist/context/DialogManagerContext.d.ts +1 -1
- package/dist/context/DialogManagerContext.js +1 -1
- package/dist/context/MessageBounceContext.js +2 -2
- package/dist/context/MessageContext.js +1 -1
- package/dist/context/WithComponents.js +1 -1
- package/dist/css/v2/index.css +2 -2
- package/dist/css/v2/index.layout.css +2 -2
- package/dist/experimental/MessageActions/MessageActions.js +0 -1
- package/dist/experimental/MessageActions/defaults.js +31 -7
- package/dist/experimental/index.browser.cjs +103 -37
- package/dist/experimental/index.browser.cjs.map +2 -2
- package/dist/experimental/index.node.cjs +103 -37
- package/dist/experimental/index.node.cjs.map +2 -2
- package/dist/i18n/Streami18n.js +11 -2
- package/dist/i18n/utils.js +14 -1
- package/dist/index.browser.cjs +1771 -981
- package/dist/index.browser.cjs.map +4 -4
- package/dist/index.node.cjs +1772 -981
- package/dist/index.node.cjs.map +4 -4
- package/dist/plugins/Emojis/EmojiPicker.js +0 -1
- package/dist/plugins/Emojis/index.browser.cjs +3 -1
- package/dist/plugins/Emojis/index.browser.cjs.map +2 -2
- package/dist/plugins/Emojis/index.node.cjs +3 -1
- package/dist/plugins/Emojis/index.node.cjs.map +2 -2
- package/dist/plugins/encoders/mp3.browser.cjs +7 -4
- package/dist/plugins/encoders/mp3.browser.cjs.map +2 -2
- package/dist/plugins/encoders/mp3.node.cjs +7 -4
- package/dist/plugins/encoders/mp3.node.cjs.map +2 -2
- package/dist/scss/v2/ChannelSearch/ChannelSearch-layout.scss +1 -0
- package/dist/scss/v2/Message/Message-layout.scss +12 -5
- package/dist/scss/v2/Poll/Poll-layout.scss +1 -1
- package/dist/scss/v2/Search/Search-layout.scss +148 -0
- package/dist/scss/v2/Search/Search-theme.scss +222 -0
- package/dist/scss/v2/_icons.scss +2 -0
- package/dist/scss/v2/index.layout.scss +1 -0
- package/dist/scss/v2/index.scss +1 -0
- package/dist/store/hooks/useStateStore.js +35 -11
- package/package.json +21 -33
|
@@ -18,8 +18,8 @@ const selectColor = (number, dark) => {
|
|
|
18
18
|
};
|
|
19
19
|
const hashUserId = (userId) => {
|
|
20
20
|
const hash = userId.split('').reduce((acc, c) => {
|
|
21
|
-
acc = (acc << 5) - acc + c.charCodeAt(0);
|
|
22
|
-
return acc & acc;
|
|
21
|
+
acc = (acc << 5) - acc + c.charCodeAt(0);
|
|
22
|
+
return acc & acc;
|
|
23
23
|
}, 0);
|
|
24
24
|
return Math.abs(hash) / 10 ** Math.ceil(Math.log10(Math.abs(hash) + 1));
|
|
25
25
|
};
|
|
@@ -27,7 +27,7 @@ const getUserColor = (theme, userId) => selectColor(hashUserId(userId), theme.in
|
|
|
27
27
|
const UnMemoizedFixedHeightMessage = (props) => {
|
|
28
28
|
const { groupedByUser: propGroupedByUser, message: propMessage } = props;
|
|
29
29
|
const { theme } = useChatContext('FixedHeightMessage');
|
|
30
|
-
const { groupedByUser: contextGroupedByUser, message: contextMessage
|
|
30
|
+
const { groupedByUser: contextGroupedByUser, message: contextMessage } = useMessageContext('FixedHeightMessage');
|
|
31
31
|
const { MessageDeleted = DefaultMessageDeleted } = useComponentContext('FixedHeightMessage');
|
|
32
32
|
const { userLanguage } = useTranslationContext('FixedHeightMessage');
|
|
33
33
|
const groupedByUser = propGroupedByUser !== undefined ? propGroupedByUser : contextGroupedByUser;
|
|
@@ -35,11 +35,9 @@ const UnMemoizedFixedHeightMessage = (props) => {
|
|
|
35
35
|
const handleAction = useActionHandler(message);
|
|
36
36
|
const handleDelete = useDeleteHandler(message);
|
|
37
37
|
const role = useUserRole(message);
|
|
38
|
-
const messageTextToRender = message?.i18n?.[`${userLanguage}_text`] ||
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
messageTextToRender,
|
|
42
|
-
]);
|
|
38
|
+
const messageTextToRender = message?.i18n?.[`${userLanguage}_text`] ||
|
|
39
|
+
message?.text;
|
|
40
|
+
const renderedText = useMemo(() => renderText(messageTextToRender, message.mentioned_users), [message.mentioned_users, messageTextToRender]);
|
|
43
41
|
const userId = message.user?.id || '';
|
|
44
42
|
const userColor = useMemo(() => getUserColor(theme, userId), [userId, theme]);
|
|
45
43
|
const messageActionsHandler = useCallback(() => getMessageActions(['delete'], { canDelete: role.canDelete }), [role]);
|
|
@@ -21,7 +21,8 @@ const MessageWithContext = (props) => {
|
|
|
21
21
|
read &&
|
|
22
22
|
(!read[client.user.id] ||
|
|
23
23
|
(message?.created_at &&
|
|
24
|
-
new Date(message.created_at).getTime() >
|
|
24
|
+
new Date(message.created_at).getTime() >
|
|
25
|
+
read[client.user.id].last_read.getTime()))), [client, isMyMessage, message.created_at, read]);
|
|
25
26
|
const messageActionsHandler = useCallback(() => getMessageActions(messageActions, {
|
|
26
27
|
canDelete,
|
|
27
28
|
canEdit,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
-
import { useComponentContext, useMessageContext, useTranslationContext } from '../../context';
|
|
3
|
+
import { useComponentContext, useMessageContext, useTranslationContext, } from '../../context';
|
|
4
4
|
import { Timestamp as DefaultTimestamp } from './Timestamp';
|
|
5
5
|
import { isMessageEdited } from './utils';
|
|
6
6
|
export function MessageEditedTimestamp({ message: propMessage, open, ...timestampProps }) {
|
|
@@ -35,10 +35,7 @@ const MessageSimpleWithContext = (props) => {
|
|
|
35
35
|
MessageActions = MessageOptions, MessageDeleted = DefaultMessageDeleted, MessageBouncePrompt = DefaultMessageBouncePrompt, MessageRepliesCountButton = DefaultMessageRepliesCountButton, MessageStatus = DefaultMessageStatus, MessageTimestamp = DefaultMessageTimestamp, ReactionsList = DefaultReactionList, StreamedMessageText = DefaultStreamedMessageText, PinIndicator, } = useComponentContext('MessageSimple');
|
|
36
36
|
const hasAttachment = messageHasAttachments(message);
|
|
37
37
|
const hasReactions = messageHasReactions(message);
|
|
38
|
-
const isAIGenerated = useMemo(() => isMessageAIGenerated?.(message), [
|
|
39
|
-
isMessageAIGenerated,
|
|
40
|
-
message,
|
|
41
|
-
]);
|
|
38
|
+
const isAIGenerated = useMemo(() => isMessageAIGenerated?.(message), [isMessageAIGenerated, message]);
|
|
42
39
|
if (message.customType === CUSTOM_MESSAGE_TYPE.date) {
|
|
43
40
|
return null;
|
|
44
41
|
}
|
|
@@ -98,7 +95,7 @@ const MessageSimpleWithContext = (props) => {
|
|
|
98
95
|
!isMyMessage() && !!message.user && (React.createElement("span", { className: 'str-chat__message-simple-name' }, message.user.name || message.user.id)),
|
|
99
96
|
React.createElement(MessageTimestamp, { customClass: 'str-chat__message-simple-timestamp' }),
|
|
100
97
|
isEdited && (React.createElement("span", { className: 'str-chat__mesage-simple-edited' }, t('Edited'))),
|
|
101
|
-
isEdited && React.createElement(MessageEditedTimestamp, { calendar: true, open: isEditedTimestampOpen }))))));
|
|
98
|
+
isEdited && (React.createElement(MessageEditedTimestamp, { calendar: true, open: isEditedTimestampOpen })))))));
|
|
102
99
|
};
|
|
103
100
|
const MemoizedMessageSimple = React.memo(MessageSimpleWithContext, areMessageUIPropsEqual);
|
|
104
101
|
/**
|
|
@@ -15,7 +15,7 @@ const UnMemoizedMessageStatus = (props) => {
|
|
|
15
15
|
const { handleEnter, handleLeave, tooltipVisible } = useEnterLeaveHandlers();
|
|
16
16
|
const { client } = useChatContext('MessageStatus');
|
|
17
17
|
const { Avatar: contextAvatar } = useComponentContext('MessageStatus');
|
|
18
|
-
const { isMyMessage, lastReceivedId, message, readBy, threadList
|
|
18
|
+
const { isMyMessage, lastReceivedId, message, readBy, threadList } = useMessageContext('MessageStatus');
|
|
19
19
|
const { t } = useTranslationContext('MessageStatus');
|
|
20
20
|
const [referenceElement, setReferenceElement] = useState(null);
|
|
21
21
|
const Avatar = propAvatar || contextAvatar || DefaultAvatar;
|
|
@@ -2,7 +2,7 @@ import clsx from 'clsx';
|
|
|
2
2
|
import React, { useMemo } from 'react';
|
|
3
3
|
import { QuotedMessage as DefaultQuotedMessage } from './QuotedMessage';
|
|
4
4
|
import { isOnlyEmojis, messageHasAttachments } from './utils';
|
|
5
|
-
import { useComponentContext, useMessageContext, useTranslationContext } from '../../context';
|
|
5
|
+
import { useComponentContext, useMessageContext, useTranslationContext, } from '../../context';
|
|
6
6
|
import { renderText as defaultRenderText } from './renderText';
|
|
7
7
|
import { MessageErrorText } from './MessageErrorText';
|
|
8
8
|
const UnMemoizedMessageTextComponent = (props) => {
|
|
@@ -13,14 +13,14 @@ const UnMemoizedMessageTextComponent = (props) => {
|
|
|
13
13
|
const { userLanguage } = useTranslationContext('MessageText');
|
|
14
14
|
const message = propMessage || contextMessage;
|
|
15
15
|
const hasAttachment = messageHasAttachments(message);
|
|
16
|
-
const messageTextToRender = message.i18n?.[`${userLanguage}_text`] ||
|
|
16
|
+
const messageTextToRender = message.i18n?.[`${userLanguage}_text`] ||
|
|
17
|
+
message.text;
|
|
18
|
+
const messageText = useMemo(() => renderText(messageTextToRender, message.mentioned_users),
|
|
17
19
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18
|
-
|
|
19
|
-
message.mentioned_users,
|
|
20
|
-
messageTextToRender,
|
|
21
|
-
]);
|
|
20
|
+
[message.mentioned_users, messageTextToRender]);
|
|
22
21
|
const wrapperClass = customWrapperClass || 'str-chat__message-text';
|
|
23
|
-
const innerClass = customInnerClass ||
|
|
22
|
+
const innerClass = customInnerClass ||
|
|
23
|
+
`str-chat__message-text-inner str-chat__message-${theme}-text-inner`;
|
|
24
24
|
if (!messageTextToRender && !message.quoted_message)
|
|
25
25
|
return null;
|
|
26
26
|
return (React.createElement("div", { className: wrapperClass, tabIndex: 0 },
|
|
@@ -9,7 +9,7 @@ import { useMessageContext } from '../../context/MessageContext';
|
|
|
9
9
|
import { useTranslationContext } from '../../context/TranslationContext';
|
|
10
10
|
import { useChannelActionContext } from '../../context/ChannelActionContext';
|
|
11
11
|
export const QuotedMessage = () => {
|
|
12
|
-
const { Attachment = DefaultAttachment, Avatar: ContextAvatar
|
|
12
|
+
const { Attachment = DefaultAttachment, Avatar: ContextAvatar } = useComponentContext('QuotedMessage');
|
|
13
13
|
const { client } = useChatContext();
|
|
14
14
|
const { isMyMessage, message } = useMessageContext('QuotedMessage');
|
|
15
15
|
const { t, userLanguage } = useTranslationContext('QuotedMessage');
|
|
@@ -39,5 +39,5 @@ export const QuotedMessage = () => {
|
|
|
39
39
|
React.createElement("div", { className: 'str-chat__quoted-message-bubble', "data-testid": 'quoted-message-contents' }, poll ? (React.createElement(Poll, { isQuoted: true, poll: poll })) : (React.createElement(React.Fragment, null,
|
|
40
40
|
quotedMessageAttachment && (React.createElement(Attachment, { attachments: [quotedMessageAttachment], isQuoted: true })),
|
|
41
41
|
React.createElement("div", { className: 'str-chat__quoted-message-bubble__text', "data-testid": 'quoted-message-text' }, quotedMessageText))))),
|
|
42
|
-
message.attachments?.length ? React.createElement(Attachment, { attachments: message.attachments }) : null));
|
|
42
|
+
message.attachments?.length ? (React.createElement(Attachment, { attachments: message.attachments })) : null));
|
|
43
43
|
};
|
|
@@ -16,7 +16,15 @@ export function Timestamp(props) {
|
|
|
16
16
|
t,
|
|
17
17
|
tDateTimeParser,
|
|
18
18
|
timestampTranslationKey: 'timestamp/MessageTimestamp',
|
|
19
|
-
}), [
|
|
19
|
+
}), [
|
|
20
|
+
calendar,
|
|
21
|
+
calendarFormats,
|
|
22
|
+
format,
|
|
23
|
+
formatDate,
|
|
24
|
+
normalizedTimestamp,
|
|
25
|
+
t,
|
|
26
|
+
tDateTimeParser,
|
|
27
|
+
]);
|
|
20
28
|
if (!when) {
|
|
21
29
|
return null;
|
|
22
30
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useChannelActionContext } from '../../../context/ChannelActionContext';
|
|
2
|
-
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
2
|
+
import { useChannelStateContext, } from '../../../context/ChannelStateContext';
|
|
3
3
|
export const handleActionWarning = `Action handler was called, but it is missing one of its required arguments.
|
|
4
4
|
Make sure the ChannelAction and ChannelState contexts are properly set and the hook is initialized with a valid message.`;
|
|
5
5
|
export function useActionHandler(message) {
|
|
@@ -17,7 +17,8 @@ export const useFlagHandler = (message, notifications = {}) => {
|
|
|
17
17
|
}
|
|
18
18
|
try {
|
|
19
19
|
await client.flagMessage(message.id);
|
|
20
|
-
const successMessage = getSuccessNotification &&
|
|
20
|
+
const successMessage = getSuccessNotification &&
|
|
21
|
+
validateAndGetMessage(getSuccessNotification, [message]);
|
|
21
22
|
notify(successMessage || t('Message has been successfully flagged'), 'success');
|
|
22
23
|
}
|
|
23
24
|
catch (e) {
|
|
@@ -14,7 +14,8 @@ export const useMarkUnreadHandler = (message, notifications = {}) => {
|
|
|
14
14
|
await channel.markUnread({ message_id: message.id });
|
|
15
15
|
if (!notify)
|
|
16
16
|
return;
|
|
17
|
-
const successMessage = getSuccessNotification &&
|
|
17
|
+
const successMessage = getSuccessNotification &&
|
|
18
|
+
validateAndGetMessage(getSuccessNotification, [message]);
|
|
18
19
|
if (successMessage)
|
|
19
20
|
notify(successMessage, 'success');
|
|
20
21
|
}
|
|
@@ -11,6 +11,6 @@ export type UseMessageTextStreamingProps<StreamChatGenerics extends DefaultStrea
|
|
|
11
11
|
* @param {string} text - The text that we want to render in a typewriter fashion.
|
|
12
12
|
* @returns {{ streamedMessageText: string }} - A substring of the text property, up until we've finished rendering the typewriter animation.
|
|
13
13
|
*/
|
|
14
|
-
export declare const useMessageTextStreaming: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({
|
|
14
|
+
export declare const useMessageTextStreaming: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ renderingLetterCount, streamingLetterIntervalMs, text, }: UseMessageTextStreamingProps<StreamChatGenerics>) => {
|
|
15
15
|
streamedMessageText: string;
|
|
16
16
|
};
|
|
@@ -9,7 +9,7 @@ const DEFAULT_RENDERING_LETTER_COUNT = 2;
|
|
|
9
9
|
* @param {string} text - The text that we want to render in a typewriter fashion.
|
|
10
10
|
* @returns {{ streamedMessageText: string }} - A substring of the text property, up until we've finished rendering the typewriter animation.
|
|
11
11
|
*/
|
|
12
|
-
export const useMessageTextStreaming = ({
|
|
12
|
+
export const useMessageTextStreaming = ({ renderingLetterCount = DEFAULT_RENDERING_LETTER_COUNT, streamingLetterIntervalMs = DEFAULT_LETTER_INTERVAL, text, }) => {
|
|
13
13
|
const [streamedMessageText, setStreamedMessageText] = useState(text);
|
|
14
14
|
const textCursor = useRef(text.length);
|
|
15
15
|
useEffect(() => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isUserMuted, validateAndGetMessage } from '../utils';
|
|
2
|
-
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
2
|
+
import { useChannelStateContext, } from '../../../context/ChannelStateContext';
|
|
3
3
|
import { useChatContext } from '../../../context/ChatContext';
|
|
4
4
|
import { useTranslationContext } from '../../../context/TranslationContext';
|
|
5
5
|
export const missingUseMuteHandlerParamsWarning = 'useMuteHandler was called but it is missing one or more necessary parameter.';
|
|
@@ -17,14 +17,16 @@ export const useMuteHandler = (message, notifications = {}) => {
|
|
|
17
17
|
if (!isUserMuted(message, mutes)) {
|
|
18
18
|
try {
|
|
19
19
|
await client.muteUser(message.user.id);
|
|
20
|
-
const successMessage = getSuccessNotification &&
|
|
20
|
+
const successMessage = getSuccessNotification &&
|
|
21
|
+
validateAndGetMessage(getSuccessNotification, [message.user]);
|
|
21
22
|
notify(successMessage ||
|
|
22
23
|
t(`{{ user }} has been muted`, {
|
|
23
24
|
user: message.user.name || message.user.id,
|
|
24
25
|
}), 'success');
|
|
25
26
|
}
|
|
26
27
|
catch (e) {
|
|
27
|
-
const errorMessage = getErrorNotification &&
|
|
28
|
+
const errorMessage = getErrorNotification &&
|
|
29
|
+
validateAndGetMessage(getErrorNotification, [message.user]);
|
|
28
30
|
notify(errorMessage || t('Error muting a user ...'), 'error');
|
|
29
31
|
}
|
|
30
32
|
}
|
|
@@ -42,7 +44,8 @@ export const useMuteHandler = (message, notifications = {}) => {
|
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
catch (e) {
|
|
45
|
-
const errorMessage = (getErrorNotification &&
|
|
47
|
+
const errorMessage = (getErrorNotification &&
|
|
48
|
+
validateAndGetMessage(getErrorNotification, [message.user])) ||
|
|
46
49
|
t('Error unmuting a user ...');
|
|
47
50
|
if (typeof errorMessage === 'string') {
|
|
48
51
|
notify(errorMessage, 'error');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defaultPinPermissions, validateAndGetMessage } from '../utils';
|
|
2
2
|
import { useChannelActionContext } from '../../../context/ChannelActionContext';
|
|
3
|
-
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
3
|
+
import { useChannelStateContext, } from '../../../context/ChannelStateContext';
|
|
4
4
|
import { useChatContext } from '../../../context/ChatContext';
|
|
5
5
|
import { useTranslationContext } from '../../../context/TranslationContext';
|
|
6
6
|
export const usePinHandler = (message,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import throttle from 'lodash.throttle';
|
|
3
3
|
import { useChannelActionContext } from '../../../context/ChannelActionContext';
|
|
4
|
-
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
4
|
+
import { useChannelStateContext, } from '../../../context/ChannelStateContext';
|
|
5
5
|
import { useChatContext } from '../../../context/ChatContext';
|
|
6
6
|
import { useThreadContext } from '../../Threads';
|
|
7
7
|
export const reactionHandlerWarning = `Reaction handler was called, but it is missing one of its required arguments.
|
|
@@ -18,7 +18,10 @@ export const useReactionHandler = (message) => {
|
|
|
18
18
|
if (add) {
|
|
19
19
|
const timestamp = new Date().toISOString();
|
|
20
20
|
newReactionGroups[reactionType] = hasReaction
|
|
21
|
-
? {
|
|
21
|
+
? {
|
|
22
|
+
...newReactionGroups[reactionType],
|
|
23
|
+
count: newReactionGroups[reactionType].count + 1,
|
|
24
|
+
}
|
|
22
25
|
: {
|
|
23
26
|
count: 1,
|
|
24
27
|
first_reaction_at: timestamp,
|
|
@@ -66,7 +69,7 @@ export const useReactionHandler = (message) => {
|
|
|
66
69
|
const tempMessage = createMessagePreview(add, newReaction, message);
|
|
67
70
|
try {
|
|
68
71
|
updateMessage(tempMessage);
|
|
69
|
-
// @ts-expect-error
|
|
72
|
+
// @ts-expect-error message type mismatch
|
|
70
73
|
thread?.upsertReplyLocally({ message: tempMessage });
|
|
71
74
|
const messageResponse = add
|
|
72
75
|
? await channel.sendReaction(id, { type })
|
|
@@ -77,7 +80,7 @@ export const useReactionHandler = (message) => {
|
|
|
77
80
|
catch (error) {
|
|
78
81
|
// revert to the original message if the API call fails
|
|
79
82
|
updateMessage(message);
|
|
80
|
-
// @ts-expect-error
|
|
83
|
+
// @ts-expect-error message type mismatch
|
|
81
84
|
thread?.upsertReplyLocally({ message });
|
|
82
85
|
}
|
|
83
86
|
}, 1000);
|
|
@@ -93,7 +96,9 @@ export const useReactionHandler = (message) => {
|
|
|
93
96
|
message.own_reactions.forEach((reaction) => {
|
|
94
97
|
// own user should only ever contain the current user id
|
|
95
98
|
// just in case we check to prevent bugs with message updates from breaking reactions
|
|
96
|
-
if (reaction.user &&
|
|
99
|
+
if (reaction.user &&
|
|
100
|
+
client.userID === reaction.user.id &&
|
|
101
|
+
reaction.type === reactionType) {
|
|
97
102
|
userExistingReaction = reaction;
|
|
98
103
|
}
|
|
99
104
|
else if (reaction.user && client.userID !== reaction.user.id) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useChannelActionContext } from '../../../context/ChannelActionContext';
|
|
1
|
+
import { useChannelActionContext, } from '../../../context/ChannelActionContext';
|
|
2
2
|
export const useRetryHandler = (customRetrySendMessage) => {
|
|
3
3
|
const { retrySendMessage: contextRetrySendMessage } = useChannelActionContext('useRetryHandler');
|
|
4
4
|
const retrySendMessage = customRetrySendMessage || contextRetrySendMessage;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
1
|
+
import { useChannelStateContext, } from '../../../context/ChannelStateContext';
|
|
2
2
|
import { useChatContext } from '../../../context/ChatContext';
|
|
3
3
|
export const useUserRole = (message, onlySenderCanEdit, disableQuotedMessages) => {
|
|
4
4
|
const { channel, channelCapabilities = {} } = useChannelStateContext('useUserRole');
|
|
@@ -50,7 +50,7 @@ function encodeDecode(url) {
|
|
|
50
50
|
return url;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
const urlTransform = (uri) =>
|
|
53
|
+
const urlTransform = (uri) => uri.startsWith('app://') ? uri : uriTransformer(uri);
|
|
54
54
|
const getPluginsForward = (plugins) => plugins;
|
|
55
55
|
export const markDownRenderers = {
|
|
56
56
|
a: Anchor,
|
|
@@ -77,7 +77,7 @@ export const renderText = (text, mentionedUsers, { allowedTagNames = defaultAllo
|
|
|
77
77
|
const strippedText = text?.replace(detectHttp, '');
|
|
78
78
|
if (!strippedHref || !strippedText)
|
|
79
79
|
return false;
|
|
80
|
-
return strippedHref.includes(strippedText) || strippedText.includes(strippedHref);
|
|
80
|
+
return (strippedHref.includes(strippedText) || strippedText.includes(strippedHref));
|
|
81
81
|
});
|
|
82
82
|
if (noParsingNeeded.length > 0 || linkIsInBlock)
|
|
83
83
|
return;
|
|
@@ -9,8 +9,7 @@ export const validateAndGetMessage = (func, args) => {
|
|
|
9
9
|
return null;
|
|
10
10
|
// below is due to tests passing a single argument
|
|
11
11
|
// rather than an array.
|
|
12
|
-
if (!(args
|
|
13
|
-
// @ts-expect-error
|
|
12
|
+
if (!Array.isArray(args)) {
|
|
14
13
|
args = [args];
|
|
15
14
|
}
|
|
16
15
|
const returnValue = func(...args);
|
|
@@ -154,7 +153,8 @@ export const shouldRenderMessageActions = ({ customMessageActions, CustomMessage
|
|
|
154
153
|
if (!messageActions.length)
|
|
155
154
|
return false;
|
|
156
155
|
if (inThread &&
|
|
157
|
-
messageActions.filter((action) => !ACTIONS_NOT_WORKING_IN_THREAD.includes(action))
|
|
156
|
+
messageActions.filter((action) => !ACTIONS_NOT_WORKING_IN_THREAD.includes(action))
|
|
157
|
+
.length === 0) {
|
|
158
158
|
return false;
|
|
159
159
|
}
|
|
160
160
|
if (messageActions.length === 1 &&
|
|
@@ -4,8 +4,10 @@ import { MESSAGE_ACTIONS } from '../Message/utils';
|
|
|
4
4
|
import { useChannelActionContext, useComponentContext, useMessageContext, useTranslationContext, } from '../../context';
|
|
5
5
|
import { CustomMessageActionsList as DefaultCustomMessageActionsList } from './CustomMessageActionsList';
|
|
6
6
|
const UnMemoizedMessageActionsBox = (props) => {
|
|
7
|
-
const { className, getMessageActions, handleDelete, handleEdit, handleFlag, handleMarkUnread, handleMute, handlePin, isUserMuted,
|
|
8
|
-
|
|
7
|
+
const { className, getMessageActions, handleDelete, handleEdit, handleFlag, handleMarkUnread, handleMute, handlePin, isUserMuted,
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
9
|
+
mine, open, ...restDivProps } = props;
|
|
10
|
+
const { CustomMessageActionsList = DefaultCustomMessageActionsList } = useComponentContext('MessageActionsBox');
|
|
9
11
|
const { setQuotedMessage } = useChannelActionContext('MessageActionsBox');
|
|
10
12
|
const { customMessageActions, message, threadList } = useMessageContext('MessageActionsBox');
|
|
11
13
|
const { t } = useTranslationContext('MessageActionsBox');
|
|
@@ -29,7 +31,9 @@ const UnMemoizedMessageActionsBox = (props) => {
|
|
|
29
31
|
React.createElement(CustomMessageActionsList, { customMessageActions: customMessageActions, message: message }),
|
|
30
32
|
messageActions.indexOf(MESSAGE_ACTIONS.quote) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleQuote, role: 'option' }, t('Reply'))),
|
|
31
33
|
messageActions.indexOf(MESSAGE_ACTIONS.pin) > -1 && !message.parent_id && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handlePin, role: 'option' }, !message.pinned ? t('Pin') : t('Unpin'))),
|
|
32
|
-
messageActions.indexOf(MESSAGE_ACTIONS.markUnread) > -1 &&
|
|
34
|
+
messageActions.indexOf(MESSAGE_ACTIONS.markUnread) > -1 &&
|
|
35
|
+
!threadList &&
|
|
36
|
+
!!message.id && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleMarkUnread, role: 'option' }, t('Mark as unread'))),
|
|
33
37
|
messageActions.indexOf(MESSAGE_ACTIONS.flag) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleFlag, role: 'option' }, t('Flag'))),
|
|
34
38
|
messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleMute, role: 'option' }, isUserMuted() ? t('Unmute') : t('Mute'))),
|
|
35
39
|
messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleEdit, role: 'option' }, t('Edit Message'))),
|
|
@@ -6,7 +6,7 @@ import { ImageAttachmentPreview as DefaultImagePreview, } from './ImageAttachmen
|
|
|
6
6
|
import { isLocalAttachment, isLocalAudioAttachment, isLocalFileAttachment, isLocalImageAttachment, isLocalMediaAttachment, isLocalVoiceRecordingAttachment, isScrapedContent, } from '../../Attachment';
|
|
7
7
|
import { useMessageInputContext } from '../../../context';
|
|
8
8
|
export const AttachmentPreviewList = ({ AudioAttachmentPreview = DefaultFilePreview, FileAttachmentPreview = DefaultFilePreview, ImageAttachmentPreview = DefaultImagePreview, UnsupportedAttachmentPreview = DefaultUnknownAttachmentPreview, VideoAttachmentPreview = DefaultFilePreview, VoiceRecordingPreview = DefaultVoiceRecordingPreview, }) => {
|
|
9
|
-
const { attachments, removeAttachments, uploadAttachment
|
|
9
|
+
const { attachments, removeAttachments, uploadAttachment } = useMessageInputContext('AttachmentPreviewList');
|
|
10
10
|
return (React.createElement("div", { className: 'str-chat__attachment-preview-list' },
|
|
11
11
|
React.createElement("div", { className: 'str-chat__attachment-list-scroll-container', "data-testid": 'attachment-list-scroll-container' }, attachments.map((attachment) => {
|
|
12
12
|
if (isScrapedContent(attachment))
|
|
@@ -7,13 +7,15 @@ export const FileAttachmentPreview = ({ attachment, handleRetry, removeAttachmen
|
|
|
7
7
|
return (React.createElement("div", { className: 'str-chat__attachment-preview-file', "data-testid": 'attachment-preview-file' },
|
|
8
8
|
React.createElement("div", { className: 'str-chat__attachment-preview-file-icon' },
|
|
9
9
|
React.createElement(FileIcon, { filename: attachment.title, mimeType: attachment.mime_type })),
|
|
10
|
-
React.createElement("button", { "aria-label": t('aria/Remove attachment'), className: 'str-chat__attachment-preview-delete', "data-testid": 'file-preview-item-delete-button', disabled: attachment.localMetadata?.uploadState === 'uploading', onClick: () => attachment.localMetadata?.id &&
|
|
10
|
+
React.createElement("button", { "aria-label": t('aria/Remove attachment'), className: 'str-chat__attachment-preview-delete', "data-testid": 'file-preview-item-delete-button', disabled: attachment.localMetadata?.uploadState === 'uploading', onClick: () => attachment.localMetadata?.id &&
|
|
11
|
+
removeAttachments([attachment.localMetadata?.id]) },
|
|
11
12
|
React.createElement(CloseIcon, null)),
|
|
12
13
|
attachment.localMetadata?.uploadState === 'failed' && !!handleRetry && (React.createElement("button", { className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-file', "data-testid": 'file-preview-item-retry-button', onClick: () => handleRetry(attachment) },
|
|
13
14
|
React.createElement(RetryIcon, null))),
|
|
14
15
|
React.createElement("div", { className: 'str-chat__attachment-preview-file-end' },
|
|
15
16
|
React.createElement("div", { className: 'str-chat__attachment-preview-file-name', title: attachment.title }, attachment.title),
|
|
16
|
-
attachment.localMetadata?.uploadState === 'finished' &&
|
|
17
|
+
attachment.localMetadata?.uploadState === 'finished' &&
|
|
18
|
+
!!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
19
|
React.createElement(DownloadIcon, null))),
|
|
18
20
|
attachment.localMetadata?.uploadState === 'uploading' && (React.createElement(LoadingIndicatorIcon, { size: 17 })))));
|
|
19
21
|
};
|
|
@@ -8,13 +8,15 @@ export const UnsupportedAttachmentPreview = ({ attachment, handleRetry, removeAt
|
|
|
8
8
|
return (React.createElement("div", { className: 'str-chat__attachment-preview-unsupported', "data-testid": 'attachment-preview-unknown' },
|
|
9
9
|
React.createElement("div", { className: 'str-chat__attachment-preview-file-icon' },
|
|
10
10
|
React.createElement(FileIcon, { filename: title, mimeType: attachment.mime_type })),
|
|
11
|
-
React.createElement("button", { className: 'str-chat__attachment-preview-delete', "data-testid": 'file-preview-item-delete-button', disabled: attachment.localMetadata?.uploadState === 'uploading', onClick: () => attachment.localMetadata?.id &&
|
|
11
|
+
React.createElement("button", { className: 'str-chat__attachment-preview-delete', "data-testid": 'file-preview-item-delete-button', disabled: attachment.localMetadata?.uploadState === 'uploading', onClick: () => attachment.localMetadata?.id &&
|
|
12
|
+
removeAttachments([attachment.localMetadata?.id]) },
|
|
12
13
|
React.createElement(CloseIcon, null)),
|
|
13
14
|
attachment.localMetadata?.uploadState === 'failed' && !!handleRetry && (React.createElement("button", { className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-file', "data-testid": 'file-preview-item-retry-button', onClick: () => handleRetry(attachment) },
|
|
14
15
|
React.createElement(RetryIcon, null))),
|
|
15
16
|
React.createElement("div", { className: 'str-chat__attachment-preview-metadata' },
|
|
16
17
|
React.createElement("div", { className: 'str-chat__attachment-preview-title', title: title }, title),
|
|
17
|
-
attachment.localMetadata?.uploadState === 'finished' &&
|
|
18
|
+
attachment.localMetadata?.uploadState === 'finished' &&
|
|
19
|
+
!!attachment.asset_url && (React.createElement("a", { className: 'str-chat__attachment-preview-file-download', download: true, href: attachment.asset_url, rel: 'noreferrer', target: '_blank' },
|
|
18
20
|
React.createElement(DownloadIcon, null))),
|
|
19
21
|
attachment.localMetadata?.uploadState === 'uploading' && (React.createElement(LoadingIndicatorIcon, { size: 17 })))));
|
|
20
22
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
3
|
import { UploadIcon as DefaultUploadIcon } from './icons';
|
|
4
4
|
import { CHANNEL_CONTAINER_ID } from '../Channel/constants';
|
|
5
5
|
import { DialogAnchor, useDialog, useDialogIsOpen } from '../Dialog';
|
|
@@ -20,5 +20,5 @@ export const DefaultTriggerProvider = ({ children, }) => {
|
|
|
20
20
|
...currentValue,
|
|
21
21
|
autocompleteTriggers: defaultAutocompleteTriggers,
|
|
22
22
|
};
|
|
23
|
-
return React.createElement(MessageInputContextProvider, { value: newValue }, children);
|
|
23
|
+
return (React.createElement(MessageInputContextProvider, { value: newValue }, children));
|
|
24
24
|
};
|
|
@@ -7,7 +7,7 @@ import { useChannelStateContext } from '../../context/ChannelStateContext';
|
|
|
7
7
|
import { MessageInputContextProvider, useMessageInputContext, } from '../../context/MessageInputContext';
|
|
8
8
|
const DropzoneInner = ({ children, }) => {
|
|
9
9
|
const { acceptedFiles, multipleUploads } = useChannelStateContext('DropzoneProvider');
|
|
10
|
-
const { cooldownRemaining, isUploadEnabled, maxFilesLeft, uploadNewFiles
|
|
10
|
+
const { cooldownRemaining, isUploadEnabled, maxFilesLeft, uploadNewFiles } = useMessageInputContext('DropzoneProvider');
|
|
11
11
|
return (React.createElement(ImageDropzone, { accept: acceptedFiles, disabled: !isUploadEnabled || maxFilesLeft === 0 || !!cooldownRemaining, handleFiles: uploadNewFiles, maxNumberOfFiles: maxFilesLeft, multiple: multipleUploads }, children));
|
|
12
12
|
};
|
|
13
13
|
export const DropzoneProvider = (props) => {
|
|
@@ -5,7 +5,7 @@ import { useCooldownTimer } from './hooks/useCooldownTimer';
|
|
|
5
5
|
import { useCreateMessageInputContext } from './hooks/useCreateMessageInputContext';
|
|
6
6
|
import { useMessageInputState } from './hooks/useMessageInputState';
|
|
7
7
|
import { useChannelStateContext } from '../../context/ChannelStateContext';
|
|
8
|
-
import { useComponentContext } from '../../context/ComponentContext';
|
|
8
|
+
import { useComponentContext, } from '../../context/ComponentContext';
|
|
9
9
|
import { MessageInputContextProvider } from '../../context/MessageInputContext';
|
|
10
10
|
import { DialogManagerProvider } from '../../context';
|
|
11
11
|
const MessageInputProvider = (props) => {
|
|
@@ -21,7 +21,7 @@ import { AIStates, useAIState } from '../AIStateIndicator';
|
|
|
21
21
|
export const MessageInputFlat = () => {
|
|
22
22
|
const { t } = useTranslationContext('MessageInputFlat');
|
|
23
23
|
const { asyncMessagesMultiSendEnabled, attachments, cooldownRemaining, findAndEnqueueURLsToEnrich, handleSubmit, hideSendButton, isUploadEnabled, linkPreviews, maxFilesLeft, message, numberOfUploads, parent, recordingController, setCooldownRemaining, text, uploadNewFiles, } = useMessageInputContext('MessageInputFlat');
|
|
24
|
-
const {
|
|
24
|
+
const { AttachmentPreviewList = DefaultAttachmentPreviewList, AttachmentSelector = message ? SimpleAttachmentSelector : DefaultAttachmentSelector, AudioRecorder = DefaultAudioRecorder, CooldownTimer = DefaultCooldownTimer, EmojiPicker, LinkPreviewList = DefaultLinkPreviewList, QuotedMessagePreview = DefaultQuotedMessagePreview, RecordingPermissionDeniedNotification = DefaultRecordingPermissionDeniedNotification, SendButton = DefaultSendButton, StartRecordingAudioButton = DefaultStartRecordingAudioButton, StopAIGenerationButton: StopAIGenerationButtonOverride, } = useComponentContext('MessageInputFlat');
|
|
25
25
|
const { acceptedFiles = [], multipleUploads, quotedMessage, } = useChannelStateContext('MessageInputFlat');
|
|
26
26
|
const { setQuotedMessage } = useChannelActionContext('MessageInputFlat');
|
|
27
27
|
const { channel } = useChatContext('MessageInputFlat');
|
|
@@ -76,7 +76,8 @@ export const MessageInputFlat = () => {
|
|
|
76
76
|
const StopAIGenerationButton = StopAIGenerationButtonOverride === undefined
|
|
77
77
|
? DefaultStopAIGenerationButton
|
|
78
78
|
: StopAIGenerationButtonOverride;
|
|
79
|
-
const shouldDisplayStopAIGeneration = [AIStates.Thinking, AIStates.Generating].includes(aiState) &&
|
|
79
|
+
const shouldDisplayStopAIGeneration = [AIStates.Thinking, AIStates.Generating].includes(aiState) &&
|
|
80
|
+
!!StopAIGenerationButton;
|
|
80
81
|
return (React.createElement(React.Fragment, null,
|
|
81
82
|
React.createElement("div", { ...getRootProps({ className: 'str-chat__message-input' }) },
|
|
82
83
|
recordingEnabled &&
|
|
@@ -92,7 +93,7 @@ export const MessageInputFlat = () => {
|
|
|
92
93
|
React.createElement("div", { className: 'str-chat__message-input-inner' },
|
|
93
94
|
React.createElement(AttachmentSelector, null),
|
|
94
95
|
React.createElement("div", { className: 'str-chat__message-textarea-container' },
|
|
95
|
-
displayQuotedMessage && React.createElement(QuotedMessagePreview, { quotedMessage: quotedMessage }),
|
|
96
|
+
displayQuotedMessage && (React.createElement(QuotedMessagePreview, { quotedMessage: quotedMessage })),
|
|
96
97
|
isUploadEnabled &&
|
|
97
98
|
!!(numberOfUploads + failedUploadsCount || attachments.length > 0) && (React.createElement(AttachmentPreviewList, null)),
|
|
98
99
|
React.createElement("div", { className: 'str-chat__message-textarea-with-emoji-picker' },
|
|
@@ -17,7 +17,7 @@ export const QuotedMessagePreviewHeader = () => {
|
|
|
17
17
|
};
|
|
18
18
|
export const QuotedMessagePreview = ({ quotedMessage, }) => {
|
|
19
19
|
const { client } = useChatContext();
|
|
20
|
-
const { Attachment = DefaultAttachment, Avatar = DefaultAvatar
|
|
20
|
+
const { Attachment = DefaultAttachment, Avatar = DefaultAvatar } = useComponentContext('QuotedMessagePreview');
|
|
21
21
|
const { userLanguage } = useTranslationContext('QuotedMessagePreview');
|
|
22
22
|
const quotedMessageText = quotedMessage.i18n?.[`${userLanguage}_text`] ||
|
|
23
23
|
quotedMessage.text;
|
|
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
|
|
|
2
2
|
import { nanoid } from 'nanoid';
|
|
3
3
|
import { checkUploadPermissions } from './utils';
|
|
4
4
|
import { isLocalAttachment, isLocalImageAttachment } from '../../Attachment';
|
|
5
|
-
import { createFileFromBlobs, generateFileName, isBlobButNotFile } from '../../ReactFileUtilities';
|
|
5
|
+
import { createFileFromBlobs, generateFileName, isBlobButNotFile, } from '../../ReactFileUtilities';
|
|
6
6
|
import { useChannelActionContext, useChannelStateContext, useChatContext, useTranslationContext, } from '../../../context';
|
|
7
7
|
const apiMaxNumberOfFiles = 10;
|
|
8
8
|
// const isAudioFile = (file: FileLike) => file.type.includes('audio/');
|
|
@@ -109,7 +109,10 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
catch (error) {
|
|
112
|
-
let finalError = {
|
|
112
|
+
let finalError = {
|
|
113
|
+
message: t('Error uploading attachment'),
|
|
114
|
+
name: 'Error',
|
|
115
|
+
};
|
|
113
116
|
if (typeof error.message === 'string') {
|
|
114
117
|
finalError = error;
|
|
115
118
|
}
|
|
@@ -127,7 +130,10 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
|
|
|
127
130
|
};
|
|
128
131
|
upsertAttachments([failedAttachment]);
|
|
129
132
|
if (errorHandler) {
|
|
130
|
-
errorHandler(finalError, 'upload-attachment', {
|
|
133
|
+
errorHandler(finalError, 'upload-attachment', {
|
|
134
|
+
...file,
|
|
135
|
+
id: localMetadata.id,
|
|
136
|
+
});
|
|
131
137
|
}
|
|
132
138
|
return failedAttachment;
|
|
133
139
|
}
|
|
@@ -173,7 +179,9 @@ export const useAttachments = (props, state, dispatch, textareaRef) => {
|
|
|
173
179
|
upsertAttachments,
|
|
174
180
|
]);
|
|
175
181
|
const uploadNewFiles = useCallback((files) => {
|
|
176
|
-
const filesToBeUploaded = noFiles
|
|
182
|
+
const filesToBeUploaded = noFiles
|
|
183
|
+
? Array.from(files).filter(isImageFile)
|
|
184
|
+
: Array.from(files);
|
|
177
185
|
filesToBeUploaded.slice(0, maxFilesLeft).forEach((fileLike) => {
|
|
178
186
|
uploadAttachment({
|
|
179
187
|
localMetadata: {
|
|
@@ -102,6 +102,8 @@ export const useLinkPreviews = ({ debounceURLEnrichmentMs: debounceURLEnrichment
|
|
|
102
102
|
return {
|
|
103
103
|
cancelURLEnrichment,
|
|
104
104
|
dismissLinkPreview,
|
|
105
|
-
findAndEnqueueURLsToEnrich: channelConfig?.url_enrichment && enrichURLForPreview
|
|
105
|
+
findAndEnqueueURLsToEnrich: channelConfig?.url_enrichment && enrichURLForPreview
|
|
106
|
+
? findAndEnqueueURLsToEnrich
|
|
107
|
+
: undefined,
|
|
106
108
|
};
|
|
107
109
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useCallback, useReducer, useState } from 'react';
|
|
2
2
|
import { nanoid } from 'nanoid';
|
|
3
|
-
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
3
|
+
import { useChannelStateContext, } from '../../../context/ChannelStateContext';
|
|
4
4
|
import { useAttachments } from './useAttachments';
|
|
5
5
|
import { useLinkPreviews } from './useLinkPreviews';
|
|
6
6
|
import { useMessageInputText } from './useMessageInputText';
|
|
7
7
|
import { useSubmitHandler } from './useSubmitHandler';
|
|
8
8
|
import { usePasteHandler } from './usePasteHandler';
|
|
9
|
-
import { useMediaRecorder } from '../../MediaRecorder/hooks/useMediaRecorder';
|
|
9
|
+
import { useMediaRecorder, } from '../../MediaRecorder/hooks/useMediaRecorder';
|
|
10
10
|
import { LinkPreviewState, SetLinkPreviewMode } from '../types';
|
|
11
11
|
import { mergeDeep } from '../../../utils/mergeDeep';
|
|
12
12
|
const makeEmptyMessageInputState = () => ({
|
|
@@ -59,7 +59,8 @@ const messageInputReducer = (state, action) => {
|
|
|
59
59
|
case 'upsertAttachments': {
|
|
60
60
|
const attachments = [...state.attachments];
|
|
61
61
|
action.attachments.forEach((actionAttachment) => {
|
|
62
|
-
const attachmentIndex = state.attachments.findIndex((att) => att.localMetadata?.id &&
|
|
62
|
+
const attachmentIndex = state.attachments.findIndex((att) => att.localMetadata?.id &&
|
|
63
|
+
att.localMetadata?.id === actionAttachment.localMetadata?.id);
|
|
63
64
|
if (attachmentIndex === -1) {
|
|
64
65
|
attachments.push(actionAttachment);
|
|
65
66
|
}
|
|
@@ -32,7 +32,9 @@ export const useMessageInputText = (props, state, dispatch, findAndEnqueueURLsTo
|
|
|
32
32
|
newCursorPosition.current = selectionStart + textToInsert.length;
|
|
33
33
|
dispatch({
|
|
34
34
|
getNewText: (prevText) => {
|
|
35
|
-
const updatedText = prevText.slice(0, selectionStart) +
|
|
35
|
+
const updatedText = prevText.slice(0, selectionStart) +
|
|
36
|
+
textToInsert +
|
|
37
|
+
prevText.slice(selectionEnd);
|
|
36
38
|
if (maxLength && updatedText.length > maxLength) {
|
|
37
39
|
return updatedText.slice(0, maxLength);
|
|
38
40
|
}
|