stream-chat-react 12.0.0-rc.1 → 12.0.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/components/Attachment/components/WaveProgressBar.d.ts +3 -1
- package/dist/components/Attachment/components/WaveProgressBar.js +44 -9
- package/dist/components/Avatar/Avatar.js +5 -1
- package/dist/components/Channel/Channel.d.ts +3 -4
- package/dist/components/Channel/Channel.js +95 -56
- package/dist/components/Channel/channelState.js +1 -0
- package/dist/components/Channel/hooks/useCreateChannelStateContext.js +1 -1
- package/dist/components/ChannelList/ChannelList.js +1 -1
- package/dist/components/Chat/Chat.d.ts +1 -1
- package/dist/components/Chat/hooks/useChat.d.ts +2 -2
- package/dist/components/Chat/hooks/useChat.js +11 -8
- package/dist/components/Chat/hooks/useCreateChatClient.d.ts +4 -2
- package/dist/components/Chat/hooks/useCreateChatClient.js +5 -4
- package/dist/components/ChatView/ChatView.d.ts +18 -0
- package/dist/components/ChatView/ChatView.js +100 -0
- package/dist/components/ChatView/index.d.ts +1 -0
- package/dist/components/ChatView/index.js +1 -0
- package/dist/components/DateSeparator/DateSeparator.d.ts +1 -1
- package/dist/components/DateSeparator/DateSeparator.js +1 -1
- package/dist/components/EventComponent/EventComponent.d.ts +1 -1
- package/dist/components/EventComponent/EventComponent.js +1 -1
- package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +9 -3
- package/dist/components/MediaRecorder/AudioRecorder/AudioRecordingInProgress.js +3 -0
- package/dist/components/MediaRecorder/classes/MediaRecorderController.d.ts +6 -7
- package/dist/components/MediaRecorder/classes/MediaRecorderController.js +0 -5
- package/dist/components/MediaRecorder/hooks/index.d.ts +1 -1
- package/dist/components/MediaRecorder/hooks/useMediaRecorder.d.ts +1 -2
- package/dist/components/MediaRecorder/hooks/useMediaRecorder.js +1 -1
- package/dist/components/MediaRecorder/index.d.ts +1 -0
- package/dist/components/MediaRecorder/transcode/index.d.ts +6 -5
- package/dist/components/MediaRecorder/transcode/index.js +5 -15
- package/dist/components/Message/Message.js +2 -1
- package/dist/components/Message/MessageOptions.js +3 -4
- package/dist/components/Message/MessageSimple.js +3 -2
- package/dist/components/Message/MessageStatus.js +3 -2
- package/dist/components/Message/MessageTimestamp.d.ts +1 -2
- package/dist/components/Message/MessageTimestamp.js +0 -1
- package/dist/components/Message/QuotedMessage.js +2 -1
- package/dist/components/Message/Timestamp.d.ts +1 -2
- package/dist/components/Message/Timestamp.js +4 -5
- package/dist/components/Message/hooks/useReactionHandler.js +7 -0
- package/dist/components/Message/renderText/rehypePlugins/mentionsMarkdownPlugin.d.ts +1 -1
- package/dist/components/Message/renderText/remarkPlugins/htmlToTextPlugin.d.ts +1 -1
- package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.d.ts +1 -1
- package/dist/components/Message/renderText/remarkPlugins/keepLineBreaksPlugin.js +2 -6
- package/dist/components/Message/renderText/renderText.d.ts +2 -2
- package/dist/components/Message/renderText/renderText.js +8 -6
- package/dist/components/Message/utils.d.ts +10 -1
- package/dist/components/Message/utils.js +18 -7
- package/dist/components/MessageActions/MessageActions.js +14 -9
- package/dist/components/MessageInput/AttachmentPreviewList/AttachmentPreviewList.js +23 -27
- package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.d.ts +1 -0
- package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.js +1 -1
- package/dist/components/MessageInput/AttachmentPreviewList/ImageAttachmentPreview.js +2 -1
- package/dist/components/MessageInput/MessageInput.d.ts +4 -6
- package/dist/components/MessageInput/MessageInputFlat.js +5 -8
- package/dist/components/MessageInput/QuotedMessagePreview.js +2 -1
- package/dist/components/MessageInput/hooks/useAttachments.d.ts +1 -5
- package/dist/components/MessageInput/hooks/useAttachments.js +65 -52
- package/dist/components/MessageInput/hooks/useCreateMessageInputContext.js +2 -19
- package/dist/components/MessageInput/hooks/useMessageInputState.d.ts +2 -35
- package/dist/components/MessageInput/hooks/useMessageInputState.js +2 -107
- package/dist/components/MessageInput/hooks/usePasteHandler.js +1 -3
- package/dist/components/MessageInput/hooks/useSubmitHandler.js +19 -71
- package/dist/components/MessageInput/hooks/useTimeElapsed.js +5 -4
- package/dist/components/MessageInput/hooks/utils.d.ts +1 -2
- package/dist/components/MessageInput/icons.d.ts +0 -1
- package/dist/components/MessageInput/icons.js +0 -3
- package/dist/components/MessageInput/types.d.ts +3 -30
- package/dist/components/MessageList/MessageList.d.ts +3 -1
- package/dist/components/MessageList/MessageList.js +3 -4
- package/dist/components/MessageList/VirtualizedMessageList.d.ts +5 -2
- package/dist/components/MessageList/VirtualizedMessageList.js +8 -5
- package/dist/components/MessageList/VirtualizedMessageListComponents.d.ts +1 -1
- package/dist/components/MessageList/VirtualizedMessageListComponents.js +7 -6
- package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.d.ts +2 -1
- package/dist/components/MessageList/hooks/MessageList/useEnrichedMessages.js +3 -3
- package/dist/components/MessageList/renderMessages.d.ts +2 -2
- package/dist/components/MessageList/renderMessages.js +4 -1
- package/dist/components/MessageList/utils.d.ts +1 -1
- package/dist/components/MessageList/utils.js +17 -7
- package/dist/components/ReactFileUtilities/types.d.ts +0 -29
- package/dist/components/ReactFileUtilities/utils.d.ts +2 -0
- package/dist/components/ReactFileUtilities/utils.js +2 -0
- package/dist/components/Reactions/ReactionSelector.d.ts +5 -2
- package/dist/components/Reactions/ReactionSelector.js +2 -1
- package/dist/components/Reactions/ReactionsList.d.ts +4 -1
- package/dist/components/Reactions/hooks/useProcessReactions.js +2 -1
- package/dist/components/Thread/Thread.d.ts +0 -2
- package/dist/components/Thread/Thread.js +38 -12
- package/dist/components/Threads/ThreadContext.d.ts +9 -0
- package/dist/components/Threads/ThreadContext.js +9 -0
- package/dist/components/Threads/ThreadList/ThreadList.d.ts +9 -0
- package/dist/components/Threads/ThreadList/ThreadList.js +41 -0
- package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.d.ts +2 -0
- package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.js +5 -0
- package/dist/components/Threads/ThreadList/ThreadListItem.d.ts +9 -0
- package/dist/components/Threads/ThreadList/ThreadListItem.js +52 -0
- package/dist/components/Threads/ThreadList/ThreadListItemUI.d.ts +18 -0
- package/dist/components/Threads/ThreadList/ThreadListItemUI.js +76 -0
- package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.d.ts +2 -0
- package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.js +14 -0
- package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.d.ts +2 -0
- package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.js +16 -0
- package/dist/components/Threads/ThreadList/index.d.ts +3 -0
- package/dist/components/Threads/ThreadList/index.js +3 -0
- package/dist/components/Threads/UnreadCountBadge.d.ts +6 -0
- package/dist/components/Threads/UnreadCountBadge.js +5 -0
- package/dist/components/Threads/hooks/useStateStore.d.ts +3 -0
- package/dist/components/Threads/hooks/useStateStore.js +15 -0
- package/dist/components/Threads/hooks/useThreadManagerState.d.ts +2 -0
- package/dist/components/Threads/hooks/useThreadManagerState.js +6 -0
- package/dist/components/Threads/hooks/useThreadState.d.ts +5 -0
- package/dist/components/Threads/hooks/useThreadState.js +11 -0
- package/dist/components/Threads/icons.d.ts +8 -0
- package/dist/components/Threads/icons.js +13 -0
- package/dist/components/Threads/index.d.ts +3 -0
- package/dist/components/Threads/index.js +3 -0
- package/dist/components/UtilityComponents/ErrorBoundary.d.ts +16 -0
- package/dist/components/UtilityComponents/ErrorBoundary.js +19 -0
- package/dist/components/UtilityComponents/index.d.ts +1 -0
- package/dist/components/UtilityComponents/index.js +1 -0
- package/dist/components/Window/Window.d.ts +1 -3
- package/dist/components/Window/Window.js +2 -2
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -0
- package/dist/context/ChannelActionContext.d.ts +2 -2
- package/dist/context/ComponentContext.d.ts +15 -40
- package/dist/context/ComponentContext.js +7 -9
- package/dist/context/MessageContext.d.ts +1 -1
- package/dist/context/MessageContext.js +3 -2
- package/dist/context/MessageInputContext.d.ts +1 -5
- package/dist/context/TranslationContext.d.ts +1 -11
- package/dist/context/TranslationContext.js +1 -9
- package/dist/context/WithComponents.d.ts +5 -0
- package/dist/context/WithComponents.js +7 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +1 -0
- package/dist/css/v2/emoji-replacement.css +1 -1
- package/dist/css/v2/index.css +2 -2
- package/dist/css/v2/index.layout.css +2 -2
- package/dist/i18n/Streami18n.d.ts +3 -3
- package/dist/i18n/Streami18n.js +1 -2
- package/dist/i18n/de.json +3 -1
- package/dist/i18n/en.json +3 -1
- package/dist/i18n/es.json +3 -1
- package/dist/i18n/fr.json +3 -1
- package/dist/i18n/hi.json +3 -1
- package/dist/i18n/index.d.ts +2 -1
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/it.json +3 -1
- package/dist/i18n/ja.json +3 -1
- package/dist/i18n/ko.json +3 -1
- package/dist/i18n/nl.json +3 -1
- package/dist/i18n/pt.json +3 -1
- package/dist/i18n/ru.json +3 -1
- package/dist/i18n/tr.json +3 -1
- package/dist/i18n/types.d.ts +26 -0
- package/dist/i18n/types.js +1 -0
- package/dist/i18n/utils.d.ts +9 -20
- package/dist/i18n/utils.js +10 -1
- package/dist/index.browser.cjs +47726 -0
- package/dist/index.browser.cjs.map +7 -0
- package/dist/{index.cjs.js → index.node.cjs} +21289 -32183
- package/dist/index.node.cjs.map +7 -0
- package/dist/{components → plugins}/Emojis/EmojiPicker.js +1 -1
- package/dist/plugins/Emojis/icons.d.ts +2 -0
- package/dist/plugins/Emojis/icons.js +4 -0
- package/dist/plugins/Emojis/index.browser.cjs +167 -0
- package/dist/plugins/Emojis/index.browser.cjs.map +7 -0
- package/dist/plugins/Emojis/index.d.ts +2 -0
- package/dist/plugins/Emojis/index.js +2 -0
- package/dist/{components/Emojis/index.cjs.js → plugins/Emojis/index.node.cjs} +31 -192
- package/dist/plugins/Emojis/index.node.cjs.map +7 -0
- package/dist/plugins/encoders/mp3.browser.cjs +105 -0
- package/dist/plugins/encoders/mp3.browser.cjs.map +7 -0
- package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.js +3 -3
- package/dist/plugins/encoders/mp3.node.cjs +109 -0
- package/dist/plugins/encoders/mp3.node.cjs.map +7 -0
- package/dist/scss/v2/Autocomplete/Autocomplete-layout.scss +1 -1
- package/dist/scss/v2/Autocomplete/Autocomplete-theme.scss +4 -2
- package/dist/scss/v2/Avatar/Avatar-layout.scss +38 -22
- package/dist/scss/v2/Avatar/Avatar-theme.scss +5 -0
- package/dist/scss/v2/Channel/Channel-layout.scss +0 -4
- package/dist/scss/v2/ChannelList/ChannelList-layout.scss +0 -5
- package/dist/scss/v2/ChannelSearch/ChannelSearch-layout.scss +1 -0
- package/dist/scss/v2/ChatView/ChatView-layout.scss +43 -0
- package/dist/scss/v2/ChatView/ChatView-theme.scss +31 -0
- package/dist/scss/v2/EditMessageForm/EditMessageForm-theme.scss +9 -9
- package/dist/scss/v2/LoadingIndicator/LoadingIndicator-layout.scss +16 -0
- package/dist/scss/v2/Message/Message-layout.scss +39 -6
- package/dist/scss/v2/MessageList/MessageList-layout.scss +0 -6
- package/dist/scss/v2/MessageList/VirtualizedMessageList-layout.scss +0 -12
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +18 -0
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +5 -0
- package/dist/scss/v2/Thread/Thread-layout.scss +13 -4
- package/dist/scss/v2/ThreadList/ThreadList-layout.scss +149 -0
- package/dist/scss/v2/ThreadList/ThreadList-theme.scss +73 -0
- package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-layout.scss +49 -0
- package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-theme.scss +10 -0
- package/dist/scss/v2/_base.scss +1 -0
- package/dist/scss/v2/_emoji-replacement.scss +4 -2
- package/dist/scss/v2/index.layout.scss +3 -0
- package/dist/scss/v2/index.scss +3 -0
- package/package.json +51 -19
- package/dist/components/Emojis/index.cjs.js.map +0 -7
- package/dist/components/Emojis/index.d.ts +0 -1
- package/dist/components/Emojis/index.js +0 -1
- package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.d.ts +0 -11
- package/dist/components/MessageInput/AttachmentPreviewList/UploadPreviewItem.js +0 -51
- package/dist/components/MessageInput/hooks/useFileUploads.d.ts +0 -7
- package/dist/components/MessageInput/hooks/useFileUploads.js +0 -85
- package/dist/components/MessageInput/hooks/useImageUploads.d.ts +0 -8
- package/dist/components/MessageInput/hooks/useImageUploads.js +0 -94
- package/dist/index.cjs.js.map +0 -7
- /package/dist/{components → plugins}/Emojis/EmojiPicker.d.ts +0 -0
- /package/dist/{components/MediaRecorder/transcode → plugins/encoders}/mp3.d.ts +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PropsWithChildren } from 'react';
|
|
3
|
+
import type { Thread } from 'stream-chat';
|
|
4
|
+
export declare const ChatView: {
|
|
5
|
+
({ children }: PropsWithChildren): React.JSX.Element;
|
|
6
|
+
Channels: ({ children }: PropsWithChildren) => React.JSX.Element | null;
|
|
7
|
+
Threads: ({ children }: PropsWithChildren) => React.JSX.Element | null;
|
|
8
|
+
ThreadAdapter: ({ children }: PropsWithChildren) => React.JSX.Element;
|
|
9
|
+
Selector: () => React.JSX.Element;
|
|
10
|
+
};
|
|
11
|
+
export type ThreadsViewContextValue = {
|
|
12
|
+
activeThread: Thread | undefined;
|
|
13
|
+
setActiveThread: (cv: ThreadsViewContextValue['activeThread']) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const useThreadsViewContext: () => ThreadsViewContextValue;
|
|
16
|
+
export declare const useActiveThread: ({ activeThread }: {
|
|
17
|
+
activeThread?: Thread;
|
|
18
|
+
}) => void;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { ThreadProvider, useStateStore } from '../Threads';
|
|
3
|
+
import { Icon } from '../Threads/icons';
|
|
4
|
+
import { UnreadCountBadge } from '../Threads/UnreadCountBadge';
|
|
5
|
+
import { useChatContext } from '../../context';
|
|
6
|
+
const availableChatViews = ['channels', 'threads'];
|
|
7
|
+
const ChatViewContext = createContext({
|
|
8
|
+
activeChatView: 'channels',
|
|
9
|
+
setActiveChatView: () => undefined,
|
|
10
|
+
});
|
|
11
|
+
export const ChatView = ({ children }) => {
|
|
12
|
+
const [activeChatView, setActiveChatView] = useState('channels');
|
|
13
|
+
const value = useMemo(() => ({ activeChatView, setActiveChatView }), [activeChatView]);
|
|
14
|
+
return (React.createElement(ChatViewContext.Provider, { value: value },
|
|
15
|
+
React.createElement("div", { className: 'str-chat str-chat__chat-view' }, children)));
|
|
16
|
+
};
|
|
17
|
+
const ChannelsView = ({ children }) => {
|
|
18
|
+
const { activeChatView } = useContext(ChatViewContext);
|
|
19
|
+
if (activeChatView !== 'channels')
|
|
20
|
+
return null;
|
|
21
|
+
return React.createElement("div", { className: 'str-chat__chat-view__channels' }, children);
|
|
22
|
+
};
|
|
23
|
+
const ThreadsViewContext = createContext({
|
|
24
|
+
activeThread: undefined,
|
|
25
|
+
setActiveThread: () => undefined,
|
|
26
|
+
});
|
|
27
|
+
export const useThreadsViewContext = () => useContext(ThreadsViewContext);
|
|
28
|
+
const ThreadsView = ({ children }) => {
|
|
29
|
+
const { activeChatView } = useContext(ChatViewContext);
|
|
30
|
+
const [activeThread, setActiveThread] = useState(undefined);
|
|
31
|
+
const value = useMemo(() => ({ activeThread, setActiveThread }), [activeThread]);
|
|
32
|
+
if (activeChatView !== 'threads')
|
|
33
|
+
return null;
|
|
34
|
+
return (React.createElement(ThreadsViewContext.Provider, { value: value },
|
|
35
|
+
React.createElement("div", { className: 'str-chat__chat-view__threads' }, children)));
|
|
36
|
+
};
|
|
37
|
+
// thread business logic that's impossible to keep within client but encapsulated for ease of use
|
|
38
|
+
export const useActiveThread = ({ activeThread }) => {
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!activeThread)
|
|
41
|
+
return;
|
|
42
|
+
const handleVisibilityChange = () => {
|
|
43
|
+
if (document.visibilityState === 'visible' && document.hasFocus()) {
|
|
44
|
+
activeThread.activate();
|
|
45
|
+
}
|
|
46
|
+
if (document.visibilityState === 'hidden' || !document.hasFocus()) {
|
|
47
|
+
activeThread.deactivate();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
handleVisibilityChange();
|
|
51
|
+
window.addEventListener('focus', handleVisibilityChange);
|
|
52
|
+
window.addEventListener('blur', handleVisibilityChange);
|
|
53
|
+
return () => {
|
|
54
|
+
activeThread.deactivate();
|
|
55
|
+
window.addEventListener('blur', handleVisibilityChange);
|
|
56
|
+
window.removeEventListener('focus', handleVisibilityChange);
|
|
57
|
+
};
|
|
58
|
+
}, [activeThread]);
|
|
59
|
+
};
|
|
60
|
+
// ThreadList under View.Threads context, will access setting function and on item click will set activeThread
|
|
61
|
+
// which can be accessed for the ease of use by ThreadAdapter which forwards it to required ThreadProvider
|
|
62
|
+
// ThreadList can easily live without this context and click handler can be overriden, ThreadAdapter is then no longer needed
|
|
63
|
+
/**
|
|
64
|
+
* // this setup still works
|
|
65
|
+
* const MyCustomComponent = () => {
|
|
66
|
+
* const [activeThread, setActiveThread] = useState();
|
|
67
|
+
*
|
|
68
|
+
* return <>
|
|
69
|
+
* // simplified
|
|
70
|
+
* <ThreadList onItemPointerDown={setActiveThread} />
|
|
71
|
+
* <ThreadProvider thread={activeThread}>
|
|
72
|
+
* <Thread />
|
|
73
|
+
* </ThreadProvider>
|
|
74
|
+
* </>
|
|
75
|
+
* }
|
|
76
|
+
*
|
|
77
|
+
*/
|
|
78
|
+
const ThreadAdapter = ({ children }) => {
|
|
79
|
+
const { activeThread } = useThreadsViewContext();
|
|
80
|
+
useActiveThread({ activeThread });
|
|
81
|
+
return React.createElement(ThreadProvider, { thread: activeThread }, children);
|
|
82
|
+
};
|
|
83
|
+
const selector = (nextValue) => [nextValue.unreadThreadCount];
|
|
84
|
+
const ChatViewSelector = () => {
|
|
85
|
+
const { client } = useChatContext();
|
|
86
|
+
const [unreadThreadCount] = useStateStore(client.threads.state, selector);
|
|
87
|
+
const { activeChatView, setActiveChatView } = useContext(ChatViewContext);
|
|
88
|
+
return (React.createElement("div", { className: 'str-chat__chat-view__selector' },
|
|
89
|
+
React.createElement("button", { "aria-selected": activeChatView === 'channels', className: 'str-chat__chat-view__selector-button', onPointerDown: () => setActiveChatView('channels'), role: 'tab' },
|
|
90
|
+
React.createElement(Icon.MessageBubbleEmpty, null),
|
|
91
|
+
React.createElement("div", { className: 'str-chat__chat-view__selector-button-text' }, "Channels")),
|
|
92
|
+
React.createElement("button", { "aria-selected": activeChatView === 'threads', className: 'str-chat__chat-view__selector-button', onPointerDown: () => setActiveChatView('threads'), role: 'tab' },
|
|
93
|
+
React.createElement(UnreadCountBadge, { count: unreadThreadCount, position: 'top-right' },
|
|
94
|
+
React.createElement(Icon.MessageBubble, null)),
|
|
95
|
+
React.createElement("div", { className: 'str-chat__chat-view__selector-button-text' }, "Threads"))));
|
|
96
|
+
};
|
|
97
|
+
ChatView.Channels = ChannelsView;
|
|
98
|
+
ChatView.Threads = ThreadsView;
|
|
99
|
+
ChatView.ThreadAdapter = ThreadAdapter;
|
|
100
|
+
ChatView.Selector = ChatViewSelector;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ChatView';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ChatView';
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { useTranslationContext } from '../../context/TranslationContext';
|
|
3
3
|
import { getDateString } from '../../i18n/utils';
|
|
4
4
|
const UnMemoizedDateSeparator = (props) => {
|
|
5
|
-
const { calendar
|
|
5
|
+
const { calendar, date: messageCreatedAt, formatDate, position = 'right', unread, ...restTimestampFormatterOptions } = props;
|
|
6
6
|
const { t, tDateTimeParser } = useTranslationContext('DateSeparator');
|
|
7
7
|
const formattedDate = getDateString({
|
|
8
8
|
calendar,
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { AvatarProps } from '../Avatar';
|
|
3
3
|
import type { StreamMessage } from '../../context/ChannelStateContext';
|
|
4
4
|
import type { DefaultStreamChatGenerics } from '../../types/types';
|
|
5
|
-
import { TimestampFormatterOptions } from '../../i18n/
|
|
5
|
+
import type { TimestampFormatterOptions } from '../../i18n/types';
|
|
6
6
|
export type EventComponentProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = TimestampFormatterOptions & {
|
|
7
7
|
/** Message object */
|
|
8
8
|
message: StreamMessage<StreamChatGenerics>;
|
|
@@ -6,7 +6,7 @@ import { getDateString } from '../../i18n/utils';
|
|
|
6
6
|
* Component to display system and channel event messages
|
|
7
7
|
*/
|
|
8
8
|
const UnMemoizedEventComponent = (props) => {
|
|
9
|
-
const { calendar, calendarFormats, format
|
|
9
|
+
const { calendar, calendarFormats, format, Avatar = DefaultAvatar, message } = props;
|
|
10
10
|
const { t, tDateTimeParser } = useTranslationContext('EventComponent');
|
|
11
11
|
const { created_at = '', event, text, type } = message;
|
|
12
12
|
const getDateOptions = { messageCreatedAt: created_at.toString(), tDateTimeParser };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { deprecationAndReplacementWarning } from '../../utils/deprecationWarning';
|
|
3
3
|
import { DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD } from '../../constants/limits';
|
|
4
4
|
/**
|
|
@@ -17,6 +17,8 @@ export const InfiniteScroll = (props) => {
|
|
|
17
17
|
const hasNextPageFlag = hasNextPage || hasMoreNewer;
|
|
18
18
|
const hasPreviousPageFlag = hasPreviousPage || hasMore;
|
|
19
19
|
const scrollComponent = useRef();
|
|
20
|
+
const previousOffset = useRef();
|
|
21
|
+
const previousReverseOffset = useRef();
|
|
20
22
|
const scrollListenerRef = useRef();
|
|
21
23
|
scrollListenerRef.current = () => {
|
|
22
24
|
const element = scrollComponent.current;
|
|
@@ -32,7 +34,11 @@ export const InfiniteScroll = (props) => {
|
|
|
32
34
|
}
|
|
33
35
|
if (isLoading)
|
|
34
36
|
return;
|
|
35
|
-
|
|
37
|
+
if (previousOffset.current === offset && previousReverseOffset.current === reverseOffset)
|
|
38
|
+
return;
|
|
39
|
+
previousOffset.current = offset;
|
|
40
|
+
previousReverseOffset.current = reverseOffset;
|
|
41
|
+
// FIXME: this triggers loadMore call when a user types messages in thread and the scroll container expands
|
|
36
42
|
if (reverseOffset < Number(threshold) &&
|
|
37
43
|
typeof loadPreviousPageFn === 'function' &&
|
|
38
44
|
hasPreviousPageFlag) {
|
|
@@ -51,7 +57,7 @@ export const InfiniteScroll = (props) => {
|
|
|
51
57
|
], 'InfiniteScroll');
|
|
52
58
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
59
|
}, []);
|
|
54
|
-
|
|
60
|
+
useEffect(() => {
|
|
55
61
|
const scrollElement = scrollComponent.current?.parentNode;
|
|
56
62
|
if (!scrollElement)
|
|
57
63
|
return;
|
|
@@ -29,6 +29,9 @@ export const AudioRecordingInProgress = () => {
|
|
|
29
29
|
if (!recorder?.mediaRecorder)
|
|
30
30
|
return;
|
|
31
31
|
const { mediaRecorder } = recorder;
|
|
32
|
+
if (mediaRecorder.state === 'recording') {
|
|
33
|
+
startCounter();
|
|
34
|
+
}
|
|
32
35
|
mediaRecorder.addEventListener('start', startCounter);
|
|
33
36
|
mediaRecorder.addEventListener('resume', startCounter);
|
|
34
37
|
mediaRecorder.addEventListener('stop', stopCounter);
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
import { AmplitudeRecorder, AmplitudeRecorderConfig } from './AmplitudeRecorder';
|
|
2
2
|
import { BrowserPermission } from './BrowserPermission';
|
|
3
3
|
import { BehaviorSubject, Subject } from '../observable';
|
|
4
|
+
import { TranscoderConfig } from '../transcode';
|
|
4
5
|
import { RecordedMediaType } from '../../ReactFileUtilities';
|
|
5
6
|
import { TranslationContextValue } from '../../../context';
|
|
6
7
|
import type { LocalVoiceRecordingAttachment } from '../../MessageInput';
|
|
7
8
|
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
8
|
-
export declare const POSSIBLE_TRANSCODING_MIME_TYPES: readonly ["audio/wav", "audio/mp3"];
|
|
9
9
|
export declare const DEFAULT_MEDIA_RECORDER_CONFIG: MediaRecorderConfig;
|
|
10
10
|
export declare const DEFAULT_AUDIO_TRANSCODER_CONFIG: TranscoderConfig;
|
|
11
|
-
type SupportedTranscodeMimeTypes = typeof POSSIBLE_TRANSCODING_MIME_TYPES[number];
|
|
12
|
-
export type TranscoderConfig = {
|
|
13
|
-
sampleRate: number;
|
|
14
|
-
targetMimeType: SupportedTranscodeMimeTypes;
|
|
15
|
-
};
|
|
16
11
|
type MediaRecorderConfig = Omit<MediaRecorderOptions, 'mimeType'> & Required<Pick<MediaRecorderOptions, 'mimeType'>>;
|
|
17
12
|
export type AudioRecorderConfig = {
|
|
18
13
|
amplitudeRecorderConfig: AmplitudeRecorderConfig;
|
|
19
14
|
mediaRecorderConfig: MediaRecorderOptions;
|
|
20
15
|
transcoderConfig: TranscoderConfig;
|
|
21
16
|
};
|
|
17
|
+
type PartialValues<T> = {
|
|
18
|
+
[P in keyof T]?: Partial<T[P]>;
|
|
19
|
+
};
|
|
20
|
+
export type CustomAudioRecordingConfig = PartialValues<AudioRecorderConfig>;
|
|
22
21
|
export type AudioRecorderOptions = {
|
|
23
|
-
config?:
|
|
22
|
+
config?: CustomAudioRecordingConfig;
|
|
24
23
|
generateRecordingTitle?: (mimeType: string) => string;
|
|
25
24
|
t?: TranslationContextValue['t'];
|
|
26
25
|
};
|
|
@@ -15,7 +15,6 @@ const RECORDED_MIME_TYPE_BY_BROWSER = {
|
|
|
15
15
|
safari: 'audio/mp4;codecs=mp4a.40.2',
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
|
-
export const POSSIBLE_TRANSCODING_MIME_TYPES = ['audio/wav', 'audio/mp3'];
|
|
19
18
|
export const DEFAULT_MEDIA_RECORDER_CONFIG = {
|
|
20
19
|
mimeType: isSafari()
|
|
21
20
|
? RECORDED_MIME_TYPE_BY_BROWSER.audio.safari
|
|
@@ -23,7 +22,6 @@ export const DEFAULT_MEDIA_RECORDER_CONFIG = {
|
|
|
23
22
|
};
|
|
24
23
|
export const DEFAULT_AUDIO_TRANSCODER_CONFIG = {
|
|
25
24
|
sampleRate: 16000,
|
|
26
|
-
targetMimeType: 'audio/mp3',
|
|
27
25
|
};
|
|
28
26
|
const disposeOfMediaStream = (stream) => {
|
|
29
27
|
if (!stream?.active)
|
|
@@ -245,9 +243,6 @@ export class MediaRecorderController {
|
|
|
245
243
|
this.amplitudeRecorderConfig = mergeDeepUndefined({ ...config?.amplitudeRecorderConfig }, DEFAULT_AMPLITUDE_RECORDER_CONFIG);
|
|
246
244
|
this.mediaRecorderConfig = mergeDeepUndefined({ ...config?.mediaRecorderConfig }, DEFAULT_MEDIA_RECORDER_CONFIG);
|
|
247
245
|
this.transcoderConfig = mergeDeepUndefined({ ...config?.transcoderConfig }, DEFAULT_AUDIO_TRANSCODER_CONFIG);
|
|
248
|
-
if (!POSSIBLE_TRANSCODING_MIME_TYPES.includes(this.transcoderConfig.targetMimeType)) {
|
|
249
|
-
this.transcoderConfig.targetMimeType = DEFAULT_AUDIO_TRANSCODER_CONFIG.targetMimeType;
|
|
250
|
-
}
|
|
251
246
|
const mediaType = getRecordedMediaTypeFromMimeType(this.mediaRecorderConfig.mimeType);
|
|
252
247
|
if (!mediaType) {
|
|
253
248
|
throw new Error(`Unsupported media type (supported audio or video only). Provided mimeType: ${this.mediaRecorderConfig.mimeType}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { RecordingController } from './useMediaRecorder';
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { MessageInputContextValue } from '../../../context';
|
|
2
|
-
import {
|
|
2
|
+
import { CustomAudioRecordingConfig, MediaRecorderController, MediaRecordingState } from '../classes';
|
|
3
3
|
import type { LocalVoiceRecordingAttachment } from '../../MessageInput';
|
|
4
4
|
import type { DefaultStreamChatGenerics } from '../../../types';
|
|
5
|
-
export type CustomAudioRecordingConfig = Partial<AudioRecorderConfig>;
|
|
6
5
|
export type RecordingController<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
7
6
|
completeRecording: () => void;
|
|
8
7
|
permissionState?: PermissionState;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useTranslationContext } from '../../../context';
|
|
3
|
-
import { MediaRecorderController } from '../classes';
|
|
3
|
+
import { MediaRecorderController, } from '../classes';
|
|
4
4
|
export const useMediaRecorder = ({ asyncMessagesMultiSendEnabled, enabled, generateRecordingTitle, handleSubmit, recordingConfig, uploadAttachment, }) => {
|
|
5
5
|
const { t } = useTranslationContext('useMediaRecorder');
|
|
6
6
|
const [recording, setRecording] = useState();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
type
|
|
2
|
-
blob: Blob;
|
|
1
|
+
export type TranscoderConfig = {
|
|
3
2
|
sampleRate: number;
|
|
4
|
-
|
|
3
|
+
encoder?: (file: File, sampleRate: number) => Promise<Blob>;
|
|
4
|
+
};
|
|
5
|
+
export type TranscodeParams = TranscoderConfig & {
|
|
6
|
+
blob: Blob;
|
|
5
7
|
};
|
|
6
|
-
export declare const transcode: ({ blob, sampleRate,
|
|
7
|
-
export {};
|
|
8
|
+
export declare const transcode: ({ blob, encoder, sampleRate, }: TranscodeParams) => Promise<Blob>;
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { encodeToWaw } from './wav';
|
|
2
|
-
import { encodeToMp3 } from './mp3';
|
|
3
2
|
import { createFileFromBlobs, getExtensionFromMimeType } from '../../ReactFileUtilities';
|
|
4
|
-
export const transcode = ({ blob, sampleRate,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
if (targetMimeType.match('audio/wav')) {
|
|
11
|
-
return encodeToWaw(file, sampleRate);
|
|
12
|
-
}
|
|
13
|
-
if (targetMimeType.match('audio/mp3')) {
|
|
14
|
-
return encodeToMp3(file, sampleRate);
|
|
15
|
-
}
|
|
16
|
-
return Promise.resolve(blob);
|
|
17
|
-
};
|
|
3
|
+
export const transcode = ({ blob, encoder = encodeToWaw, sampleRate, }) => encoder(createFileFromBlobs({
|
|
4
|
+
blobsArray: [blob],
|
|
5
|
+
fileName: `audio_recording_${new Date().toISOString()}.${getExtensionFromMimeType(blob.type)}`,
|
|
6
|
+
mimeType: blob.type,
|
|
7
|
+
}), sampleRate);
|
|
@@ -2,13 +2,14 @@ import React, { useCallback, useMemo, useRef } from 'react';
|
|
|
2
2
|
import { useActionHandler, useDeleteHandler, useEditHandler, useFlagHandler, useMarkUnreadHandler, useMentionsHandler, useMuteHandler, useOpenThreadHandler, usePinHandler, useReactionClick, useReactionHandler, useReactionsFetcher, useRetryHandler, useUserHandler, useUserRole, } from './hooks';
|
|
3
3
|
import { areMessagePropsEqual, getMessageActions, MESSAGE_ACTIONS } from './utils';
|
|
4
4
|
import { MessageProvider, useChannelActionContext, useChannelStateContext, useChatContext, useComponentContext, } from '../../context';
|
|
5
|
+
import { MessageSimple as DefaultMessage } from './MessageSimple';
|
|
5
6
|
const MessageWithContext = (props) => {
|
|
6
7
|
const { canPin, groupedByUser, Message: propMessage, message, messageActions = Object.keys(MESSAGE_ACTIONS), onUserClick: propOnUserClick, onUserHover: propOnUserHover, userRoles, } = props;
|
|
7
8
|
const { client } = useChatContext('Message');
|
|
8
9
|
const { read } = useChannelStateContext('Message');
|
|
9
10
|
const { Message: contextMessage } = useComponentContext('Message');
|
|
10
11
|
const actionsEnabled = message.type === 'regular' && message.status === 'received';
|
|
11
|
-
const MessageUIComponent = propMessage
|
|
12
|
+
const MessageUIComponent = propMessage ?? contextMessage ?? DefaultMessage;
|
|
12
13
|
const { clearEdit, editing, setEdit } = useEditHandler();
|
|
13
14
|
const { onUserClick, onUserHover } = useUserHandler(message, {
|
|
14
15
|
onUserClickHandler: propOnUserClick,
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ActionsIcon as DefaultActionsIcon, ReactionIcon as DefaultReactionIcon, ThreadIcon as DefaultThreadIcon, } from './icons';
|
|
3
|
-
import { MESSAGE_ACTIONS
|
|
3
|
+
import { MESSAGE_ACTIONS } from './utils';
|
|
4
4
|
import { MessageActions } from '../MessageActions';
|
|
5
5
|
import { useMessageContext } from '../../context/MessageContext';
|
|
6
6
|
import { useTranslationContext } from '../../context';
|
|
7
7
|
const UnMemoizedMessageOptions = (props) => {
|
|
8
8
|
const { ActionsIcon = DefaultActionsIcon, displayReplies = true, handleOpenThread: propHandleOpenThread, messageWrapperRef, ReactionIcon = DefaultReactionIcon, theme = 'simple', ThreadIcon = DefaultThreadIcon, } = props;
|
|
9
|
-
const {
|
|
9
|
+
const { getMessageActions, handleOpenThread: contextHandleOpenThread, initialMessage, message, onReactionListClick, showDetailedReactions, threadList, } = useMessageContext('MessageOptions');
|
|
10
10
|
const { t } = useTranslationContext('MessageOptions');
|
|
11
11
|
const handleOpenThread = propHandleOpenThread || contextHandleOpenThread;
|
|
12
12
|
const messageActions = getMessageActions();
|
|
13
|
-
const showActionsBox = showMessageActionsBox(messageActions, threadList) || !!customMessageActions;
|
|
14
13
|
const shouldShowReactions = messageActions.indexOf(MESSAGE_ACTIONS.react) > -1;
|
|
15
14
|
const shouldShowReplies = messageActions.indexOf(MESSAGE_ACTIONS.reply) > -1 && displayReplies && !threadList;
|
|
16
15
|
if (!message.type ||
|
|
@@ -24,7 +23,7 @@ const UnMemoizedMessageOptions = (props) => {
|
|
|
24
23
|
}
|
|
25
24
|
const rootClassName = `str-chat__message-${theme}__actions str-chat__message-options`;
|
|
26
25
|
return (React.createElement("div", { className: rootClassName, "data-testid": 'message-options' },
|
|
27
|
-
|
|
26
|
+
React.createElement(MessageActions, { ActionsIcon: ActionsIcon, messageWrapperRef: messageWrapperRef }),
|
|
28
27
|
shouldShowReplies && (React.createElement("button", { "aria-label": t('aria/Open Thread'), className: `str-chat__message-${theme}__actions__action str-chat__message-${theme}__actions__action--thread str-chat__message-reply-in-thread-button`, "data-testid": 'thread-action', onClick: handleOpenThread },
|
|
29
28
|
React.createElement(ThreadIcon, { className: 'str-chat__message-action-icon' }))),
|
|
30
29
|
shouldShowReactions && (React.createElement("button", { "aria-expanded": showDetailedReactions, "aria-label": t('aria/Open Reaction Selector'), className: `str-chat__message-${theme}__actions__action str-chat__message-${theme}__actions__action--reactions str-chat__message-reactions-button`, "data-testid": 'message-reaction-action', onClick: onReactionListClick },
|
|
@@ -10,6 +10,7 @@ import { MessageText } from './MessageText';
|
|
|
10
10
|
import { MessageTimestamp as DefaultMessageTimestamp } from './MessageTimestamp';
|
|
11
11
|
import { areMessageUIPropsEqual, isMessageBounced, isMessageEdited, messageHasAttachments, messageHasReactions, } from './utils';
|
|
12
12
|
import { Avatar as DefaultAvatar } from '../Avatar';
|
|
13
|
+
import { Attachment as DefaultAttachment } from '../Attachment';
|
|
13
14
|
import { CUSTOM_MESSAGE_TYPE } from '../../constants/messageTypes';
|
|
14
15
|
import { EditMessageForm as DefaultEditMessageForm, MessageInput } from '../MessageInput';
|
|
15
16
|
import { MML } from '../MML';
|
|
@@ -25,7 +26,7 @@ const MessageSimpleWithContext = (props) => {
|
|
|
25
26
|
const { t } = useTranslationContext('MessageSimple');
|
|
26
27
|
const [isBounceDialogOpen, setIsBounceDialogOpen] = useState(false);
|
|
27
28
|
const [isEditedTimestampOpen, setEditedTimestampOpen] = useState(false);
|
|
28
|
-
const { Attachment, Avatar = DefaultAvatar, EditMessageInput = DefaultEditMessageForm, MessageDeleted = DefaultMessageDeleted, MessageBouncePrompt = DefaultMessageBouncePrompt, MessageOptions = DefaultMessageOptions, MessageRepliesCountButton = DefaultMessageRepliesCountButton, MessageStatus = DefaultMessageStatus, MessageTimestamp = DefaultMessageTimestamp, ReactionSelector = DefaultReactionSelector, ReactionsList = DefaultReactionList, PinIndicator, } = useComponentContext('MessageSimple');
|
|
29
|
+
const { Attachment = DefaultAttachment, Avatar = DefaultAvatar, EditMessageInput = DefaultEditMessageForm, MessageDeleted = DefaultMessageDeleted, MessageBouncePrompt = DefaultMessageBouncePrompt, MessageOptions = DefaultMessageOptions, MessageRepliesCountButton = DefaultMessageRepliesCountButton, MessageStatus = DefaultMessageStatus, MessageTimestamp = DefaultMessageTimestamp, ReactionSelector = DefaultReactionSelector, ReactionsList = DefaultReactionList, PinIndicator, } = useComponentContext('MessageSimple');
|
|
29
30
|
const hasAttachment = messageHasAttachments(message);
|
|
30
31
|
const hasReactions = messageHasReactions(message);
|
|
31
32
|
if (message.customType === CUSTOM_MESSAGE_TYPE.date) {
|
|
@@ -92,7 +93,7 @@ const MessageSimpleWithContext = (props) => {
|
|
|
92
93
|
showMetadata && (React.createElement("div", { className: 'str-chat__message-metadata' },
|
|
93
94
|
React.createElement(MessageStatus, null),
|
|
94
95
|
!isMyMessage() && !!message.user && (React.createElement("span", { className: 'str-chat__message-simple-name' }, message.user.name || message.user.id)),
|
|
95
|
-
React.createElement(MessageTimestamp, {
|
|
96
|
+
React.createElement(MessageTimestamp, { customClass: 'str-chat__message-simple-timestamp' }),
|
|
96
97
|
isEdited && (React.createElement("span", { className: 'str-chat__mesage-simple-edited' }, t('Edited'))),
|
|
97
98
|
isEdited && React.createElement(MessageEditedTimestamp, { calendar: true, open: isEditedTimestampOpen }))))));
|
|
98
99
|
};
|
|
@@ -26,9 +26,10 @@ const UnMemoizedMessageStatus = (props) => {
|
|
|
26
26
|
const sending = message.status === 'sending';
|
|
27
27
|
const delivered = message.status === 'received' && message.id === lastReceivedId && !threadList;
|
|
28
28
|
const deliveredAndRead = !!(readBy?.length && !threadList && !justReadByMe);
|
|
29
|
-
const
|
|
29
|
+
const readersWithoutOwnUser = deliveredAndRead
|
|
30
30
|
? readBy.filter((item) => item.id !== client.user?.id)
|
|
31
31
|
: [];
|
|
32
|
+
const [lastReadUser] = readersWithoutOwnUser;
|
|
32
33
|
return (React.createElement("span", { className: rootClassName, "data-testid": clsx({
|
|
33
34
|
'message-status-read-by': deliveredAndRead,
|
|
34
35
|
'message-status-received': delivered && !deliveredAndRead,
|
|
@@ -47,6 +48,6 @@ const UnMemoizedMessageStatus = (props) => {
|
|
|
47
48
|
(MessageReadStatus ? (React.createElement(MessageReadStatus, null)) : (React.createElement(React.Fragment, null,
|
|
48
49
|
React.createElement(PopperTooltip, { offset: [0, 5], referenceElement: referenceElement, visible: tooltipVisible }, getReadByTooltipText(readBy, t, client, tooltipUserNameMapper)),
|
|
49
50
|
React.createElement(Avatar, { className: 'str-chat__avatar--message-status', image: lastReadUser.image, name: lastReadUser.name || lastReadUser.id, user: lastReadUser }),
|
|
50
|
-
|
|
51
|
+
readersWithoutOwnUser.length > 1 && (React.createElement("span", { className: `str-chat__message-${messageType}-status-number`, "data-testid": 'message-status-read-by-many' }, readersWithoutOwnUser.length)))))));
|
|
51
52
|
};
|
|
52
53
|
export const MessageStatus = React.memo(UnMemoizedMessageStatus);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { StreamMessage } from '../../context/ChannelStateContext';
|
|
3
|
+
import type { TimestampFormatterOptions } from '../../i18n/types';
|
|
3
4
|
import type { DefaultStreamChatGenerics } from '../../types/types';
|
|
4
|
-
import type { TimestampFormatterOptions } from '../../i18n/utils';
|
|
5
|
-
export declare const defaultTimestampFormat = "h:mmA";
|
|
6
5
|
export type MessageTimestampProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = TimestampFormatterOptions & {
|
|
7
6
|
customClass?: string;
|
|
8
7
|
message?: StreamMessage<StreamChatGenerics>;
|
|
@@ -2,7 +2,6 @@ import React from 'react';
|
|
|
2
2
|
import { useMessageContext } from '../../context/MessageContext';
|
|
3
3
|
import { Timestamp as DefaultTimestamp } from './Timestamp';
|
|
4
4
|
import { useComponentContext } from '../../context';
|
|
5
|
-
export const defaultTimestampFormat = 'h:mmA';
|
|
6
5
|
const UnMemoizedMessageTimestamp = (props) => {
|
|
7
6
|
const { message: propMessage, ...timestampProps } = props;
|
|
8
7
|
const { message: contextMessage } = useMessageContext('MessageTimestamp');
|
|
@@ -5,8 +5,9 @@ import { useComponentContext } from '../../context/ComponentContext';
|
|
|
5
5
|
import { useMessageContext } from '../../context/MessageContext';
|
|
6
6
|
import { useTranslationContext } from '../../context/TranslationContext';
|
|
7
7
|
import { useChannelActionContext } from '../../context/ChannelActionContext';
|
|
8
|
+
import { Attachment as DefaultAttachment } from '../Attachment';
|
|
8
9
|
export const QuotedMessage = () => {
|
|
9
|
-
const { Attachment, Avatar: ContextAvatar } = useComponentContext('QuotedMessage');
|
|
10
|
+
const { Attachment = DefaultAttachment, Avatar: ContextAvatar, } = useComponentContext('QuotedMessage');
|
|
10
11
|
const { isMyMessage, message } = useMessageContext('QuotedMessage');
|
|
11
12
|
const { t, userLanguage } = useTranslationContext('QuotedMessage');
|
|
12
13
|
const { jumpToMessage } = useChannelActionContext('QuotedMessage');
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { TimestampFormatterOptions } from '../../i18n/
|
|
2
|
+
import type { TimestampFormatterOptions } from '../../i18n/types';
|
|
3
3
|
export interface TimestampProps extends TimestampFormatterOptions {
|
|
4
4
|
customClass?: string;
|
|
5
5
|
timestamp?: Date | string;
|
|
6
6
|
}
|
|
7
|
-
export declare const defaultTimestampFormat = "h:mmA";
|
|
8
7
|
export declare function Timestamp(props: TimestampProps): React.JSX.Element | null;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { useMessageContext } from '../../context/MessageContext';
|
|
3
|
-
import {
|
|
4
|
-
import { getDateString } from '../../i18n/utils';
|
|
5
|
-
export const defaultTimestampFormat = 'h:mmA';
|
|
3
|
+
import { useTranslationContext } from '../../context/TranslationContext';
|
|
4
|
+
import { getDateString, isDate } from '../../i18n/utils';
|
|
6
5
|
export function Timestamp(props) {
|
|
7
|
-
const { calendar, calendarFormats, customClass, format
|
|
6
|
+
const { calendar, calendarFormats, customClass, format, timestamp } = props;
|
|
8
7
|
const { formatDate } = useMessageContext('MessageTimestamp');
|
|
9
8
|
const { t, tDateTimeParser } = useTranslationContext('MessageTimestamp');
|
|
10
9
|
const normalizedTimestamp = timestamp && isDate(timestamp) ? timestamp.toISOString() : timestamp;
|
|
@@ -16,7 +15,7 @@ export function Timestamp(props) {
|
|
|
16
15
|
messageCreatedAt: normalizedTimestamp,
|
|
17
16
|
t,
|
|
18
17
|
tDateTimeParser,
|
|
19
|
-
timestampTranslationKey: 'timestamp/
|
|
18
|
+
timestampTranslationKey: 'timestamp/MessageTimestamp',
|
|
20
19
|
}), [calendar, calendarFormats, format, formatDate, normalizedTimestamp, t, tDateTimeParser]);
|
|
21
20
|
if (!when) {
|
|
22
21
|
return null;
|
|
@@ -3,9 +3,11 @@ import throttle from 'lodash.throttle';
|
|
|
3
3
|
import { useChannelActionContext } from '../../../context/ChannelActionContext';
|
|
4
4
|
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
5
5
|
import { useChatContext } from '../../../context/ChatContext';
|
|
6
|
+
import { useThreadContext } from '../../Threads';
|
|
6
7
|
export const reactionHandlerWarning = `Reaction handler was called, but it is missing one of its required arguments.
|
|
7
8
|
Make sure the ChannelAction and ChannelState contexts are properly set and the hook is initialized with a valid message.`;
|
|
8
9
|
export const useReactionHandler = (message) => {
|
|
10
|
+
const thread = useThreadContext();
|
|
9
11
|
const { updateMessage } = useChannelActionContext('useReactionHandler');
|
|
10
12
|
const { channel, channelCapabilities } = useChannelStateContext('useReactionHandler');
|
|
11
13
|
const { client } = useChatContext('useReactionHandler');
|
|
@@ -64,14 +66,19 @@ export const useReactionHandler = (message) => {
|
|
|
64
66
|
const tempMessage = createMessagePreview(add, newReaction, message);
|
|
65
67
|
try {
|
|
66
68
|
updateMessage(tempMessage);
|
|
69
|
+
// @ts-expect-error
|
|
70
|
+
thread?.upsertReplyLocally({ message: tempMessage });
|
|
67
71
|
const messageResponse = add
|
|
68
72
|
? await channel.sendReaction(id, { type })
|
|
69
73
|
: await channel.deleteReaction(id, type);
|
|
74
|
+
// seems useless as we're expecting WS event to come in and replace this anyway
|
|
70
75
|
updateMessage(messageResponse.message);
|
|
71
76
|
}
|
|
72
77
|
catch (error) {
|
|
73
78
|
// revert to the original message if the API call fails
|
|
74
79
|
updateMessage(message);
|
|
80
|
+
// @ts-expect-error
|
|
81
|
+
thread?.upsertReplyLocally({ message });
|
|
75
82
|
}
|
|
76
83
|
}, 1000);
|
|
77
84
|
return async (reactionType, event) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Nodes } from 'hast-util-find-and-replace/lib';
|
|
2
2
|
import type { UserResponse } from 'stream-chat';
|
|
3
|
-
import type { DefaultStreamChatGenerics } from '../../../../types
|
|
3
|
+
import type { DefaultStreamChatGenerics } from '../../../../types';
|
|
4
4
|
export declare const mentionsMarkdownPlugin: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(mentioned_users: UserResponse<StreamChatGenerics>[]) => () => (tree: Nodes) => void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Nodes } from '
|
|
1
|
+
import type { Nodes } from 'hast-util-find-and-replace/lib';
|
|
2
2
|
export declare const htmlToTextPlugin: () => (tree: Nodes) => void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Nodes } from '
|
|
1
|
+
import type { Nodes } from 'hast-util-find-and-replace/lib';
|
|
2
2
|
export declare const keepLineBreaksPlugin: () => (tree: Nodes) => void;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { visit } from 'unist-util-visit';
|
|
2
2
|
import { u } from 'unist-builder';
|
|
3
3
|
const visitor = (node, index, parent) => {
|
|
4
|
-
if (
|
|
5
|
-
return;
|
|
6
|
-
if (typeof parent === 'undefined')
|
|
7
|
-
return;
|
|
8
|
-
if (!node.position)
|
|
4
|
+
if (!(index && parent && node.position))
|
|
9
5
|
return;
|
|
10
6
|
const prevSibling = parent.children.at(index - 1);
|
|
11
7
|
if (!prevSibling?.position)
|
|
12
8
|
return;
|
|
13
9
|
if (node.position.start.line === prevSibling.position.start.line)
|
|
14
|
-
return
|
|
10
|
+
return;
|
|
15
11
|
const ownStartLine = node.position.start.line;
|
|
16
12
|
const prevEndLine = prevSibling.position.end.line;
|
|
17
13
|
// the -1 is adjustment for the single line break into which multiple line breaks are converted
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { ComponentType } from 'react';
|
|
2
2
|
import { Options } from 'react-markdown';
|
|
3
|
-
import {
|
|
4
|
-
import type { PluggableList } from 'react-markdown/lib';
|
|
3
|
+
import type { PluggableList } from 'react-markdown/lib/react-markdown';
|
|
5
4
|
import type { UserResponse } from 'stream-chat';
|
|
5
|
+
import { MentionProps } from './componentRenderers';
|
|
6
6
|
import type { DefaultStreamChatGenerics } from '../../../types/types';
|
|
7
7
|
export type RenderTextPluginConfigurator = (defaultPlugins: PluggableList) => PluggableList;
|
|
8
8
|
export declare const defaultAllowedTagNames: Array<keyof JSX.IntrinsicElements | 'emoji' | 'mention'>;
|