stream-chat-react-native-core 5.43.3-beta.1 → 5.44.0-beta.1
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/lib/commonjs/components/AITypingIndicatorView/AITypingIndicatorView.js +53 -0
- package/lib/commonjs/components/AITypingIndicatorView/AITypingIndicatorView.js.map +1 -0
- package/lib/commonjs/components/AITypingIndicatorView/hooks/useAIState.js +59 -0
- package/lib/commonjs/components/AITypingIndicatorView/hooks/useAIState.js.map +1 -0
- package/lib/commonjs/components/AITypingIndicatorView/index.js +26 -0
- package/lib/commonjs/components/AITypingIndicatorView/index.js.map +1 -0
- package/lib/commonjs/components/Attachment/AudioAttachment.js +27 -22
- package/lib/commonjs/components/Attachment/AudioAttachment.js.map +1 -1
- package/lib/commonjs/components/Channel/Channel.js +9 -1
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useCreateInputMessageInputContext.js +2 -0
- package/lib/commonjs/components/Channel/hooks/useCreateInputMessageInputContext.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js +2 -0
- package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
- package/lib/commonjs/components/Message/Message.js +3 -1
- package/lib/commonjs/components/Message/Message.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/MessageContent.js +8 -3
- package/lib/commonjs/components/Message/MessageSimple/MessageContent.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/MessageFooter.js +5 -4
- package/lib/commonjs/components/Message/MessageSimple/MessageFooter.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/MessageSimple.js +1 -1
- package/lib/commonjs/components/Message/MessageSimple/MessageSimple.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/StreamingMessageView.js +36 -0
- package/lib/commonjs/components/Message/MessageSimple/StreamingMessageView.js.map +1 -0
- package/lib/commonjs/components/Message/MessageSimple/utils/generateMarkdownText.js +5 -1
- package/lib/commonjs/components/Message/MessageSimple/utils/generateMarkdownText.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/utils/renderText.js +209 -23
- package/lib/commonjs/components/Message/MessageSimple/utils/renderText.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useStreamingMessage.js +43 -0
- package/lib/commonjs/components/Message/hooks/useStreamingMessage.js.map +1 -0
- package/lib/commonjs/components/MessageInput/MessageInput.js +20 -6
- package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/commonjs/components/MessageInput/StopMessageStreamingButton.js +39 -0
- package/lib/commonjs/components/MessageInput/StopMessageStreamingButton.js.map +1 -0
- package/lib/commonjs/components/MessageOverlay/MessageOverlay.js +6 -1
- package/lib/commonjs/components/MessageOverlay/MessageOverlay.js.map +1 -1
- package/lib/commonjs/components/index.js +44 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +4 -2
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +2 -0
- package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
- package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js +7 -0
- package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/commonjs/i18n/en.json +2 -0
- package/lib/commonjs/i18n/es.json +2 -0
- package/lib/commonjs/i18n/fr.json +2 -0
- package/lib/commonjs/i18n/he.json +2 -0
- package/lib/commonjs/i18n/hi.json +2 -0
- package/lib/commonjs/i18n/it.json +2 -0
- package/lib/commonjs/i18n/ja.json +2 -0
- package/lib/commonjs/i18n/ko.json +2 -0
- package/lib/commonjs/i18n/nl.json +2 -0
- package/lib/commonjs/i18n/pt-br.json +2 -0
- package/lib/commonjs/i18n/ru.json +2 -0
- package/lib/commonjs/i18n/tr.json +2 -0
- package/lib/commonjs/utils/getTrimmedAttachmentTitle.js +8 -2
- package/lib/commonjs/utils/getTrimmedAttachmentTitle.js.map +1 -1
- package/lib/commonjs/utils/utils.js +1 -1
- package/lib/commonjs/utils/utils.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/AITypingIndicatorView/AITypingIndicatorView.js +53 -0
- package/lib/module/components/AITypingIndicatorView/AITypingIndicatorView.js.map +1 -0
- package/lib/module/components/AITypingIndicatorView/hooks/useAIState.js +59 -0
- package/lib/module/components/AITypingIndicatorView/hooks/useAIState.js.map +1 -0
- package/lib/module/components/AITypingIndicatorView/index.js +26 -0
- package/lib/module/components/AITypingIndicatorView/index.js.map +1 -0
- package/lib/module/components/Attachment/AudioAttachment.js +27 -22
- package/lib/module/components/Attachment/AudioAttachment.js.map +1 -1
- package/lib/module/components/Channel/Channel.js +9 -1
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/hooks/useCreateInputMessageInputContext.js +2 -0
- package/lib/module/components/Channel/hooks/useCreateInputMessageInputContext.js.map +1 -1
- package/lib/module/components/Channel/hooks/useCreateMessagesContext.js +2 -0
- package/lib/module/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
- package/lib/module/components/Message/Message.js +3 -1
- package/lib/module/components/Message/Message.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/MessageContent.js +8 -3
- package/lib/module/components/Message/MessageSimple/MessageContent.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/MessageFooter.js +5 -4
- package/lib/module/components/Message/MessageSimple/MessageFooter.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/MessageSimple.js +1 -1
- package/lib/module/components/Message/MessageSimple/MessageSimple.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/StreamingMessageView.js +36 -0
- package/lib/module/components/Message/MessageSimple/StreamingMessageView.js.map +1 -0
- package/lib/module/components/Message/MessageSimple/utils/generateMarkdownText.js +5 -1
- package/lib/module/components/Message/MessageSimple/utils/generateMarkdownText.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/utils/renderText.js +209 -23
- package/lib/module/components/Message/MessageSimple/utils/renderText.js.map +1 -1
- package/lib/module/components/Message/hooks/useStreamingMessage.js +43 -0
- package/lib/module/components/Message/hooks/useStreamingMessage.js.map +1 -0
- package/lib/module/components/MessageInput/MessageInput.js +20 -6
- package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/module/components/MessageInput/StopMessageStreamingButton.js +39 -0
- package/lib/module/components/MessageInput/StopMessageStreamingButton.js.map +1 -0
- package/lib/module/components/MessageOverlay/MessageOverlay.js +6 -1
- package/lib/module/components/MessageOverlay/MessageOverlay.js.map +1 -1
- package/lib/module/components/index.js +44 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/contexts/messageInputContext/MessageInputContext.js +4 -2
- package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +2 -0
- package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
- package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/module/contexts/themeContext/utils/theme.js +7 -0
- package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/module/i18n/en.json +2 -0
- package/lib/module/i18n/es.json +2 -0
- package/lib/module/i18n/fr.json +2 -0
- package/lib/module/i18n/he.json +2 -0
- package/lib/module/i18n/hi.json +2 -0
- package/lib/module/i18n/it.json +2 -0
- package/lib/module/i18n/ja.json +2 -0
- package/lib/module/i18n/ko.json +2 -0
- package/lib/module/i18n/nl.json +2 -0
- package/lib/module/i18n/pt-br.json +2 -0
- package/lib/module/i18n/ru.json +2 -0
- package/lib/module/i18n/tr.json +2 -0
- package/lib/module/utils/getTrimmedAttachmentTitle.js +8 -2
- package/lib/module/utils/getTrimmedAttachmentTitle.js.map +1 -1
- package/lib/module/utils/utils.js +1 -1
- package/lib/module/utils/utils.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/AITypingIndicatorView/AITypingIndicatorView.d.ts +11 -0
- package/lib/typescript/components/AITypingIndicatorView/AITypingIndicatorView.d.ts.map +1 -0
- package/lib/typescript/components/AITypingIndicatorView/hooks/useAIState.d.ts +18 -0
- package/lib/typescript/components/AITypingIndicatorView/hooks/useAIState.d.ts.map +1 -0
- package/lib/typescript/components/AITypingIndicatorView/index.d.ts +3 -0
- package/lib/typescript/components/AITypingIndicatorView/index.d.ts.map +1 -0
- package/lib/typescript/components/Attachment/AudioAttachment.d.ts.map +1 -1
- package/lib/typescript/components/Channel/Channel.d.ts +2 -2
- package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useCreateInputMessageInputContext.d.ts +1 -1
- package/lib/typescript/components/Channel/hooks/useCreateInputMessageInputContext.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts +1 -1
- package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts.map +1 -1
- package/lib/typescript/components/Message/Message.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageSimple/MessageContent.d.ts +1 -1
- package/lib/typescript/components/Message/MessageSimple/MessageContent.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageSimple/MessageFooter.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageSimple/MessageSimple.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageSimple/StreamingMessageView.d.ts +12 -0
- package/lib/typescript/components/Message/MessageSimple/StreamingMessageView.d.ts.map +1 -0
- package/lib/typescript/components/Message/MessageSimple/utils/generateMarkdownText.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageSimple/utils/renderText.d.ts +16 -1
- package/lib/typescript/components/Message/MessageSimple/utils/renderText.d.ts.map +1 -1
- package/lib/typescript/components/Message/hooks/useStreamingMessage.d.ts +17 -0
- package/lib/typescript/components/Message/hooks/useStreamingMessage.d.ts.map +1 -0
- package/lib/typescript/components/MessageInput/MessageInput.d.ts +1 -1
- package/lib/typescript/components/MessageInput/MessageInput.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/StopMessageStreamingButton.d.ts +10 -0
- package/lib/typescript/components/MessageInput/StopMessageStreamingButton.d.ts.map +1 -0
- package/lib/typescript/components/MessageOverlay/MessageOverlay.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +4 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +3 -2
- package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messageInputContext/hooks/useCreateMessageInputContext.d.ts +1 -1
- package/lib/typescript/contexts/messageInputContext/hooks/useCreateMessageInputContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +6 -2
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts.map +1 -1
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts +8 -1
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
- package/lib/typescript/i18n/en.json +2 -0
- package/lib/typescript/i18n/es.json +2 -0
- package/lib/typescript/i18n/fr.json +2 -0
- package/lib/typescript/i18n/he.json +2 -0
- package/lib/typescript/i18n/hi.json +2 -0
- package/lib/typescript/i18n/it.json +2 -0
- package/lib/typescript/i18n/ja.json +2 -0
- package/lib/typescript/i18n/ko.json +2 -0
- package/lib/typescript/i18n/nl.json +2 -0
- package/lib/typescript/i18n/pt-br.json +2 -0
- package/lib/typescript/i18n/ru.json +2 -0
- package/lib/typescript/i18n/tr.json +2 -0
- package/lib/typescript/utils/getTrimmedAttachmentTitle.d.ts.map +1 -1
- package/lib/typescript/utils/i18n/Streami18n.d.ts +2 -0
- package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
- package/lib/typescript/utils/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/AITypingIndicatorView/AITypingIndicatorView.tsx +50 -0
- package/src/components/AITypingIndicatorView/hooks/useAIState.ts +68 -0
- package/src/components/AITypingIndicatorView/index.ts +2 -0
- package/src/components/Attachment/AudioAttachment.tsx +20 -19
- package/src/components/Channel/Channel.tsx +26 -2
- package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts +2 -0
- package/src/components/Channel/hooks/useCreateMessagesContext.ts +2 -0
- package/src/components/Message/Message.tsx +4 -1
- package/src/components/Message/MessageSimple/MessageContent.tsx +14 -2
- package/src/components/Message/MessageSimple/MessageFooter.tsx +6 -4
- package/src/components/Message/MessageSimple/MessageSimple.tsx +3 -1
- package/src/components/Message/MessageSimple/StreamingMessageView.tsx +34 -0
- package/src/components/Message/MessageSimple/utils/generateMarkdownText.ts +9 -1
- package/src/components/Message/MessageSimple/utils/renderText.tsx +207 -3
- package/src/components/Message/hooks/useStreamingMessage.ts +54 -0
- package/src/components/MessageInput/MessageInput.tsx +19 -3
- package/src/components/MessageInput/StopMessageStreamingButton.tsx +34 -0
- package/src/components/MessageOverlay/MessageOverlay.tsx +10 -1
- package/src/components/index.ts +5 -0
- package/src/contexts/messageInputContext/MessageInputContext.tsx +5 -1
- package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts +2 -0
- package/src/contexts/messagesContext/MessagesContext.tsx +6 -1
- package/src/contexts/themeContext/utils/theme.ts +14 -1
- package/src/i18n/en.json +2 -0
- package/src/i18n/es.json +2 -0
- package/src/i18n/fr.json +2 -0
- package/src/i18n/he.json +2 -0
- package/src/i18n/hi.json +2 -0
- package/src/i18n/it.json +2 -0
- package/src/i18n/ja.json +2 -0
- package/src/i18n/ko.json +2 -0
- package/src/i18n/nl.json +2 -0
- package/src/i18n/pt-br.json +2 -0
- package/src/i18n/ru.json +2 -0
- package/src/i18n/tr.json +2 -0
- package/src/utils/getTrimmedAttachmentTitle.ts +10 -2
- package/src/utils/utils.ts +1 -1
- package/src/version.json +1 -1
|
@@ -135,6 +135,7 @@ import { MessageSimple as MessageSimpleDefault } from '../Message/MessageSimple/
|
|
|
135
135
|
import { MessageStatus as MessageStatusDefault } from '../Message/MessageSimple/MessageStatus';
|
|
136
136
|
import { MessageTimestamp as MessageTimestampDefault } from '../Message/MessageSimple/MessageTimestamp';
|
|
137
137
|
import { ReactionList as ReactionListDefault } from '../Message/MessageSimple/ReactionList';
|
|
138
|
+
import { StreamingMessageView as DefaultStreamingMessageView } from '../Message/MessageSimple/StreamingMessageView';
|
|
138
139
|
import { AttachButton as AttachButtonDefault } from '../MessageInput/AttachButton';
|
|
139
140
|
import { CommandsButton as CommandsButtonDefault } from '../MessageInput/CommandsButton';
|
|
140
141
|
import { AudioRecorder as AudioRecorderDefault } from '../MessageInput/components/AudioRecorder/AudioRecorder';
|
|
@@ -154,6 +155,7 @@ import { MoreOptionsButton as MoreOptionsButtonDefault } from '../MessageInput/M
|
|
|
154
155
|
import { SendButton as SendButtonDefault } from '../MessageInput/SendButton';
|
|
155
156
|
import { SendMessageDisallowedIndicator as SendMessageDisallowedIndicatorDefault } from '../MessageInput/SendMessageDisallowedIndicator';
|
|
156
157
|
import { ShowThreadMessageInChannelButton as ShowThreadMessageInChannelButtonDefault } from '../MessageInput/ShowThreadMessageInChannelButton';
|
|
158
|
+
import { StopMessageStreamingButton as DefaultStopMessageStreamingButton } from '../MessageInput/StopMessageStreamingButton';
|
|
157
159
|
import { UploadProgressIndicator as UploadProgressIndicatorDefault } from '../MessageInput/UploadProgressIndicator';
|
|
158
160
|
import { DateHeader as DateHeaderDefault } from '../MessageList/DateHeader';
|
|
159
161
|
import type { MessageType } from '../MessageList/hooks/useMessageList';
|
|
@@ -333,6 +335,7 @@ export type ChannelPropsWithContext<
|
|
|
333
335
|
| 'VideoThumbnail'
|
|
334
336
|
| 'PollContent'
|
|
335
337
|
| 'hasCreatePoll'
|
|
338
|
+
| 'StreamingMessageView'
|
|
336
339
|
>
|
|
337
340
|
> &
|
|
338
341
|
Partial<Pick<ThreadContextValue<StreamChatGenerics>, 'allowThreadMessagesInChannel'>> & {
|
|
@@ -420,7 +423,12 @@ export type ChannelPropsWithContext<
|
|
|
420
423
|
* Tells if channel is rendering a thread list
|
|
421
424
|
*/
|
|
422
425
|
threadList?: boolean;
|
|
423
|
-
} & Partial<
|
|
426
|
+
} & Partial<
|
|
427
|
+
Pick<
|
|
428
|
+
InputMessageInputContextValue,
|
|
429
|
+
'openPollCreationDialog' | 'CreatePollContent' | 'StopMessageStreamingButton'
|
|
430
|
+
>
|
|
431
|
+
>;
|
|
424
432
|
|
|
425
433
|
const ChannelWithContext = <
|
|
426
434
|
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
@@ -544,7 +552,15 @@ const ChannelWithContext = <
|
|
|
544
552
|
MessageAvatar = MessageAvatarDefault,
|
|
545
553
|
MessageBounce = MessageBounceDefault,
|
|
546
554
|
MessageContent = MessageContentDefault,
|
|
547
|
-
messageContentOrder = [
|
|
555
|
+
messageContentOrder = [
|
|
556
|
+
'quoted_reply',
|
|
557
|
+
'gallery',
|
|
558
|
+
'files',
|
|
559
|
+
'poll',
|
|
560
|
+
'ai_text',
|
|
561
|
+
'text',
|
|
562
|
+
'attachments',
|
|
563
|
+
],
|
|
548
564
|
MessageDeleted = MessageDeletedDefault,
|
|
549
565
|
MessageEditedTimestamp = MessageEditedTimestampDefault,
|
|
550
566
|
MessageError = MessageErrorDefault,
|
|
@@ -596,6 +612,8 @@ const ChannelWithContext = <
|
|
|
596
612
|
StartAudioRecordingButton = AudioRecordingButtonDefault,
|
|
597
613
|
stateUpdateThrottleInterval = defaultThrottleInterval,
|
|
598
614
|
StickyHeader = StickyHeaderDefault,
|
|
615
|
+
StopMessageStreamingButton: StopMessageStreamingButtonOverride,
|
|
616
|
+
StreamingMessageView = DefaultStreamingMessageView,
|
|
599
617
|
supportedReactions = reactionData,
|
|
600
618
|
t,
|
|
601
619
|
thread: threadFromProps,
|
|
@@ -612,6 +630,10 @@ const ChannelWithContext = <
|
|
|
612
630
|
} = props;
|
|
613
631
|
|
|
614
632
|
const { thread: threadProps, threadInstance } = threadFromProps;
|
|
633
|
+
const StopMessageStreamingButton =
|
|
634
|
+
StopMessageStreamingButtonOverride === undefined
|
|
635
|
+
? DefaultStopMessageStreamingButton
|
|
636
|
+
: StopMessageStreamingButtonOverride;
|
|
615
637
|
|
|
616
638
|
const {
|
|
617
639
|
theme: {
|
|
@@ -2338,6 +2360,7 @@ const ChannelWithContext = <
|
|
|
2338
2360
|
setQuotedMessageState,
|
|
2339
2361
|
ShowThreadMessageInChannelButton,
|
|
2340
2362
|
StartAudioRecordingButton,
|
|
2363
|
+
StopMessageStreamingButton,
|
|
2341
2364
|
UploadProgressIndicator,
|
|
2342
2365
|
});
|
|
2343
2366
|
|
|
@@ -2439,6 +2462,7 @@ const ChannelWithContext = <
|
|
|
2439
2462
|
setEditingState,
|
|
2440
2463
|
setQuotedMessageState,
|
|
2441
2464
|
shouldShowUnreadUnderlay,
|
|
2465
|
+
StreamingMessageView,
|
|
2442
2466
|
supportedReactions,
|
|
2443
2467
|
targetedMessage,
|
|
2444
2468
|
TypingIndicator,
|
|
@@ -64,6 +64,7 @@ export const useCreateInputMessageInputContext = <
|
|
|
64
64
|
showPollCreationDialog,
|
|
65
65
|
ShowThreadMessageInChannelButton,
|
|
66
66
|
StartAudioRecordingButton,
|
|
67
|
+
StopMessageStreamingButton,
|
|
67
68
|
UploadProgressIndicator,
|
|
68
69
|
}: InputMessageInputContextValue<StreamChatGenerics> & {
|
|
69
70
|
/**
|
|
@@ -137,6 +138,7 @@ export const useCreateInputMessageInputContext = <
|
|
|
137
138
|
showPollCreationDialog,
|
|
138
139
|
ShowThreadMessageInChannelButton,
|
|
139
140
|
StartAudioRecordingButton,
|
|
141
|
+
StopMessageStreamingButton,
|
|
140
142
|
UploadProgressIndicator,
|
|
141
143
|
}),
|
|
142
144
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -88,6 +88,7 @@ export const useCreateMessagesContext = <
|
|
|
88
88
|
setEditingState,
|
|
89
89
|
setQuotedMessageState,
|
|
90
90
|
shouldShowUnreadUnderlay,
|
|
91
|
+
StreamingMessageView,
|
|
91
92
|
supportedReactions,
|
|
92
93
|
targetedMessage,
|
|
93
94
|
TypingIndicator,
|
|
@@ -189,6 +190,7 @@ export const useCreateMessagesContext = <
|
|
|
189
190
|
setEditingState,
|
|
190
191
|
setQuotedMessageState,
|
|
191
192
|
shouldShowUnreadUnderlay,
|
|
193
|
+
StreamingMessageView,
|
|
192
194
|
supportedReactions,
|
|
193
195
|
targetedMessage,
|
|
194
196
|
TypingIndicator,
|
|
@@ -456,6 +456,8 @@ const MessageWithContext = <
|
|
|
456
456
|
return !!attachments.images.length || !!attachments.videos.length;
|
|
457
457
|
case 'poll':
|
|
458
458
|
return !!message.poll_id;
|
|
459
|
+
case 'ai_text':
|
|
460
|
+
return !!message.ai_generated;
|
|
459
461
|
case 'text':
|
|
460
462
|
default:
|
|
461
463
|
return !!message.text;
|
|
@@ -863,7 +865,8 @@ const areEqual = <StreamChatGenerics extends DefaultStreamChatGenerics = Default
|
|
|
863
865
|
prevMessage.text === nextMessage.text &&
|
|
864
866
|
prevMessage.pinned === nextMessage.pinned &&
|
|
865
867
|
`${prevMessage?.updated_at}` === `${nextMessage?.updated_at}` &&
|
|
866
|
-
prevMessage.i18n === nextMessage.i18n
|
|
868
|
+
prevMessage.i18n === nextMessage.i18n &&
|
|
869
|
+
prevMessage.ai_generated === nextMessage.ai_generated;
|
|
867
870
|
|
|
868
871
|
if (!messageEqual) return false;
|
|
869
872
|
|
|
@@ -100,6 +100,7 @@ export type MessageContentPropsWithContext<
|
|
|
100
100
|
| 'myMessageTheme'
|
|
101
101
|
| 'onPressInMessage'
|
|
102
102
|
| 'Reply'
|
|
103
|
+
| 'StreamingMessageView'
|
|
103
104
|
> &
|
|
104
105
|
Pick<TranslationContextValue, 't'> & {
|
|
105
106
|
setMessageContentWidth: React.Dispatch<React.SetStateAction<number>>;
|
|
@@ -142,6 +143,7 @@ const MessageContentWithContext = <
|
|
|
142
143
|
Reply,
|
|
143
144
|
setMessageContentWidth,
|
|
144
145
|
showMessageStatus,
|
|
146
|
+
StreamingMessageView,
|
|
145
147
|
threadList,
|
|
146
148
|
} = props;
|
|
147
149
|
const { client } = useChatContext();
|
|
@@ -393,9 +395,16 @@ const MessageContentWithContext = <
|
|
|
393
395
|
/>
|
|
394
396
|
) : null;
|
|
395
397
|
}
|
|
398
|
+
case 'ai_text':
|
|
399
|
+
return message.ai_generated ? (
|
|
400
|
+
<StreamingMessageView
|
|
401
|
+
key={`ai_message_text_container_${messageContentOrderIndex}`}
|
|
402
|
+
/>
|
|
403
|
+
) : null;
|
|
396
404
|
case 'text':
|
|
397
405
|
default:
|
|
398
|
-
return otherAttachments.length && otherAttachments[0].actions
|
|
406
|
+
return (otherAttachments.length && otherAttachments[0].actions) ||
|
|
407
|
+
message.ai_generated ? null : (
|
|
399
408
|
<MessageTextContainer<StreamChatGenerics>
|
|
400
409
|
key={`message_text_container_${messageContentOrderIndex}`}
|
|
401
410
|
/>
|
|
@@ -484,7 +493,8 @@ const areEqual = <StreamChatGenerics extends DefaultStreamChatGenerics = Default
|
|
|
484
493
|
prevMessage.type === nextMessage.type &&
|
|
485
494
|
prevMessage.text === nextMessage.text &&
|
|
486
495
|
prevMessage.pinned === nextMessage.pinned &&
|
|
487
|
-
prevMessage.i18n === nextMessage.i18n
|
|
496
|
+
prevMessage.i18n === nextMessage.i18n &&
|
|
497
|
+
prevMessage.ai_generated === nextMessage.ai_generated;
|
|
488
498
|
if (!messageEqual) return false;
|
|
489
499
|
|
|
490
500
|
const isPrevQuotedMessageTypeDeleted = prevMessage.quoted_message?.type === 'deleted';
|
|
@@ -597,6 +607,7 @@ export const MessageContent = <
|
|
|
597
607
|
MessageStatus,
|
|
598
608
|
myMessageTheme,
|
|
599
609
|
Reply,
|
|
610
|
+
StreamingMessageView,
|
|
600
611
|
} = useMessagesContext<StreamChatGenerics>();
|
|
601
612
|
const { t } = useTranslationContext();
|
|
602
613
|
|
|
@@ -635,6 +646,7 @@ export const MessageContent = <
|
|
|
635
646
|
preventPress,
|
|
636
647
|
Reply,
|
|
637
648
|
showMessageStatus,
|
|
649
|
+
StreamingMessageView,
|
|
638
650
|
t,
|
|
639
651
|
threadList,
|
|
640
652
|
}}
|
|
@@ -129,6 +129,8 @@ const MessageFooterWithContext = <
|
|
|
129
129
|
return null;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
const isEdited = isEditedMessage(message);
|
|
133
|
+
|
|
132
134
|
return (
|
|
133
135
|
<>
|
|
134
136
|
<View style={[styles.container, metaContainer]} testID='message-status-time'>
|
|
@@ -141,7 +143,7 @@ const MessageFooterWithContext = <
|
|
|
141
143
|
{showMessageStatus && <MessageStatus />}
|
|
142
144
|
<MessageTimestamp formattedDate={formattedDate} timestamp={date} />
|
|
143
145
|
|
|
144
|
-
{
|
|
146
|
+
{isEdited && !isEditedMessageOpen ? (
|
|
145
147
|
<>
|
|
146
148
|
<Text
|
|
147
149
|
style={[
|
|
@@ -159,11 +161,11 @@ const MessageFooterWithContext = <
|
|
|
159
161
|
{t<string>('Edited')}
|
|
160
162
|
</Text>
|
|
161
163
|
</>
|
|
162
|
-
)}
|
|
164
|
+
) : null}
|
|
163
165
|
</View>
|
|
164
|
-
{
|
|
166
|
+
{isEdited && isEditedMessageOpen ? (
|
|
165
167
|
<MessageEditedTimestamp message={message} MessageTimestamp={MessageTimestamp} />
|
|
166
|
-
)}
|
|
168
|
+
) : null}
|
|
167
169
|
</>
|
|
168
170
|
);
|
|
169
171
|
};
|
|
@@ -154,7 +154,9 @@ const areEqual = <StreamChatGenerics extends DefaultStreamChatGenerics = Default
|
|
|
154
154
|
prevMessage.status === nextMessage.status &&
|
|
155
155
|
prevMessage.type === nextMessage.type &&
|
|
156
156
|
prevMessage.text === nextMessage.text &&
|
|
157
|
-
prevMessage.i18n === nextMessage.i18n
|
|
157
|
+
prevMessage.i18n === nextMessage.i18n &&
|
|
158
|
+
prevMessage.pinned === nextMessage.pinned &&
|
|
159
|
+
prevMessage.ai_generated === nextMessage.ai_generated;
|
|
158
160
|
if (!messageEqual) return false;
|
|
159
161
|
|
|
160
162
|
const isPrevQuotedMessageTypeDeleted = prevMessage.quoted_message?.type === 'deleted';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { MessageTextContainer, MessageTextContainerProps } from './MessageTextContainer';
|
|
4
|
+
|
|
5
|
+
import { useMessageContext } from '../../../contexts';
|
|
6
|
+
import type { DefaultStreamChatGenerics } from '../../../types/types';
|
|
7
|
+
import { useStreamingMessage } from '../hooks/useStreamingMessage';
|
|
8
|
+
|
|
9
|
+
export type StreamingMessageViewProps<
|
|
10
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
11
|
+
> = Pick<MessageTextContainerProps<StreamChatGenerics>, 'message'> & {
|
|
12
|
+
letterInterval?: number;
|
|
13
|
+
renderingLetterCount?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const StreamingMessageView = <
|
|
17
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
18
|
+
>(
|
|
19
|
+
props: StreamingMessageViewProps<StreamChatGenerics>,
|
|
20
|
+
) => {
|
|
21
|
+
const { letterInterval, message: messageFromProps, renderingLetterCount } = props;
|
|
22
|
+
const { message: messageFromContext } = useMessageContext<StreamChatGenerics>();
|
|
23
|
+
const message = messageFromProps || messageFromContext;
|
|
24
|
+
const { text = '' } = message;
|
|
25
|
+
const { streamedMessageText } = useStreamingMessage({
|
|
26
|
+
letterInterval,
|
|
27
|
+
renderingLetterCount,
|
|
28
|
+
text,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return <MessageTextContainer message={{ ...message, text: streamedMessageText }} />;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
StreamingMessageView.displayName = 'StreamingMessageView{messageSimple{content}}';
|
|
@@ -33,7 +33,11 @@ export const generateMarkdownText = (text?: string) => {
|
|
|
33
33
|
resultText = resultText.replace(mentionsRegex, `@${displayLink}`);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// Escape the " and ' characters, except in code blocks where we deem this allowed.
|
|
37
|
+
resultText = resultText.replace(/(```[\s\S]*?```|`.*?`)|[<"'>]/g, (match, code) => {
|
|
38
|
+
if (code) return code;
|
|
39
|
+
return `\\${match}`;
|
|
40
|
+
});
|
|
37
41
|
|
|
38
42
|
// Remove whitespaces that come directly after newlines except in code blocks where we deem this allowed.
|
|
39
43
|
resultText = resultText.replace(/(```[\s\S]*?```|`.*?`)|\n[ ]{2,}/g, (_, code) => {
|
|
@@ -41,5 +45,9 @@ export const generateMarkdownText = (text?: string) => {
|
|
|
41
45
|
return '\n';
|
|
42
46
|
});
|
|
43
47
|
|
|
48
|
+
// Always replace \n``` with \n\n``` to force the markdown state machine to treat it as a separate block. Otherwise, code blocks inside of list
|
|
49
|
+
// items for example were broken. We clean up the code block closing state within the rendering itself.
|
|
50
|
+
resultText = resultText.replace(/\n```/g, '\n\n```');
|
|
51
|
+
|
|
44
52
|
return resultText;
|
|
45
53
|
};
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import React, { PropsWithChildren } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import React, { PropsWithChildren, ReactNode, useCallback, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
GestureResponderEvent,
|
|
4
|
+
Linking,
|
|
5
|
+
Platform,
|
|
6
|
+
Text,
|
|
7
|
+
TextProps,
|
|
8
|
+
View,
|
|
9
|
+
ViewProps,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
|
|
12
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
4
13
|
// @ts-expect-error
|
|
5
14
|
import Markdown from 'react-native-markdown-package';
|
|
15
|
+
import Animated, { clamp, scrollTo, useAnimatedRef, useSharedValue } from 'react-native-reanimated';
|
|
6
16
|
|
|
7
17
|
import {
|
|
8
18
|
DefaultRules,
|
|
@@ -26,7 +36,64 @@ import type { DefaultStreamChatGenerics } from '../../../../types/types';
|
|
|
26
36
|
import { escapeRegExp } from '../../../../utils/utils';
|
|
27
37
|
import type { MessageType } from '../../../MessageList/hooks/useMessageList';
|
|
28
38
|
|
|
39
|
+
export const MarkdownReactiveScrollView = ({ children }: { children: ReactNode }) => {
|
|
40
|
+
const scrollViewRef = useAnimatedRef<Animated.ScrollView>();
|
|
41
|
+
const contentWidth = useSharedValue(0);
|
|
42
|
+
const visibleContentWidth = useSharedValue(0);
|
|
43
|
+
const offsetBeforeScroll = useSharedValue(0);
|
|
44
|
+
|
|
45
|
+
const panGesture = Gesture.Pan()
|
|
46
|
+
.activeOffsetX([-5, 5])
|
|
47
|
+
.onUpdate((event) => {
|
|
48
|
+
const { translationX } = event;
|
|
49
|
+
|
|
50
|
+
scrollTo(scrollViewRef, offsetBeforeScroll.value - translationX, 0, false);
|
|
51
|
+
})
|
|
52
|
+
.onEnd((event) => {
|
|
53
|
+
const { translationX } = event;
|
|
54
|
+
|
|
55
|
+
const velocityEffect = event.velocityX * 0.3;
|
|
56
|
+
|
|
57
|
+
const finalPosition = clamp(
|
|
58
|
+
offsetBeforeScroll.value - translationX - velocityEffect,
|
|
59
|
+
0,
|
|
60
|
+
contentWidth.value - visibleContentWidth.value,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
offsetBeforeScroll.value = finalPosition;
|
|
64
|
+
|
|
65
|
+
scrollTo(scrollViewRef, finalPosition, 0, true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<View style={{ width: '100%' }}>
|
|
70
|
+
<GestureDetector gesture={panGesture}>
|
|
71
|
+
<Animated.ScrollView
|
|
72
|
+
contentContainerStyle={{ flexGrow: 1 }}
|
|
73
|
+
horizontal
|
|
74
|
+
nestedScrollEnabled={true}
|
|
75
|
+
onContentSizeChange={(width) => {
|
|
76
|
+
contentWidth.value = width;
|
|
77
|
+
}}
|
|
78
|
+
onLayout={(e) => {
|
|
79
|
+
visibleContentWidth.value = e.nativeEvent.layout.width;
|
|
80
|
+
}}
|
|
81
|
+
ref={scrollViewRef}
|
|
82
|
+
scrollEnabled={false}
|
|
83
|
+
>
|
|
84
|
+
{children}
|
|
85
|
+
</Animated.ScrollView>
|
|
86
|
+
</GestureDetector>
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
29
91
|
const defaultMarkdownStyles: MarkdownStyle = {
|
|
92
|
+
codeBlock: {
|
|
93
|
+
fontFamily: Platform.OS === 'ios' ? 'Courier' : 'Monospace',
|
|
94
|
+
fontWeight: '500',
|
|
95
|
+
marginVertical: 8,
|
|
96
|
+
},
|
|
30
97
|
inlineCode: {
|
|
31
98
|
fontSize: 13,
|
|
32
99
|
padding: 3,
|
|
@@ -60,6 +127,26 @@ const defaultMarkdownStyles: MarkdownStyle = {
|
|
|
60
127
|
marginBottom: 8,
|
|
61
128
|
marginTop: 8,
|
|
62
129
|
},
|
|
130
|
+
table: {
|
|
131
|
+
borderRadius: 3,
|
|
132
|
+
borderWidth: 1,
|
|
133
|
+
flex: 1,
|
|
134
|
+
flexDirection: 'row',
|
|
135
|
+
},
|
|
136
|
+
tableHeader: {
|
|
137
|
+
flexDirection: 'row',
|
|
138
|
+
justifyContent: 'space-around',
|
|
139
|
+
},
|
|
140
|
+
tableHeaderCell: {
|
|
141
|
+
fontWeight: '500',
|
|
142
|
+
},
|
|
143
|
+
tableRow: {
|
|
144
|
+
alignItems: 'center',
|
|
145
|
+
justifyContent: 'space-around',
|
|
146
|
+
},
|
|
147
|
+
tableRowCell: {
|
|
148
|
+
flex: 1,
|
|
149
|
+
},
|
|
63
150
|
};
|
|
64
151
|
|
|
65
152
|
const mentionsParseFunction: ParseFunction = (capture, parse, state) => ({
|
|
@@ -113,6 +200,13 @@ export const renderText = <
|
|
|
113
200
|
color: colors.accent_blue,
|
|
114
201
|
...markdownStyles?.autolink,
|
|
115
202
|
},
|
|
203
|
+
codeBlock: {
|
|
204
|
+
...defaultMarkdownStyles.codeBlock,
|
|
205
|
+
backgroundColor: colors.code_block,
|
|
206
|
+
color: colors.black,
|
|
207
|
+
padding: 8,
|
|
208
|
+
...markdownStyles?.codeBlock,
|
|
209
|
+
},
|
|
116
210
|
inlineCode: {
|
|
117
211
|
...defaultMarkdownStyles.inlineCode,
|
|
118
212
|
backgroundColor: colors.white_smoke,
|
|
@@ -125,6 +219,35 @@ export const renderText = <
|
|
|
125
219
|
color: colors.accent_blue,
|
|
126
220
|
...markdownStyles?.mentions,
|
|
127
221
|
},
|
|
222
|
+
table: {
|
|
223
|
+
...defaultMarkdownStyles.table,
|
|
224
|
+
borderColor: colors.grey_dark,
|
|
225
|
+
marginVertical: 8,
|
|
226
|
+
...markdownStyles?.table,
|
|
227
|
+
},
|
|
228
|
+
tableHeader: {
|
|
229
|
+
...defaultMarkdownStyles.tableHeader,
|
|
230
|
+
backgroundColor: colors.grey,
|
|
231
|
+
...markdownStyles?.tableHeader,
|
|
232
|
+
},
|
|
233
|
+
tableHeaderCell: {
|
|
234
|
+
...defaultMarkdownStyles.tableHeaderCell,
|
|
235
|
+
padding: 5,
|
|
236
|
+
...markdownStyles?.tableHeaderCell,
|
|
237
|
+
},
|
|
238
|
+
tableRow: {
|
|
239
|
+
...defaultMarkdownStyles.tableRow,
|
|
240
|
+
...markdownStyles?.tableRow,
|
|
241
|
+
},
|
|
242
|
+
tableRowCell: {
|
|
243
|
+
...defaultMarkdownStyles.tableRowCell,
|
|
244
|
+
borderColor: colors.grey_dark,
|
|
245
|
+
padding: 5,
|
|
246
|
+
...markdownStyles?.tableRowCell,
|
|
247
|
+
},
|
|
248
|
+
tableRowLast: {
|
|
249
|
+
...markdownStyles?.tableRowLast,
|
|
250
|
+
},
|
|
128
251
|
text: {
|
|
129
252
|
...defaultMarkdownStyles.text,
|
|
130
253
|
color: colors.black,
|
|
@@ -263,6 +386,18 @@ export const renderText = <
|
|
|
263
386
|
/>
|
|
264
387
|
);
|
|
265
388
|
|
|
389
|
+
const codeBlockReact: ReactNodeOutput = (node, _, state) => (
|
|
390
|
+
<MarkdownReactiveScrollView key={state.key}>
|
|
391
|
+
<Text style={styles.codeBlock}>{node?.content?.trim()}</Text>
|
|
392
|
+
</MarkdownReactiveScrollView>
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
const tableReact: ReactNodeOutput = (node, output, state) => (
|
|
396
|
+
<MarkdownReactiveScrollView key={state.key}>
|
|
397
|
+
<MarkdownTable node={node} output={output} state={state} styles={styles} />
|
|
398
|
+
</MarkdownReactiveScrollView>
|
|
399
|
+
);
|
|
400
|
+
|
|
266
401
|
const customRules = {
|
|
267
402
|
// do not render images, we will scrape them out of the message and show on attachment card component
|
|
268
403
|
image: { match: () => null },
|
|
@@ -283,6 +418,8 @@ export const renderText = <
|
|
|
283
418
|
},
|
|
284
419
|
}
|
|
285
420
|
: {}),
|
|
421
|
+
codeBlock: { react: codeBlockReact },
|
|
422
|
+
table: { react: tableReact },
|
|
286
423
|
};
|
|
287
424
|
|
|
288
425
|
return (
|
|
@@ -373,3 +510,70 @@ const ListRow = ({ children, style }: PropsWithChildren<ViewProps>) => (
|
|
|
373
510
|
const ListItem = ({ children, style }: PropsWithChildren<TextProps>) => (
|
|
374
511
|
<Text style={style}>{children}</Text>
|
|
375
512
|
);
|
|
513
|
+
|
|
514
|
+
export type MarkdownTableProps = {
|
|
515
|
+
node: SingleASTNode;
|
|
516
|
+
output: ReactOutput;
|
|
517
|
+
state: State;
|
|
518
|
+
styles: Partial<MarkdownStyle>;
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
const transpose = (matrix: SingleASTNode[][]) =>
|
|
522
|
+
matrix[0].map((_, colIndex) => matrix.map((row) => row[colIndex]));
|
|
523
|
+
|
|
524
|
+
const MarkdownTable = ({ node, output, state, styles }: MarkdownTableProps) => {
|
|
525
|
+
const content = useMemo(() => {
|
|
526
|
+
const nodeContent = [node?.header, ...node?.cells];
|
|
527
|
+
return transpose(nodeContent);
|
|
528
|
+
}, [node?.cells, node?.header]);
|
|
529
|
+
const columns = content?.map((column, idx) => (
|
|
530
|
+
<MarkdownTableColumn
|
|
531
|
+
items={column}
|
|
532
|
+
key={`column-${idx}`}
|
|
533
|
+
output={output}
|
|
534
|
+
state={state}
|
|
535
|
+
styles={styles}
|
|
536
|
+
/>
|
|
537
|
+
));
|
|
538
|
+
|
|
539
|
+
return (
|
|
540
|
+
<View key={state.key} style={styles.table}>
|
|
541
|
+
{columns}
|
|
542
|
+
</View>
|
|
543
|
+
);
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
export type MarkdownTableRowProps = {
|
|
547
|
+
items: SingleASTNode[];
|
|
548
|
+
output: ReactOutput;
|
|
549
|
+
state: State;
|
|
550
|
+
styles: Partial<MarkdownStyle>;
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
const MarkdownTableColumn = ({ items, output, state, styles }: MarkdownTableRowProps) => {
|
|
554
|
+
const [headerCellContent, ...columnCellContents] = items;
|
|
555
|
+
|
|
556
|
+
const ColumnCell = useCallback(
|
|
557
|
+
({ content }: { content: SingleASTNode }) =>
|
|
558
|
+
content ? (
|
|
559
|
+
<View style={styles.tableRow}>
|
|
560
|
+
<View style={styles.tableRowCell}>{output(content, state)}</View>
|
|
561
|
+
</View>
|
|
562
|
+
) : null,
|
|
563
|
+
[output, state, styles],
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
return (
|
|
567
|
+
<View style={{ flex: 1, flexDirection: 'column' }}>
|
|
568
|
+
{headerCellContent ? (
|
|
569
|
+
<View key={-1} style={styles.tableHeader}>
|
|
570
|
+
<Text style={styles.tableHeaderCell}>{output(headerCellContent, state)}</Text>
|
|
571
|
+
</View>
|
|
572
|
+
) : null}
|
|
573
|
+
{columnCellContents &&
|
|
574
|
+
columnCellContents.map((content, idx) => (
|
|
575
|
+
<ColumnCell content={content} key={`cell-${idx}`} />
|
|
576
|
+
))}
|
|
577
|
+
</View>
|
|
578
|
+
);
|
|
579
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { DefaultStreamChatGenerics } from '../../../types/types';
|
|
4
|
+
import { StreamingMessageViewProps } from '../MessageSimple/StreamingMessageView';
|
|
5
|
+
|
|
6
|
+
export type UseStreamingMessageProps<
|
|
7
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
8
|
+
> = Pick<
|
|
9
|
+
StreamingMessageViewProps<StreamChatGenerics>,
|
|
10
|
+
'letterInterval' | 'renderingLetterCount'
|
|
11
|
+
> & { text: string };
|
|
12
|
+
|
|
13
|
+
const DEFAULT_LETTER_INTERVAL = 0;
|
|
14
|
+
const DEFAULT_RENDERING_LETTER_COUNT = 2;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A hook that returns text in a streamed, typewriter fashion. The speed of streaming is
|
|
18
|
+
* configurable.
|
|
19
|
+
* @param {number} [letterInterval=0] - The timeout between each typing animation in milliseconds.
|
|
20
|
+
* @param {number} [renderingLetterCount=2] - The number of letters to be rendered each time we update.
|
|
21
|
+
* @param {string} text - The text that we want to render in a typewriter fashion.
|
|
22
|
+
* @returns {{ streamedMessageText: string }} - A substring of the text property, up until we've finished rendering the typewriter animation.
|
|
23
|
+
*/
|
|
24
|
+
export const useStreamingMessage = <
|
|
25
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
26
|
+
>({
|
|
27
|
+
letterInterval = DEFAULT_LETTER_INTERVAL,
|
|
28
|
+
renderingLetterCount = DEFAULT_RENDERING_LETTER_COUNT,
|
|
29
|
+
text,
|
|
30
|
+
}: UseStreamingMessageProps<StreamChatGenerics>): { streamedMessageText: string } => {
|
|
31
|
+
const [streamedMessageText, setStreamedMessageText] = useState<string>(text);
|
|
32
|
+
const textCursor = useRef<number>(text.length);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const textLength = text.length;
|
|
36
|
+
const interval = setInterval(() => {
|
|
37
|
+
if (!text || textCursor.current >= textLength) {
|
|
38
|
+
clearInterval(interval);
|
|
39
|
+
}
|
|
40
|
+
const newCursorValue = textCursor.current + renderingLetterCount;
|
|
41
|
+
const newText = text.substring(0, newCursorValue);
|
|
42
|
+
textCursor.current += newText.length - textCursor.current;
|
|
43
|
+
const codeBlockCounts = (newText.match(/```/g) || []).length;
|
|
44
|
+
const shouldOptimisticallyCloseCodeBlock = codeBlockCounts > 0 && codeBlockCounts % 2 > 0;
|
|
45
|
+
setStreamedMessageText(shouldOptimisticallyCloseCodeBlock ? newText + '```' : newText);
|
|
46
|
+
}, letterInterval);
|
|
47
|
+
|
|
48
|
+
return () => {
|
|
49
|
+
clearInterval(interval);
|
|
50
|
+
};
|
|
51
|
+
}, [letterInterval, renderingLetterCount, text]);
|
|
52
|
+
|
|
53
|
+
return { streamedMessageText };
|
|
54
|
+
};
|