stream-chat-react 12.0.0-rc.9 → 12.1.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/Avatar/Avatar.js +5 -1
- package/dist/components/Channel/Channel.d.ts +5 -106
- package/dist/components/Channel/Channel.js +76 -24
- package/dist/components/Channel/hooks/useCreateChannelStateContext.js +2 -1
- package/dist/components/ChannelHeader/ChannelHeader.js +4 -5
- package/dist/components/ChannelPreview/hooks/useChannelPreviewInfo.js +14 -16
- package/dist/components/ChannelPreview/utils.js +9 -20
- package/dist/components/ChannelSearch/hooks/useChannelSearch.js +2 -3
- 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/ChatView/ChatView.d.ts +18 -0
- package/dist/components/ChatView/ChatView.js +103 -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/Dialog/DialogAnchor.d.ts +25 -0
- package/dist/components/Dialog/DialogAnchor.js +68 -0
- package/dist/components/Dialog/DialogManager.d.ts +43 -0
- package/dist/components/Dialog/DialogManager.js +98 -0
- package/dist/components/Dialog/DialogPortal.d.ts +7 -0
- package/dist/components/Dialog/DialogPortal.js +25 -0
- package/dist/components/Dialog/hooks/index.d.ts +1 -0
- package/dist/components/Dialog/hooks/index.js +1 -0
- package/dist/components/Dialog/hooks/useDialog.d.ts +4 -0
- package/dist/components/Dialog/hooks/useDialog.js +26 -0
- package/dist/components/Dialog/index.d.ts +4 -0
- package/dist/components/Dialog/index.js +4 -0
- package/dist/components/EventComponent/EventComponent.d.ts +1 -1
- package/dist/components/Message/Message.js +5 -6
- package/dist/components/Message/MessageOptions.d.ts +1 -2
- package/dist/components/Message/MessageOptions.js +14 -11
- package/dist/components/Message/MessageSimple.js +6 -14
- package/dist/components/Message/MessageTimestamp.d.ts +1 -1
- package/dist/components/Message/QuotedMessage.js +2 -1
- package/dist/components/Message/Timestamp.d.ts +1 -1
- package/dist/components/Message/Timestamp.js +2 -2
- package/dist/components/Message/hooks/useReactionHandler.d.ts +1 -7
- package/dist/components/Message/hooks/useReactionHandler.js +8 -63
- package/dist/components/Message/utils.d.ts +10 -1
- package/dist/components/Message/utils.js +19 -7
- package/dist/components/MessageActions/MessageActions.d.ts +1 -2
- package/dist/components/MessageActions/MessageActions.js +26 -55
- package/dist/components/MessageActions/MessageActionsBox.d.ts +1 -1
- package/dist/components/MessageActions/MessageActionsBox.js +6 -6
- package/dist/components/MessageInput/MessageInputFlat.js +2 -2
- package/dist/components/MessageInput/QuotedMessagePreview.js +2 -1
- package/dist/components/MessageInput/hooks/useUserTrigger.js +0 -1
- package/dist/components/MessageList/MessageList.js +9 -9
- package/dist/components/MessageList/MessageListMainPanel.d.ts +1 -1
- package/dist/components/MessageList/MessageListMainPanel.js +1 -1
- package/dist/components/MessageList/VirtualizedMessageList.d.ts +2 -1
- package/dist/components/MessageList/VirtualizedMessageList.js +45 -40
- package/dist/components/MessageList/VirtualizedMessageListComponents.d.ts +1 -1
- package/dist/components/MessageList/VirtualizedMessageListComponents.js +6 -6
- package/dist/components/MessageList/hooks/MessageList/useUnreadMessagesNotification.js +2 -2
- package/dist/components/MessageList/renderMessages.d.ts +2 -2
- package/dist/components/MessageList/renderMessages.js +4 -1
- package/dist/components/MessageList/utils.js +1 -1
- package/dist/components/Reactions/ReactionSelector.d.ts +6 -3
- package/dist/components/Reactions/ReactionSelector.js +34 -24
- package/dist/components/Reactions/ReactionSelectorWithButton.d.ts +13 -0
- package/dist/components/Reactions/ReactionSelectorWithButton.js +22 -0
- package/dist/components/Reactions/ReactionsList.d.ts +4 -4
- package/dist/components/Reactions/hooks/useProcessReactions.js +2 -1
- package/dist/components/Thread/Thread.js +38 -10
- 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 +15 -0
- package/dist/components/Threads/ThreadList/ThreadListItemUI.js +75 -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/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 +2 -0
- package/dist/components/Threads/index.js +2 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +3 -0
- package/dist/context/ComponentContext.d.ts +64 -40
- package/dist/context/ComponentContext.js +7 -9
- package/dist/context/DialogManagerContext.d.ts +10 -0
- package/dist/context/DialogManagerContext.js +14 -0
- package/dist/context/MessageContext.d.ts +3 -11
- package/dist/context/MessageContext.js +3 -2
- 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 +2 -0
- package/dist/context/index.js +2 -0
- package/dist/css/v2/index.css +2 -2
- package/dist/css/v2/index.layout.css +2 -2
- package/dist/i18n/Streami18n.d.ts +1 -3
- package/dist/i18n/Streami18n.js +1 -2
- package/dist/i18n/index.d.ts +2 -1
- package/dist/i18n/index.js +2 -0
- 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 +10823 -10583
- package/dist/index.browser.cjs.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.node.cjs +10765 -10541
- package/dist/index.node.cjs.map +4 -4
- package/dist/plugins/Emojis/index.browser.cjs +7 -169
- package/dist/plugins/Emojis/index.browser.cjs.map +4 -4
- package/dist/plugins/Emojis/index.node.cjs +7 -169
- package/dist/plugins/Emojis/index.node.cjs.map +4 -4
- package/dist/plugins/encoders/mp3.browser.cjs +2 -4
- package/dist/plugins/encoders/mp3.browser.cjs.map +1 -1
- package/dist/plugins/encoders/mp3.node.cjs +2 -4
- package/dist/plugins/encoders/mp3.node.cjs.map +1 -1
- package/dist/scss/v2/Avatar/Avatar-layout.scss +10 -2
- package/dist/scss/v2/Avatar/Avatar-theme.scss +5 -0
- package/dist/scss/v2/ChatView/ChatView-layout.scss +43 -0
- package/dist/scss/v2/ChatView/ChatView-theme.scss +32 -0
- package/dist/scss/v2/Dialog/Dialog-layout.scss +8 -0
- package/dist/scss/v2/LoadingIndicator/LoadingIndicator-layout.scss +16 -0
- package/dist/scss/v2/Message/Message-layout.scss +8 -0
- package/dist/scss/v2/MessageActionsBox/MessageActionsBox-theme.scss +8 -0
- 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 +16 -0
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +6 -0
- package/dist/scss/v2/Thread/Thread-layout.scss +15 -1
- package/dist/scss/v2/ThreadList/ThreadList-layout.scss +152 -0
- package/dist/scss/v2/ThreadList/ThreadList-theme.scss +75 -0
- package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-layout.scss +49 -0
- package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-theme.scss +11 -0
- package/dist/scss/v2/_base.scss +31 -0
- package/dist/scss/v2/index.layout.scss +4 -0
- package/dist/scss/v2/index.scss +3 -0
- package/dist/store/hooks/index.d.ts +1 -0
- package/dist/store/hooks/index.js +1 -0
- package/dist/store/hooks/useStateStore.d.ts +3 -0
- package/dist/store/hooks/useStateStore.js +15 -0
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.js +1 -0
- package/package.json +7 -6
- package/dist/assets/Poweredby_100px-White_VertText.png +0 -0
- package/dist/assets/str-chat__reaction-list-sprite@1x.png +0 -0
- package/dist/assets/str-chat__reaction-list-sprite@2x.png +0 -0
- package/dist/assets/str-chat__reaction-list-sprite@3x.png +0 -0
|
@@ -6,11 +6,15 @@ import { useGiphyPreview, useMessageSetKey, useNewMessageNotification, usePrepen
|
|
|
6
6
|
import { useMarkRead } from './hooks/useMarkRead';
|
|
7
7
|
import { MessageNotification as DefaultMessageNotification } from './MessageNotification';
|
|
8
8
|
import { MessageListNotifications as DefaultMessageListNotifications } from './MessageListNotifications';
|
|
9
|
-
import { MessageListMainPanel } from './MessageListMainPanel';
|
|
9
|
+
import { MessageListMainPanel as DefaultMessageListMainPanel } from './MessageListMainPanel';
|
|
10
10
|
import { getGroupStyles, getLastReceived, processMessages, } from './utils';
|
|
11
11
|
import { MessageSimple } from '../Message';
|
|
12
12
|
import { UnreadMessagesNotification as DefaultUnreadMessagesNotification } from './UnreadMessagesNotification';
|
|
13
13
|
import { calculateFirstItemIndex, calculateItemIndex, EmptyPlaceholder, Header, Item, makeItemsRenderedHandler, messageRenderer, } from './VirtualizedMessageListComponents';
|
|
14
|
+
import { UnreadMessagesSeparator as DefaultUnreadMessagesSeparator } from '../MessageList';
|
|
15
|
+
import { DateSeparator as DefaultDateSeparator } from '../DateSeparator';
|
|
16
|
+
import { EventComponent as DefaultMessageSystem } from '../EventComponent';
|
|
17
|
+
import { DialogManagerProvider } from '../../context';
|
|
14
18
|
import { useChannelActionContext, } from '../../context/ChannelActionContext';
|
|
15
19
|
import { useChannelStateContext, } from '../../context/ChannelStateContext';
|
|
16
20
|
import { useChatContext } from '../../context/ChatContext';
|
|
@@ -53,7 +57,7 @@ const VirtualizedMessageListWithContext = (props) => {
|
|
|
53
57
|
// Stops errors generated from react-virtuoso to bubble up
|
|
54
58
|
// to Sentry or other tracking tools.
|
|
55
59
|
useCaptureResizeObserverExceededError();
|
|
56
|
-
const { DateSeparator, GiphyPreviewMessage = DefaultGiphyPreviewMessage, MessageListNotifications = DefaultMessageListNotifications, MessageNotification = DefaultMessageNotification, MessageSystem, UnreadMessagesNotification = DefaultUnreadMessagesNotification, UnreadMessagesSeparator, VirtualMessage: MessageUIComponentFromContext = MessageSimple, TypingIndicator, } = useComponentContext('VirtualizedMessageList');
|
|
60
|
+
const { DateSeparator = DefaultDateSeparator, GiphyPreviewMessage = DefaultGiphyPreviewMessage, MessageListNotifications = DefaultMessageListNotifications, MessageNotification = DefaultMessageNotification, MessageSystem = DefaultMessageSystem, MessageListMainPanel = DefaultMessageListMainPanel, UnreadMessagesNotification = DefaultUnreadMessagesNotification, UnreadMessagesSeparator = DefaultUnreadMessagesSeparator, VirtualMessage: MessageUIComponentFromContext = MessageSimple, TypingIndicator, } = useComponentContext('VirtualizedMessageList');
|
|
57
61
|
const MessageUIComponent = MessageUIComponentFromProps || MessageUIComponentFromContext;
|
|
58
62
|
const { client, customClasses } = useChatContext('VirtualizedMessageList');
|
|
59
63
|
const virtuoso = useRef(null);
|
|
@@ -187,43 +191,44 @@ const VirtualizedMessageListWithContext = (props) => {
|
|
|
187
191
|
return null;
|
|
188
192
|
return (React.createElement(React.Fragment, null,
|
|
189
193
|
React.createElement(MessageListMainPanel, null,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
React.createElement(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
194
|
+
React.createElement(DialogManagerProvider, { id: 'virtualized-message-list-dialog-manager' },
|
|
195
|
+
!threadList && showUnreadMessagesNotification && (React.createElement(UnreadMessagesNotification, { unreadCount: channelUnreadUiState?.unread_messages })),
|
|
196
|
+
React.createElement("div", { className: customClasses?.virtualizedMessageList || 'str-chat__virtual-list' },
|
|
197
|
+
React.createElement(Virtuoso, { atBottomStateChange: atBottomStateChange, atBottomThreshold: 100, atTopStateChange: atTopStateChange, atTopThreshold: 100, className: 'str-chat__message-list-scroll', components: {
|
|
198
|
+
EmptyPlaceholder,
|
|
199
|
+
Header,
|
|
200
|
+
Item,
|
|
201
|
+
...virtuosoComponentsFromProps,
|
|
202
|
+
}, computeItemKey: computeItemKey, context: {
|
|
203
|
+
additionalMessageInputProps,
|
|
204
|
+
closeReactionSelectorOnClick,
|
|
205
|
+
customClasses,
|
|
206
|
+
customMessageActions,
|
|
207
|
+
customMessageRenderer,
|
|
208
|
+
DateSeparator,
|
|
209
|
+
firstUnreadMessageId: channelUnreadUiState?.first_unread_message_id,
|
|
210
|
+
formatDate,
|
|
211
|
+
head,
|
|
212
|
+
lastReadDate: channelUnreadUiState?.last_read,
|
|
213
|
+
lastReadMessageId: channelUnreadUiState?.last_read_message_id,
|
|
214
|
+
lastReceivedMessageId,
|
|
215
|
+
loadingMore,
|
|
216
|
+
Message: MessageUIComponent,
|
|
217
|
+
messageActions,
|
|
218
|
+
messageGroupStyles,
|
|
219
|
+
MessageSystem,
|
|
220
|
+
numItemsPrepended,
|
|
221
|
+
ownMessagesReadByOthers,
|
|
222
|
+
processedMessages,
|
|
223
|
+
reactionDetailsSort,
|
|
224
|
+
shouldGroupByUser,
|
|
225
|
+
sortReactionDetails,
|
|
226
|
+
sortReactions,
|
|
227
|
+
threadList,
|
|
228
|
+
unreadMessageCount: channelUnreadUiState?.unread_messages,
|
|
229
|
+
UnreadMessagesSeparator,
|
|
230
|
+
virtuosoRef: virtuoso,
|
|
231
|
+
}, firstItemIndex: calculateFirstItemIndex(numItemsPrepended), followOutput: followOutput, increaseViewportBy: { bottom: 200, top: 0 }, initialTopMostItemIndex: calculateInitialTopMostItemIndex(processedMessages, highlightedMessageId), itemContent: messageRenderer, itemSize: fractionalItemSize, itemsRendered: handleItemsRendered, key: messageSetKey, overscan: overscan, ref: virtuoso, style: { overflowX: 'hidden' }, totalCount: processedMessages.length, ...overridingVirtuosoProps, ...(scrollSeekPlaceHolder ? { scrollSeek: scrollSeekPlaceHolder } : {}), ...(defaultItemHeight ? { defaultItemHeight } : {}) }))),
|
|
227
232
|
TypingIndicator && React.createElement(TypingIndicator, null)),
|
|
228
233
|
React.createElement(MessageListNotifications, { hasNewMessages: newMessagesNotification, isMessageListScrolledToBottom: isMessageListScrolledToBottom, isNotAtLatestMessageSet: hasMoreNewer, MessageNotification: MessageNotification, notifications: notifications, scrollToBottom: scrollToBottom, threadList: threadList, unreadCount: threadList ? undefined : channelUnreadUiState?.unread_messages }),
|
|
229
234
|
giphyPreviewMessage && React.createElement(GiphyPreviewMessage, { message: giphyPreviewMessage })));
|
|
@@ -236,5 +241,5 @@ export function VirtualizedMessageList(props) {
|
|
|
236
241
|
const { jumpToLatestMessage, loadMore, loadMoreNewer, } = useChannelActionContext('VirtualizedMessageList');
|
|
237
242
|
const { channel, channelUnreadUiState, hasMore, hasMoreNewer, highlightedMessageId, loadingMore, loadingMoreNewer, messages: contextMessages, notifications, read, suppressAutoscroll, } = useChannelStateContext('VirtualizedMessageList');
|
|
238
243
|
const messages = props.messages || contextMessages;
|
|
239
|
-
return (React.createElement(VirtualizedMessageListWithContext, { channel: channel, channelUnreadUiState: channelUnreadUiState, hasMore: !!hasMore, hasMoreNewer: !!hasMoreNewer, highlightedMessageId: highlightedMessageId, jumpToLatestMessage: jumpToLatestMessage, loadingMore: !!loadingMore, loadingMoreNewer: !!loadingMoreNewer, loadMore: loadMore, loadMoreNewer: loadMoreNewer, messages: messages, notifications: notifications, read: read, suppressAutoscroll: suppressAutoscroll, ...props }));
|
|
244
|
+
return (React.createElement(VirtualizedMessageListWithContext, { channel: channel, channelUnreadUiState: props.channelUnreadUiState ?? channelUnreadUiState, hasMore: !!hasMore, hasMoreNewer: !!hasMoreNewer, highlightedMessageId: highlightedMessageId, jumpToLatestMessage: jumpToLatestMessage, loadingMore: !!loadingMore, loadingMoreNewer: !!loadingMoreNewer, loadMore: loadMore, loadMoreNewer: loadMoreNewer, messages: messages, notifications: notifications, read: read, suppressAutoscroll: suppressAutoscroll, ...props }));
|
|
240
245
|
}
|
|
@@ -11,7 +11,7 @@ type CommonVirtuosoComponentProps<StreamChatGenerics extends DefaultStreamChatGe
|
|
|
11
11
|
context?: VirtuosoContext<StreamChatGenerics>;
|
|
12
12
|
};
|
|
13
13
|
export declare const Item: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ context, ...props }: ItemProps & CommonVirtuosoComponentProps<StreamChatGenerics>) => React.JSX.Element;
|
|
14
|
-
export declare const Header: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ context, }: CommonVirtuosoComponentProps<StreamChatGenerics>) => React.JSX.Element
|
|
14
|
+
export declare const Header: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ context, }: CommonVirtuosoComponentProps<StreamChatGenerics>) => React.JSX.Element;
|
|
15
15
|
export declare const EmptyPlaceholder: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ context, }: CommonVirtuosoComponentProps<StreamChatGenerics>) => React.JSX.Element;
|
|
16
16
|
export declare const messageRenderer: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(virtuosoIndex: number, _data: UnknownType, virtuosoContext: VirtuosoContext<StreamChatGenerics>) => React.JSX.Element | null;
|
|
17
17
|
export {};
|
|
@@ -37,17 +37,17 @@ export const Item = ({ context, ...props }) => {
|
|
|
37
37
|
};
|
|
38
38
|
export const Header = ({ context, }) => {
|
|
39
39
|
const { LoadingIndicator = DefaultLoadingIndicator } = useComponentContext('VirtualizedMessageListHeader');
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
return (React.createElement(React.Fragment, null,
|
|
41
|
+
context?.head,
|
|
42
|
+
context?.loadingMore && LoadingIndicator && (React.createElement("div", { className: 'str-chat__virtual-list__loading' },
|
|
43
|
+
React.createElement(LoadingIndicator, { size: 20 })))));
|
|
44
44
|
};
|
|
45
45
|
export const EmptyPlaceholder = ({ context, }) => {
|
|
46
46
|
const { EmptyStateIndicator = DefaultEmptyStateIndicator, } = useComponentContext('VirtualizedMessageList');
|
|
47
47
|
return (React.createElement(React.Fragment, null, EmptyStateIndicator && (React.createElement(EmptyStateIndicator, { listType: context?.threadList ? 'thread' : 'message' }))));
|
|
48
48
|
};
|
|
49
49
|
export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
|
|
50
|
-
const { additionalMessageInputProps, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, DateSeparator, firstUnreadMessageId, formatDate, lastReadDate, lastReadMessageId, lastReceivedMessageId, Message: MessageUIComponent, messageActions, messageGroupStyles, MessageSystem, numItemsPrepended, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, shouldGroupByUser, sortReactionDetails, sortReactions, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = virtuosoContext;
|
|
50
|
+
const { additionalMessageInputProps, closeReactionSelectorOnClick, customMessageActions, customMessageRenderer, DateSeparator, firstUnreadMessageId, formatDate, lastReadDate, lastReadMessageId, lastReceivedMessageId, Message: MessageUIComponent, messageActions, messageGroupStyles, MessageSystem, numItemsPrepended, ownMessagesReadByOthers, processedMessages: messageList, reactionDetailsSort, shouldGroupByUser, sortReactionDetails, sortReactions, threadList, unreadMessageCount = 0, UnreadMessagesSeparator, virtuosoRef, } = virtuosoContext;
|
|
51
51
|
const streamMessageIndex = calculateItemIndex(virtuosoIndex, numItemsPrepended);
|
|
52
52
|
if (customMessageRenderer) {
|
|
53
53
|
return customMessageRenderer(messageList, streamMessageIndex);
|
|
@@ -89,7 +89,7 @@ export const messageRenderer = (virtuosoIndex, _data, virtuosoContext) => {
|
|
|
89
89
|
return (React.createElement(React.Fragment, null,
|
|
90
90
|
showUnreadSeparatorAbove && (React.createElement("div", { className: 'str-chat__unread-messages-separator-wrapper' },
|
|
91
91
|
React.createElement(UnreadMessagesSeparator, { unreadCount: unreadMessageCount }))),
|
|
92
|
-
React.createElement(Message, { additionalMessageInputProps: additionalMessageInputProps, autoscrollToBottom: virtuosoRef.current?.autoscrollToBottom, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: customMessageActions, endOfGroup: endOfGroup, firstOfGroup: firstOfGroup, formatDate: formatDate, groupedByUser: groupedByUser, groupStyles: [messageGroupStyles[message.id] ?? ''], lastReceivedId: lastReceivedMessageId, message: message, Message: MessageUIComponent, messageActions: messageActions, reactionDetailsSort: reactionDetailsSort, readBy: ownMessagesReadByOthers[message.id] || [], sortReactionDetails: sortReactionDetails, sortReactions: sortReactions }),
|
|
92
|
+
React.createElement(Message, { additionalMessageInputProps: additionalMessageInputProps, autoscrollToBottom: virtuosoRef.current?.autoscrollToBottom, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: customMessageActions, endOfGroup: endOfGroup, firstOfGroup: firstOfGroup, formatDate: formatDate, groupedByUser: groupedByUser, groupStyles: [messageGroupStyles[message.id] ?? ''], lastReceivedId: lastReceivedMessageId, message: message, Message: MessageUIComponent, messageActions: messageActions, reactionDetailsSort: reactionDetailsSort, readBy: ownMessagesReadByOthers[message.id] || [], sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: threadList }),
|
|
93
93
|
showUnreadSeparatorBelow && (React.createElement("div", { className: 'str-chat__unread-messages-separator-wrapper' },
|
|
94
94
|
React.createElement(UnreadMessagesSeparator, { unreadCount: unreadMessageCount })))));
|
|
95
95
|
};
|
|
@@ -21,10 +21,10 @@ export const useUnreadMessagesNotification = ({ isMessageListScrolledToBottom, s
|
|
|
21
21
|
setShow(false);
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
const msgListPanel = document.
|
|
24
|
+
const [msgListPanel] = document.getElementsByClassName(MESSAGE_LIST_MAIN_PANEL_CLASS);
|
|
25
25
|
if (!msgListPanel)
|
|
26
26
|
return;
|
|
27
|
-
const observedTarget = document.
|
|
27
|
+
const [observedTarget] = document.getElementsByClassName(UNREAD_MESSAGE_SEPARATOR_CLASS);
|
|
28
28
|
if (!observedTarget) {
|
|
29
29
|
setShow(true);
|
|
30
30
|
return;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
|
+
import type { UserResponse } from 'stream-chat';
|
|
2
3
|
import { GroupStyle } from './utils';
|
|
3
|
-
import { MessageProps } from '../Message';
|
|
4
4
|
import { ComponentContextValue, CustomClasses } from '../../context';
|
|
5
|
-
import type { UserResponse } from 'stream-chat';
|
|
6
5
|
import type { ChannelUnreadUiState, DefaultStreamChatGenerics } from '../../types';
|
|
7
6
|
import type { StreamMessage } from '../../context/ChannelStateContext';
|
|
7
|
+
import type { MessageProps } from '../Message';
|
|
8
8
|
export interface RenderMessagesOptions<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> {
|
|
9
9
|
components: ComponentContextValue<StreamChatGenerics>;
|
|
10
10
|
lastReceivedMessageId: string | null;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React, { Fragment } from 'react';
|
|
2
2
|
import { isDateSeparatorMessage } from './utils';
|
|
3
3
|
import { Message } from '../Message';
|
|
4
|
+
import { DateSeparator as DefaultDateSeparator } from '../DateSeparator';
|
|
5
|
+
import { EventComponent as DefaultMessageSystem } from '../EventComponent';
|
|
6
|
+
import { UnreadMessagesSeparator as DefaultUnreadMessagesSeparator } from './UnreadMessagesSeparator';
|
|
4
7
|
import { CUSTOM_MESSAGE_TYPE } from '../../constants/messageTypes';
|
|
5
8
|
export function defaultRenderMessages({ channelUnreadUiState, components, customClasses, lastReceivedMessageId: lastReceivedId, messageGroupStyles, messages, readData, sharedMessageProps: messageProps, }) {
|
|
6
|
-
const { DateSeparator, HeaderComponent, MessageSystem, UnreadMessagesSeparator } = components;
|
|
9
|
+
const { DateSeparator = DefaultDateSeparator, HeaderComponent, MessageSystem = DefaultMessageSystem, UnreadMessagesSeparator = DefaultUnreadMessagesSeparator, } = components;
|
|
7
10
|
const renderedMessages = [];
|
|
8
11
|
let firstMessage;
|
|
9
12
|
for (let index = 0; index < messages.length; index++) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable no-continue */
|
|
2
2
|
import { nanoid } from 'nanoid';
|
|
3
3
|
import { CUSTOM_MESSAGE_TYPE } from '../../constants/messageTypes';
|
|
4
|
-
import { isDate } from '../../context/TranslationContext';
|
|
5
4
|
import { isMessageEdited } from '../Message/utils';
|
|
5
|
+
import { isDate } from '../../i18n';
|
|
6
6
|
/**
|
|
7
7
|
* processMessages - Transform the input message list according to config parameters
|
|
8
8
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { AvatarProps } from '../Avatar';
|
|
3
2
|
import type { ReactionGroupResponse, ReactionResponse } from 'stream-chat';
|
|
3
|
+
import type { AvatarProps } from '../Avatar';
|
|
4
4
|
import type { DefaultStreamChatGenerics } from '../../types/types';
|
|
5
5
|
import type { ReactionOptions } from './reactionOptions';
|
|
6
6
|
export type ReactionSelectorProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
|
|
@@ -21,7 +21,10 @@ export type ReactionSelectorProps<StreamChatGenerics extends DefaultStreamChatGe
|
|
|
21
21
|
reaction_counts?: Record<string, number>;
|
|
22
22
|
/** An object containing summary for each reaction type on a message */
|
|
23
23
|
reaction_groups?: Record<string, ReactionGroupResponse>;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated
|
|
26
|
+
* A list of the currently supported reactions on a message
|
|
27
|
+
* */
|
|
25
28
|
reactionOptions?: ReactionOptions;
|
|
26
29
|
/** If true, adds a CSS class that reverses the horizontal positioning of the selector */
|
|
27
30
|
reverse?: boolean;
|
|
@@ -29,4 +32,4 @@ export type ReactionSelectorProps<StreamChatGenerics extends DefaultStreamChatGe
|
|
|
29
32
|
/**
|
|
30
33
|
* Component that allows a user to select a reaction.
|
|
31
34
|
*/
|
|
32
|
-
export declare const ReactionSelector:
|
|
35
|
+
export declare const ReactionSelector: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: ReactionSelectorProps<StreamChatGenerics>) => React.JSX.Element;
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
-
import { isMutableRef } from './utils/utils';
|
|
4
3
|
import { Avatar as DefaultAvatar } from '../Avatar';
|
|
4
|
+
import { useDialog } from '../Dialog';
|
|
5
|
+
import { defaultReactionOptions } from './reactionOptions';
|
|
6
|
+
import { isMutableRef } from './utils/utils';
|
|
5
7
|
import { useComponentContext } from '../../context/ComponentContext';
|
|
6
8
|
import { useMessageContext } from '../../context/MessageContext';
|
|
7
|
-
const UnMemoizedReactionSelector =
|
|
9
|
+
const UnMemoizedReactionSelector = (props) => {
|
|
8
10
|
const { Avatar: propAvatar, detailedView = true, handleReaction: propHandleReaction, latest_reactions: propLatestReactions, own_reactions: propOwnReactions, reaction_groups: propReactionGroups, reactionOptions: propReactionOptions, reverse = false, } = props;
|
|
9
|
-
const { Avatar: contextAvatar, reactionOptions: contextReactionOptions, } = useComponentContext('ReactionSelector');
|
|
10
|
-
const { handleReaction: contextHandleReaction, message, } = useMessageContext('ReactionSelector');
|
|
11
|
+
const { Avatar: contextAvatar, reactionOptions: contextReactionOptions = defaultReactionOptions, } = useComponentContext('ReactionSelector');
|
|
12
|
+
const { closeReactionSelectorOnClick, handleReaction: contextHandleReaction, message, } = useMessageContext('ReactionSelector');
|
|
13
|
+
const dialogId = `reaction-selector--${message.id}`;
|
|
14
|
+
const dialog = useDialog({ id: dialogId });
|
|
11
15
|
const reactionOptions = propReactionOptions ?? contextReactionOptions;
|
|
12
16
|
const Avatar = propAvatar || contextAvatar || DefaultAvatar;
|
|
13
17
|
const handleReaction = propHandleReaction || contextHandleReaction;
|
|
@@ -16,6 +20,7 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
16
20
|
const reactionGroups = propReactionGroups || message?.reaction_groups || {};
|
|
17
21
|
const [tooltipReactionType, setTooltipReactionType] = useState(null);
|
|
18
22
|
const [tooltipPositions, setTooltipPositions] = useState(null);
|
|
23
|
+
const rootRef = useRef(null);
|
|
19
24
|
const targetRef = useRef(null);
|
|
20
25
|
const tooltipRef = useRef(null);
|
|
21
26
|
const showTooltip = useCallback((event, reactionType) => {
|
|
@@ -27,22 +32,22 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
27
32
|
setTooltipPositions(null);
|
|
28
33
|
}, []);
|
|
29
34
|
useEffect(() => {
|
|
30
|
-
if (tooltipReactionType)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
}, [tooltipReactionType,
|
|
35
|
+
if (!tooltipReactionType || !rootRef.current)
|
|
36
|
+
return;
|
|
37
|
+
const tooltip = tooltipRef.current?.getBoundingClientRect();
|
|
38
|
+
const target = targetRef.current?.getBoundingClientRect();
|
|
39
|
+
const container = isMutableRef(rootRef) ? rootRef.current?.getBoundingClientRect() : null;
|
|
40
|
+
if (!tooltip || !target || !container)
|
|
41
|
+
return;
|
|
42
|
+
const tooltipPosition = tooltip.width === container.width || tooltip.x < container.x
|
|
43
|
+
? 0
|
|
44
|
+
: target.left + target.width / 2 - container.left - tooltip.width / 2;
|
|
45
|
+
const arrowPosition = target.x - tooltip.x + target.width / 2 - tooltipPosition;
|
|
46
|
+
setTooltipPositions({
|
|
47
|
+
arrow: arrowPosition,
|
|
48
|
+
tooltip: tooltipPosition,
|
|
49
|
+
});
|
|
50
|
+
}, [tooltipReactionType, rootRef]);
|
|
46
51
|
const getUsersPerReactionType = (type) => latestReactions
|
|
47
52
|
.map((reaction) => {
|
|
48
53
|
if (reaction.type === type) {
|
|
@@ -54,9 +59,9 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
54
59
|
const iHaveReactedWithReaction = (reactionType) => ownReactions.find((reaction) => reaction.type === reactionType);
|
|
55
60
|
const getLatestUserForReactionType = (type) => latestReactions.find((reaction) => reaction.type === type && !!reaction.user)?.user ||
|
|
56
61
|
undefined;
|
|
57
|
-
return (React.createElement("div", { className: clsx('str-chat__reaction-selector str-chat__message-reaction-selector', {
|
|
62
|
+
return (React.createElement("div", { className: clsx('str-chat__reaction-selector str-chat__message-reaction-selector str-chat-react__message-reaction-selector', {
|
|
58
63
|
'str-chat__reaction-selector--reverse': reverse,
|
|
59
|
-
}), "data-testid": 'reaction-selector', ref:
|
|
64
|
+
}), "data-testid": 'reaction-selector', ref: rootRef },
|
|
60
65
|
!!tooltipReactionType && detailedView && (React.createElement("div", { className: 'str-chat__reaction-selector-tooltip', ref: tooltipRef, style: {
|
|
61
66
|
left: tooltipPositions?.tooltip,
|
|
62
67
|
visibility: tooltipPositions ? 'visible' : 'hidden',
|
|
@@ -69,13 +74,18 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
69
74
|
return (React.createElement("li", { key: reactionType },
|
|
70
75
|
React.createElement("button", { "aria-label": `Select Reaction: ${reactionName || reactionType}`, className: clsx('str-chat__message-reactions-list-item str-chat__message-reactions-option', {
|
|
71
76
|
'str-chat__message-reactions-option-selected': iHaveReactedWithReaction(reactionType),
|
|
72
|
-
}), "data-text": reactionType, onClick: (event) =>
|
|
77
|
+
}), "data-testid": 'select-reaction-button', "data-text": reactionType, onClick: (event) => {
|
|
78
|
+
handleReaction(reactionType, event);
|
|
79
|
+
if (closeReactionSelectorOnClick) {
|
|
80
|
+
dialog.close();
|
|
81
|
+
}
|
|
82
|
+
} },
|
|
73
83
|
!!count && detailedView && (React.createElement("div", { className: 'latest-user str-chat__message-reactions-last-user', onClick: hideTooltip, onMouseEnter: (e) => showTooltip(e, reactionType), onMouseLeave: hideTooltip }, latestUser ? (React.createElement(Avatar, { image: latestUser.image, name: latestUser.name, size: 20, user: latestUser })) : (React.createElement("div", { className: 'latest-user-not-found' })))),
|
|
74
84
|
React.createElement("span", { className: 'str-chat__message-reaction-emoji' },
|
|
75
85
|
React.createElement(Component, null)),
|
|
76
86
|
Boolean(count) && detailedView && (React.createElement("span", { className: 'str-chat__message-reactions-list-item__count' }, count || '')))));
|
|
77
87
|
}))));
|
|
78
|
-
}
|
|
88
|
+
};
|
|
79
89
|
/**
|
|
80
90
|
* Component that allows a user to select a reaction.
|
|
81
91
|
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DefaultStreamChatGenerics } from '../../types';
|
|
3
|
+
import type { IconProps } from '../../types/types';
|
|
4
|
+
type ReactionSelectorWithButtonProps = {
|
|
5
|
+
ReactionIcon: React.ComponentType<IconProps>;
|
|
6
|
+
theme: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Internal convenience component - not to be exported. It just groups the button and the dialog anchor and thus prevents
|
|
10
|
+
* cluttering the parent component.
|
|
11
|
+
*/
|
|
12
|
+
export declare const ReactionSelectorWithButton: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ ReactionIcon, theme, }: ReactionSelectorWithButtonProps) => React.JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import { ReactionSelector as DefaultReactionSelector } from './ReactionSelector';
|
|
3
|
+
import { DialogAnchor, useDialog, useDialogIsOpen } from '../Dialog';
|
|
4
|
+
import { useComponentContext, useMessageContext, useTranslationContext } from '../../context';
|
|
5
|
+
/**
|
|
6
|
+
* Internal convenience component - not to be exported. It just groups the button and the dialog anchor and thus prevents
|
|
7
|
+
* cluttering the parent component.
|
|
8
|
+
*/
|
|
9
|
+
export const ReactionSelectorWithButton = ({ ReactionIcon, theme, }) => {
|
|
10
|
+
const { t } = useTranslationContext('ReactionSelectorWithButton');
|
|
11
|
+
const { isMyMessage, message } = useMessageContext('MessageOptions');
|
|
12
|
+
const { ReactionSelector = DefaultReactionSelector } = useComponentContext('MessageOptions');
|
|
13
|
+
const buttonRef = useRef(null);
|
|
14
|
+
const dialogId = `reaction-selector--${message.id}`;
|
|
15
|
+
const dialog = useDialog({ id: dialogId });
|
|
16
|
+
const dialogIsOpen = useDialogIsOpen(dialogId);
|
|
17
|
+
return (React.createElement(React.Fragment, null,
|
|
18
|
+
React.createElement(DialogAnchor, { id: dialogId, placement: isMyMessage() ? 'top-end' : 'top-start', referenceElement: buttonRef.current, trapFocus: true },
|
|
19
|
+
React.createElement(ReactionSelector, null)),
|
|
20
|
+
React.createElement("button", { "aria-expanded": dialogIsOpen, "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: () => dialog?.toggle(), ref: buttonRef },
|
|
21
|
+
React.createElement(ReactionIcon, { className: 'str-chat__message-action-icon' }))));
|
|
22
|
+
};
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ReactionGroupResponse, ReactionResponse } from 'stream-chat';
|
|
3
|
-
import type { ReactEventHandler } from '../Message/types';
|
|
4
3
|
import type { DefaultStreamChatGenerics } from '../../types/types';
|
|
5
4
|
import type { ReactionOptions } from './reactionOptions';
|
|
6
5
|
import type { ReactionDetailsComparator, ReactionsComparator } from './types';
|
|
7
6
|
import { MessageContextValue } from '../../context';
|
|
8
7
|
export type ReactionsListProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Partial<Pick<MessageContextValue<StreamChatGenerics>, 'handleFetchReactions' | 'reactionDetailsSort'>> & {
|
|
9
|
-
/** Custom on click handler for an individual reaction, defaults to `onReactionListClick` from the `MessageContext` */
|
|
10
|
-
onClick?: ReactEventHandler;
|
|
11
8
|
/** An array of the own reaction objects to distinguish own reactions visually */
|
|
12
9
|
own_reactions?: ReactionResponse<StreamChatGenerics>[];
|
|
13
10
|
/**
|
|
@@ -17,7 +14,10 @@ export type ReactionsListProps<StreamChatGenerics extends DefaultStreamChatGener
|
|
|
17
14
|
reaction_counts?: Record<string, number>;
|
|
18
15
|
/** An object containing summary for each reaction type on a message */
|
|
19
16
|
reaction_groups?: Record<string, ReactionGroupResponse>;
|
|
20
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated
|
|
19
|
+
* A list of the currently supported reactions on a message
|
|
20
|
+
* */
|
|
21
21
|
reactionOptions?: ReactionOptions;
|
|
22
22
|
/** An array of the reaction objects to display in the list */
|
|
23
23
|
reactions?: ReactionResponse<StreamChatGenerics>[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react';
|
|
2
2
|
import { useComponentContext, useMessageContext } from '../../../context';
|
|
3
|
+
import { defaultReactionOptions } from '../reactionOptions';
|
|
3
4
|
export const defaultReactionsSort = (a, b) => {
|
|
4
5
|
if (a.firstReactionAt && b.firstReactionAt) {
|
|
5
6
|
return +a.firstReactionAt - +b.firstReactionAt;
|
|
@@ -9,7 +10,7 @@ export const defaultReactionsSort = (a, b) => {
|
|
|
9
10
|
export const useProcessReactions = (params) => {
|
|
10
11
|
const { own_reactions: propOwnReactions, reaction_groups: propReactionGroups, reactionOptions: propReactionOptions, reactions: propReactions, sortReactions: propSortReactions, } = params;
|
|
11
12
|
const { message, sortReactions: contextSortReactions } = useMessageContext('useProcessReactions');
|
|
12
|
-
const { reactionOptions: contextReactionOptions } = useComponentContext('useProcessReactions');
|
|
13
|
+
const { reactionOptions: contextReactionOptions = defaultReactionOptions, } = useComponentContext('useProcessReactions');
|
|
13
14
|
const reactionOptions = propReactionOptions ?? contextReactionOptions;
|
|
14
15
|
const sortReactions = propSortReactions ?? contextSortReactions ?? defaultReactionsSort;
|
|
15
16
|
const latestReactions = propReactions || message.latest_reactions;
|
|
@@ -6,19 +6,32 @@ import { MessageList, VirtualizedMessageList, } from '../MessageList';
|
|
|
6
6
|
import { ThreadHeader as DefaultThreadHeader } from './ThreadHeader';
|
|
7
7
|
import { ThreadHead as DefaultThreadHead } from '../Thread/ThreadHead';
|
|
8
8
|
import { useChannelActionContext, useChannelStateContext, useChatContext, useComponentContext, } from '../../context';
|
|
9
|
+
import { useThreadContext } from '../Threads';
|
|
10
|
+
import { useStateStore } from '../../store';
|
|
9
11
|
/**
|
|
10
12
|
* The Thread component renders a parent Message with a list of replies
|
|
11
13
|
*/
|
|
12
14
|
export const Thread = (props) => {
|
|
13
15
|
const { channel, channelConfig, thread } = useChannelStateContext('Thread');
|
|
14
|
-
|
|
16
|
+
const threadInstance = useThreadContext();
|
|
17
|
+
if ((!thread && !threadInstance) || channelConfig?.replies === false)
|
|
15
18
|
return null;
|
|
16
|
-
//
|
|
17
|
-
return
|
|
19
|
+
// the wrapper ensures a key variable is set and the component recreates on thread switch
|
|
20
|
+
return (
|
|
21
|
+
// FIXME: TS is having trouble here as at least one of the two would always be defined
|
|
22
|
+
React.createElement(ThreadInner, { ...props, key: `thread-${(thread ?? threadInstance)?.id}-${channel?.cid}` }));
|
|
18
23
|
};
|
|
24
|
+
const selector = (nextValue) => [
|
|
25
|
+
nextValue.replies,
|
|
26
|
+
nextValue.pagination.isLoadingPrev,
|
|
27
|
+
nextValue.pagination.isLoadingNext,
|
|
28
|
+
nextValue.parentMessage,
|
|
29
|
+
];
|
|
19
30
|
const ThreadInner = (props) => {
|
|
20
31
|
const { additionalMessageInputProps, additionalMessageListProps, additionalParentMessageProps, additionalVirtualizedMessageListProps, autoFocus = true, enableDateSeparator = false, Input: PropInput, Message: PropMessage, messageActions = Object.keys(MESSAGE_ACTIONS), virtualized, } = props;
|
|
21
|
-
const
|
|
32
|
+
const threadInstance = useThreadContext();
|
|
33
|
+
const [latestReplies, isLoadingPrev, isLoadingNext, parentMessage] = useStateStore(threadInstance?.state, selector) ?? [];
|
|
34
|
+
const { thread, threadHasMore, threadLoadingMore, threadMessages = [], threadSuppressAutoscroll, } = useChannelStateContext('Thread');
|
|
22
35
|
const { closeThread, loadMoreThread } = useChannelActionContext('Thread');
|
|
23
36
|
const { customClasses } = useChatContext('Thread');
|
|
24
37
|
const { ThreadInput: ContextInput, Message: ContextMessage, ThreadHead = DefaultThreadHead, ThreadHeader = DefaultThreadHeader, VirtualMessage, } = useComponentContext('Thread');
|
|
@@ -33,16 +46,31 @@ const ThreadInner = (props) => {
|
|
|
33
46
|
loadMoreThread();
|
|
34
47
|
}
|
|
35
48
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
|
-
}, []);
|
|
37
|
-
|
|
49
|
+
}, [thread, loadMoreThread]);
|
|
50
|
+
const threadProps = threadInstance
|
|
51
|
+
? {
|
|
52
|
+
loadingMore: isLoadingPrev,
|
|
53
|
+
loadingMoreNewer: isLoadingNext,
|
|
54
|
+
loadMore: threadInstance.loadPrevPage,
|
|
55
|
+
loadMoreNewer: threadInstance.loadNextPage,
|
|
56
|
+
messages: latestReplies,
|
|
57
|
+
}
|
|
58
|
+
: {
|
|
59
|
+
hasMore: threadHasMore,
|
|
60
|
+
loadingMore: threadLoadingMore,
|
|
61
|
+
loadMore: loadMoreThread,
|
|
62
|
+
messages: threadMessages,
|
|
63
|
+
};
|
|
64
|
+
const messageAsThread = thread ?? parentMessage;
|
|
65
|
+
if (!messageAsThread)
|
|
38
66
|
return null;
|
|
39
67
|
const threadClass = customClasses?.thread ||
|
|
40
68
|
clsx('str-chat__thread-container str-chat__thread', {
|
|
41
69
|
'str-chat__thread--virtualized': virtualized,
|
|
42
70
|
});
|
|
43
|
-
const head = (React.createElement(ThreadHead, { key:
|
|
71
|
+
const head = (React.createElement(ThreadHead, { key: messageAsThread.id, message: messageAsThread, Message: MessageUIComponent, ...additionalParentMessageProps }));
|
|
44
72
|
return (React.createElement("div", { className: threadClass },
|
|
45
|
-
React.createElement(ThreadHeader, { closeThread: closeThread, thread:
|
|
46
|
-
React.createElement(ThreadMessageList, { disableDateSeparator: !enableDateSeparator,
|
|
47
|
-
React.createElement(MessageInput, { focus: autoFocus, Input: ThreadInput, parent: thread, publishTypingEvent: false, ...additionalMessageInputProps })));
|
|
73
|
+
React.createElement(ThreadHeader, { closeThread: closeThread, thread: messageAsThread }),
|
|
74
|
+
React.createElement(ThreadMessageList, { disableDateSeparator: !enableDateSeparator, head: head, Message: MessageUIComponent, messageActions: messageActions, suppressAutoscroll: threadSuppressAutoscroll, threadList: true, ...threadProps, ...(virtualized ? additionalVirtualizedMessageListProps : additionalMessageListProps) }),
|
|
75
|
+
React.createElement(MessageInput, { focus: autoFocus, Input: ThreadInput, parent: thread ?? parentMessage, publishTypingEvent: false, ...additionalMessageInputProps })));
|
|
48
76
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PropsWithChildren } from 'react';
|
|
3
|
+
import { Thread } from 'stream-chat';
|
|
4
|
+
export type ThreadContextValue = Thread | undefined;
|
|
5
|
+
export declare const ThreadContext: React.Context<ThreadContextValue>;
|
|
6
|
+
export declare const useThreadContext: () => Thread<import("stream-chat").DefaultGenerics> | undefined;
|
|
7
|
+
export declare const ThreadProvider: ({ children, thread }: PropsWithChildren<{
|
|
8
|
+
thread?: Thread;
|
|
9
|
+
}>) => React.JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
|
+
import { Channel } from '../../components';
|
|
3
|
+
export const ThreadContext = createContext(undefined);
|
|
4
|
+
export const useThreadContext = () => {
|
|
5
|
+
const thread = useContext(ThreadContext);
|
|
6
|
+
return thread ?? undefined;
|
|
7
|
+
};
|
|
8
|
+
export const ThreadProvider = ({ children, thread }) => (React.createElement(ThreadContext.Provider, { value: thread },
|
|
9
|
+
React.createElement(Channel, { channel: thread?.channel }, children)));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { VirtuosoProps } from 'react-virtuoso';
|
|
3
|
+
import type { Thread } from 'stream-chat';
|
|
4
|
+
type ThreadListProps = {
|
|
5
|
+
virtuosoProps?: VirtuosoProps<Thread, unknown>;
|
|
6
|
+
};
|
|
7
|
+
export declare const useThreadList: () => void;
|
|
8
|
+
export declare const ThreadList: ({ virtuosoProps }: ThreadListProps) => React.JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Virtuoso } from 'react-virtuoso';
|
|
3
|
+
import { ThreadListItem as DefaultThreadListItem } from './ThreadListItem';
|
|
4
|
+
import { ThreadListEmptyPlaceholder as DefaultThreadListEmptyPlaceholder } from './ThreadListEmptyPlaceholder';
|
|
5
|
+
import { ThreadListUnseenThreadsBanner as DefaultThreadListUnseenThreadsBanner } from './ThreadListUnseenThreadsBanner';
|
|
6
|
+
import { ThreadListLoadingIndicator as DefaultThreadListLoadingIndicator } from './ThreadListLoadingIndicator';
|
|
7
|
+
import { useChatContext, useComponentContext } from '../../../context';
|
|
8
|
+
import { useStateStore } from '../../../store';
|
|
9
|
+
const selector = (nextValue) => [nextValue.threads];
|
|
10
|
+
const computeItemKey = (_, item) => item.id;
|
|
11
|
+
export const useThreadList = () => {
|
|
12
|
+
const { client } = useChatContext();
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const handleVisibilityChange = () => {
|
|
15
|
+
if (document.visibilityState === 'visible') {
|
|
16
|
+
client.threads.activate();
|
|
17
|
+
}
|
|
18
|
+
if (document.visibilityState === 'hidden') {
|
|
19
|
+
client.threads.deactivate();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
handleVisibilityChange();
|
|
23
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
24
|
+
return () => {
|
|
25
|
+
client.threads.deactivate();
|
|
26
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
27
|
+
};
|
|
28
|
+
}, [client]);
|
|
29
|
+
};
|
|
30
|
+
export const ThreadList = ({ virtuosoProps }) => {
|
|
31
|
+
const { client } = useChatContext();
|
|
32
|
+
const { ThreadListItem = DefaultThreadListItem, ThreadListEmptyPlaceholder = DefaultThreadListEmptyPlaceholder, ThreadListLoadingIndicator = DefaultThreadListLoadingIndicator, ThreadListUnseenThreadsBanner = DefaultThreadListUnseenThreadsBanner, } = useComponentContext();
|
|
33
|
+
const [threads] = useStateStore(client.threads.state, selector);
|
|
34
|
+
useThreadList();
|
|
35
|
+
return (React.createElement("div", { className: 'str-chat__thread-list-container' },
|
|
36
|
+
React.createElement(ThreadListUnseenThreadsBanner, null),
|
|
37
|
+
React.createElement(Virtuoso, { atBottomStateChange: (atBottom) => atBottom && client.threads.loadNextPage(), className: 'str-chat__thread-list', components: {
|
|
38
|
+
EmptyPlaceholder: ThreadListEmptyPlaceholder,
|
|
39
|
+
Footer: ThreadListLoadingIndicator,
|
|
40
|
+
}, computeItemKey: computeItemKey, data: threads, itemContent: (_, thread) => React.createElement(ThreadListItem, { thread: thread }), ...virtuosoProps })));
|
|
41
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Icon } from '../icons';
|
|
3
|
+
export const ThreadListEmptyPlaceholder = () => (React.createElement("div", { className: 'str-chat__thread-list-empty-placeholder' },
|
|
4
|
+
React.createElement(Icon.MessageBubble, null),
|
|
5
|
+
"No threads here yet..."));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Thread } from 'stream-chat';
|
|
3
|
+
import type { ThreadListItemUIProps } from './ThreadListItemUI';
|
|
4
|
+
export type ThreadListItemProps = {
|
|
5
|
+
thread: Thread;
|
|
6
|
+
threadListItemUIProps?: ThreadListItemUIProps;
|
|
7
|
+
};
|
|
8
|
+
export declare const useThreadListItemContext: () => Thread<import("stream-chat").DefaultGenerics> | undefined;
|
|
9
|
+
export declare const ThreadListItem: ({ thread, threadListItemUIProps }: ThreadListItemProps) => React.JSX.Element;
|