stream-chat-react 12.0.0-rc.10 → 12.0.0-rc.12

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.
Files changed (89) hide show
  1. package/dist/components/Avatar/Avatar.js +5 -1
  2. package/dist/components/Channel/Channel.d.ts +3 -4
  3. package/dist/components/Channel/Channel.js +76 -24
  4. package/dist/components/Chat/hooks/useChat.js +10 -6
  5. package/dist/components/ChatView/ChatView.d.ts +18 -0
  6. package/dist/components/ChatView/ChatView.js +100 -0
  7. package/dist/components/ChatView/index.d.ts +1 -0
  8. package/dist/components/ChatView/index.js +1 -0
  9. package/dist/components/Message/Message.js +2 -1
  10. package/dist/components/Message/MessageOptions.js +3 -4
  11. package/dist/components/Message/MessageSimple.js +2 -1
  12. package/dist/components/Message/QuotedMessage.js +2 -1
  13. package/dist/components/Message/hooks/useReactionHandler.js +7 -0
  14. package/dist/components/Message/utils.d.ts +10 -1
  15. package/dist/components/Message/utils.js +16 -7
  16. package/dist/components/MessageActions/MessageActions.js +14 -9
  17. package/dist/components/MessageInput/MessageInputFlat.js +2 -2
  18. package/dist/components/MessageInput/QuotedMessagePreview.js +2 -1
  19. package/dist/components/MessageList/MessageList.js +1 -3
  20. package/dist/components/MessageList/VirtualizedMessageList.d.ts +2 -1
  21. package/dist/components/MessageList/VirtualizedMessageList.js +5 -2
  22. package/dist/components/MessageList/VirtualizedMessageListComponents.d.ts +1 -1
  23. package/dist/components/MessageList/VirtualizedMessageListComponents.js +6 -6
  24. package/dist/components/MessageList/renderMessages.d.ts +2 -2
  25. package/dist/components/MessageList/renderMessages.js +4 -1
  26. package/dist/components/Reactions/ReactionSelector.d.ts +5 -2
  27. package/dist/components/Reactions/ReactionSelector.js +2 -1
  28. package/dist/components/Reactions/ReactionsList.d.ts +4 -1
  29. package/dist/components/Reactions/hooks/useProcessReactions.js +2 -1
  30. package/dist/components/Thread/Thread.js +37 -10
  31. package/dist/components/Threads/ThreadContext.d.ts +9 -0
  32. package/dist/components/Threads/ThreadContext.js +9 -0
  33. package/dist/components/Threads/ThreadList/ThreadList.d.ts +9 -0
  34. package/dist/components/Threads/ThreadList/ThreadList.js +41 -0
  35. package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.d.ts +2 -0
  36. package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.js +5 -0
  37. package/dist/components/Threads/ThreadList/ThreadListItem.d.ts +9 -0
  38. package/dist/components/Threads/ThreadList/ThreadListItem.js +52 -0
  39. package/dist/components/Threads/ThreadList/ThreadListItemUI.d.ts +18 -0
  40. package/dist/components/Threads/ThreadList/ThreadListItemUI.js +76 -0
  41. package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.d.ts +2 -0
  42. package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.js +14 -0
  43. package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.d.ts +2 -0
  44. package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.js +16 -0
  45. package/dist/components/Threads/ThreadList/index.d.ts +3 -0
  46. package/dist/components/Threads/ThreadList/index.js +3 -0
  47. package/dist/components/Threads/UnreadCountBadge.d.ts +6 -0
  48. package/dist/components/Threads/UnreadCountBadge.js +5 -0
  49. package/dist/components/Threads/hooks/useStateStore.d.ts +3 -0
  50. package/dist/components/Threads/hooks/useStateStore.js +15 -0
  51. package/dist/components/Threads/hooks/useThreadManagerState.d.ts +2 -0
  52. package/dist/components/Threads/hooks/useThreadManagerState.js +6 -0
  53. package/dist/components/Threads/hooks/useThreadState.d.ts +5 -0
  54. package/dist/components/Threads/hooks/useThreadState.js +11 -0
  55. package/dist/components/Threads/icons.d.ts +8 -0
  56. package/dist/components/Threads/icons.js +13 -0
  57. package/dist/components/Threads/index.d.ts +3 -0
  58. package/dist/components/Threads/index.js +3 -0
  59. package/dist/components/index.d.ts +2 -0
  60. package/dist/components/index.js +2 -0
  61. package/dist/context/ComponentContext.d.ts +15 -40
  62. package/dist/context/ComponentContext.js +7 -9
  63. package/dist/context/MessageContext.d.ts +1 -1
  64. package/dist/context/MessageContext.js +3 -2
  65. package/dist/context/WithComponents.d.ts +5 -0
  66. package/dist/context/WithComponents.js +7 -0
  67. package/dist/context/index.d.ts +1 -0
  68. package/dist/context/index.js +1 -0
  69. package/dist/css/v2/index.css +2 -2
  70. package/dist/css/v2/index.layout.css +2 -2
  71. package/dist/index.browser.cjs +6456 -5951
  72. package/dist/index.browser.cjs.map +4 -4
  73. package/dist/index.node.cjs +6390 -5870
  74. package/dist/index.node.cjs.map +4 -4
  75. package/dist/scss/v2/Avatar/Avatar-layout.scss +10 -2
  76. package/dist/scss/v2/Avatar/Avatar-theme.scss +5 -0
  77. package/dist/scss/v2/ChatView/ChatView-layout.scss +43 -0
  78. package/dist/scss/v2/ChatView/ChatView-theme.scss +31 -0
  79. package/dist/scss/v2/LoadingIndicator/LoadingIndicator-layout.scss +16 -0
  80. package/dist/scss/v2/MessageList/MessageList-layout.scss +0 -6
  81. package/dist/scss/v2/MessageList/VirtualizedMessageList-layout.scss +0 -12
  82. package/dist/scss/v2/Thread/Thread-layout.scss +15 -1
  83. package/dist/scss/v2/ThreadList/ThreadList-layout.scss +149 -0
  84. package/dist/scss/v2/ThreadList/ThreadList-theme.scss +74 -0
  85. package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-layout.scss +49 -0
  86. package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-theme.scss +10 -0
  87. package/dist/scss/v2/index.layout.scss +3 -0
  88. package/dist/scss/v2/index.scss +3 -0
  89. package/package.json +4 -4
@@ -20,7 +20,7 @@ import { useMessageInputContext } from '../../context/MessageInputContext';
20
20
  import { useComponentContext } from '../../context/ComponentContext';
21
21
  export const MessageInputFlat = () => {
22
22
  const { t } = useTranslationContext('MessageInputFlat');
23
- const { asyncMessagesMultiSendEnabled, attachments, cooldownRemaining, findAndEnqueueURLsToEnrich, handleSubmit, hideSendButton, isUploadEnabled, linkPreviews, maxFilesLeft, message, numberOfUploads, recordingController, setCooldownRemaining, text, uploadNewFiles, } = useMessageInputContext('MessageInputFlat');
23
+ const { asyncMessagesMultiSendEnabled, attachments, cooldownRemaining, findAndEnqueueURLsToEnrich, handleSubmit, hideSendButton, isUploadEnabled, linkPreviews, maxFilesLeft, message, numberOfUploads, parent, recordingController, setCooldownRemaining, text, uploadNewFiles, } = useMessageInputContext('MessageInputFlat');
24
24
  const { AudioRecorder = DefaultAudioRecorder, AttachmentPreviewList = DefaultAttachmentPreviewList, CooldownTimer = DefaultCooldownTimer, FileUploadIcon = DefaultUploadIcon, LinkPreviewList = DefaultLinkPreviewList, QuotedMessagePreview = DefaultQuotedMessagePreview, RecordingPermissionDeniedNotification = DefaultRecordingPermissionDeniedNotification, SendButton = DefaultSendButton, StartRecordingAudioButton = DefaultStartRecordingAudioButton, EmojiPicker, } = useComponentContext('MessageInputFlat');
25
25
  const { acceptedFiles = [], multipleUploads, quotedMessage, } = useChannelStateContext('MessageInputFlat');
26
26
  const { setQuotedMessage } = useChannelActionContext('MessageInputFlat');
@@ -64,7 +64,7 @@ export const MessageInputFlat = () => {
64
64
  return React.createElement(AudioRecorder, null);
65
65
  // TODO: "!message" condition is a temporary fix for shared
66
66
  // state when editing a message (fix shared state issue)
67
- const displayQuotedMessage = !message && quotedMessage && !quotedMessage.parent_id;
67
+ const displayQuotedMessage = !message && quotedMessage && quotedMessage.parent_id === parent?.id;
68
68
  const recordingEnabled = !!(recordingController.recorder && navigator.mediaDevices); // account for requirement on iOS as per this bug report: https://bugs.webkit.org/show_bug.cgi?id=252303
69
69
  const isRecording = !!recordingController.recordingState;
70
70
  return (React.createElement(React.Fragment, null,
@@ -1,4 +1,5 @@
1
1
  import React, { useMemo } from 'react';
2
+ import { Attachment as DefaultAttachment } from '../Attachment';
2
3
  import { Avatar as DefaultAvatar } from '../Avatar';
3
4
  import { CloseIcon } from './icons';
4
5
  import { useChannelActionContext } from '../../context/ChannelActionContext';
@@ -13,7 +14,7 @@ export const QuotedMessagePreviewHeader = () => {
13
14
  React.createElement(CloseIcon, null))));
14
15
  };
15
16
  export const QuotedMessagePreview = ({ quotedMessage, }) => {
16
- const { Attachment, Avatar = DefaultAvatar } = useComponentContext('QuotedMessagePreview');
17
+ const { Attachment = DefaultAttachment, Avatar = DefaultAvatar, } = useComponentContext('QuotedMessagePreview');
17
18
  const { userLanguage } = useTranslationContext('QuotedMessagePreview');
18
19
  const quotedMessageText = quotedMessage.i18n?.[`${userLanguage}_text`] ||
19
20
  quotedMessage.text;
@@ -127,9 +127,7 @@ const MessageListWithContext = (props) => {
127
127
  return (React.createElement(MessageListContextProvider, { value: { listElement, scrollToBottom } },
128
128
  React.createElement(MessageListMainPanel, null,
129
129
  !threadList && showUnreadMessagesNotification && (React.createElement(UnreadMessagesNotification, { unreadCount: channelUnreadUiState?.unread_messages })),
130
- React.createElement("div", { className: clsx(messageListClass, {
131
- [customClasses?.threadList || 'str-chat__thread-list']: threadList,
132
- }), onScroll: onScroll, ref: setListElement, tabIndex: 0 }, showEmptyStateIndicator ? (React.createElement(EmptyStateIndicator, { key: 'empty-state-indicator', listType: threadList ? 'thread' : 'message' })) : (React.createElement(InfiniteScroll, { className: 'str-chat__message-list-scroll', "data-testid": 'reverse-infinite-scroll', hasNextPage: props.hasMoreNewer, hasPreviousPage: props.hasMore, head: props.head, isLoading: props.loadingMore, loader: React.createElement("div", { className: 'str-chat__list__loading', key: 'loading-indicator' }, props.loadingMore && React.createElement(LoadingIndicator, { size: 20 })), loadNextPage: loadMoreNewer, loadPreviousPage: loadMore, threshold: loadMoreScrollThreshold, ...restInternalInfiniteScrollProps },
130
+ React.createElement("div", { className: clsx(messageListClass, customClasses?.threadList), onScroll: onScroll, ref: setListElement, tabIndex: 0 }, showEmptyStateIndicator ? (React.createElement(EmptyStateIndicator, { listType: threadList ? 'thread' : 'message' })) : (React.createElement(InfiniteScroll, { className: 'str-chat__message-list-scroll', "data-testid": 'reverse-infinite-scroll', hasNextPage: props.hasMoreNewer, hasPreviousPage: props.hasMore, head: props.head, isLoading: props.loadingMore, loader: React.createElement("div", { className: 'str-chat__list__loading', key: 'loading-indicator' }, props.loadingMore && React.createElement(LoadingIndicator, { size: 20 })), loadNextPage: loadMoreNewer, loadPreviousPage: loadMore, threshold: loadMoreScrollThreshold, ...restInternalInfiniteScrollProps },
133
131
  React.createElement("ul", { className: 'str-chat__ul', ref: setUlElement }, elements),
134
132
  React.createElement(TypingIndicator, { threadList: threadList }),
135
133
  React.createElement("div", { key: 'bottom' }))))),
@@ -3,7 +3,7 @@ import { ScrollSeekConfiguration, ScrollSeekPlaceholderProps, VirtuosoHandle, Vi
3
3
  import { GroupStyle, ProcessMessagesParams } from './utils';
4
4
  import { MessageProps, MessageUIComponentProps } from '../Message';
5
5
  import { ChannelActionContextValue } from '../../context/ChannelActionContext';
6
- import { StreamMessage } from '../../context/ChannelStateContext';
6
+ import { ChannelStateContextValue, StreamMessage } from '../../context/ChannelStateContext';
7
7
  import { ChatContextValue } from '../../context/ChatContext';
8
8
  import { ComponentContextValue } from '../../context/ComponentContext';
9
9
  import type { UserResponse } from 'stream-chat';
@@ -40,6 +40,7 @@ type PropsDrilledToMessage = 'additionalMessageInputProps' | 'customMessageActio
40
40
  export type VirtualizedMessageListProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Partial<Pick<MessageProps<StreamChatGenerics>, PropsDrilledToMessage>> & {
41
41
  /** Additional props to be passed the underlying [`react-virtuoso` virtualized list dependency](https://virtuoso.dev/virtuoso-api-reference/) */
42
42
  additionalVirtuosoProps?: VirtuosoProps<UnknownType, VirtuosoContext<StreamChatGenerics>>;
43
+ channelUnreadUiState?: ChannelStateContextValue['channelUnreadUiState'];
43
44
  /** If true, picking a reaction from the `ReactionSelector` component will close the selector */
44
45
  closeReactionSelectorOnClick?: boolean;
45
46
  /** Custom render function, if passed, certain UI props are ignored */
@@ -11,6 +11,9 @@ 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';
14
17
  import { useChannelActionContext, } from '../../context/ChannelActionContext';
15
18
  import { useChannelStateContext, } from '../../context/ChannelStateContext';
16
19
  import { useChatContext } from '../../context/ChatContext';
@@ -53,7 +56,7 @@ const VirtualizedMessageListWithContext = (props) => {
53
56
  // Stops errors generated from react-virtuoso to bubble up
54
57
  // to Sentry or other tracking tools.
55
58
  useCaptureResizeObserverExceededError();
56
- const { DateSeparator, GiphyPreviewMessage = DefaultGiphyPreviewMessage, MessageListNotifications = DefaultMessageListNotifications, MessageNotification = DefaultMessageNotification, MessageSystem, UnreadMessagesNotification = DefaultUnreadMessagesNotification, UnreadMessagesSeparator, VirtualMessage: MessageUIComponentFromContext = MessageSimple, TypingIndicator, } = useComponentContext('VirtualizedMessageList');
59
+ const { DateSeparator = DefaultDateSeparator, GiphyPreviewMessage = DefaultGiphyPreviewMessage, MessageListNotifications = DefaultMessageListNotifications, MessageNotification = DefaultMessageNotification, MessageSystem = DefaultMessageSystem, UnreadMessagesNotification = DefaultUnreadMessagesNotification, UnreadMessagesSeparator = DefaultUnreadMessagesSeparator, VirtualMessage: MessageUIComponentFromContext = MessageSimple, TypingIndicator, } = useComponentContext('VirtualizedMessageList');
57
60
  const MessageUIComponent = MessageUIComponentFromProps || MessageUIComponentFromContext;
58
61
  const { client, customClasses } = useChatContext('VirtualizedMessageList');
59
62
  const virtuoso = useRef(null);
@@ -236,5 +239,5 @@ export function VirtualizedMessageList(props) {
236
239
  const { jumpToLatestMessage, loadMore, loadMoreNewer, } = useChannelActionContext('VirtualizedMessageList');
237
240
  const { channel, channelUnreadUiState, hasMore, hasMoreNewer, highlightedMessageId, loadingMore, loadingMoreNewer, messages: contextMessages, notifications, read, suppressAutoscroll, } = useChannelStateContext('VirtualizedMessageList');
238
241
  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 }));
242
+ 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
243
  }
@@ -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 | null;
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
- if (!context?.loadingMore)
41
- return null;
42
- return LoadingIndicator ? (React.createElement("div", { className: 'str-chat__virtual-list__loading' },
43
- React.createElement(LoadingIndicator, { size: 20 }))) : (context?.head || null);
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
  };
@@ -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,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
- /** A list of the currently supported reactions on a message */
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;
@@ -4,9 +4,10 @@ import { isMutableRef } from './utils/utils';
4
4
  import { Avatar as DefaultAvatar } from '../Avatar';
5
5
  import { useComponentContext } from '../../context/ComponentContext';
6
6
  import { useMessageContext } from '../../context/MessageContext';
7
+ import { defaultReactionOptions } from './reactionOptions';
7
8
  const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
8
9
  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 { Avatar: contextAvatar, reactionOptions: contextReactionOptions = defaultReactionOptions, } = useComponentContext('ReactionSelector');
10
11
  const { handleReaction: contextHandleReaction, message, } = useMessageContext('ReactionSelector');
11
12
  const reactionOptions = propReactionOptions ?? contextReactionOptions;
12
13
  const Avatar = propAvatar || contextAvatar || DefaultAvatar;
@@ -17,7 +17,10 @@ export type ReactionsListProps<StreamChatGenerics extends DefaultStreamChatGener
17
17
  reaction_counts?: Record<string, number>;
18
18
  /** An object containing summary for each reaction type on a message */
19
19
  reaction_groups?: Record<string, ReactionGroupResponse>;
20
- /** A list of the currently supported reactions on a message */
20
+ /**
21
+ * @deprecated
22
+ * A list of the currently supported reactions on a message
23
+ * */
21
24
  reactionOptions?: ReactionOptions;
22
25
  /** An array of the reaction objects to display in the list */
23
26
  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,31 @@ 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 { useStateStore, useThreadContext } from '../../components/Threads';
9
10
  /**
10
11
  * The Thread component renders a parent Message with a list of replies
11
12
  */
12
13
  export const Thread = (props) => {
13
14
  const { channel, channelConfig, thread } = useChannelStateContext('Thread');
14
- if (!thread || channelConfig?.replies === false)
15
+ const threadInstance = useThreadContext();
16
+ if ((!thread && !threadInstance) || channelConfig?.replies === false)
15
17
  return null;
16
- // The wrapper ensures a key variable is set and the component recreates on thread switch
17
- return React.createElement(ThreadInner, { ...props, key: `thread-${thread.id}-${channel?.cid}` });
18
+ // the wrapper ensures a key variable is set and the component recreates on thread switch
19
+ return (
20
+ // FIXME: TS is having trouble here as at least one of the two would always be defined
21
+ React.createElement(ThreadInner, { ...props, key: `thread-${(thread ?? threadInstance)?.id}-${channel?.cid}` }));
18
22
  };
23
+ const selector = (nextValue) => [
24
+ nextValue.replies,
25
+ nextValue.pagination.isLoadingPrev,
26
+ nextValue.pagination.isLoadingNext,
27
+ nextValue.parentMessage,
28
+ ];
19
29
  const ThreadInner = (props) => {
20
30
  const { additionalMessageInputProps, additionalMessageListProps, additionalParentMessageProps, additionalVirtualizedMessageListProps, autoFocus = true, enableDateSeparator = false, Input: PropInput, Message: PropMessage, messageActions = Object.keys(MESSAGE_ACTIONS), virtualized, } = props;
21
- const { thread, threadHasMore, threadLoadingMore, threadMessages, threadSuppressAutoscroll, } = useChannelStateContext('Thread');
31
+ const threadInstance = useThreadContext();
32
+ const [latestReplies, isLoadingPrev, isLoadingNext, parentMessage] = useStateStore(threadInstance?.state, selector) ?? [];
33
+ const { thread, threadHasMore, threadLoadingMore, threadMessages = [], threadSuppressAutoscroll, } = useChannelStateContext('Thread');
22
34
  const { closeThread, loadMoreThread } = useChannelActionContext('Thread');
23
35
  const { customClasses } = useChatContext('Thread');
24
36
  const { ThreadInput: ContextInput, Message: ContextMessage, ThreadHead = DefaultThreadHead, ThreadHeader = DefaultThreadHeader, VirtualMessage, } = useComponentContext('Thread');
@@ -33,16 +45,31 @@ const ThreadInner = (props) => {
33
45
  loadMoreThread();
34
46
  }
35
47
  // eslint-disable-next-line react-hooks/exhaustive-deps
36
- }, []);
37
- if (!thread)
48
+ }, [thread, loadMoreThread]);
49
+ const threadProps = threadInstance
50
+ ? {
51
+ loadingMore: isLoadingPrev,
52
+ loadingMoreNewer: isLoadingNext,
53
+ loadMore: threadInstance.loadPrevPage,
54
+ loadMoreNewer: threadInstance.loadNextPage,
55
+ messages: latestReplies,
56
+ }
57
+ : {
58
+ hasMore: threadHasMore,
59
+ loadingMore: threadLoadingMore,
60
+ loadMore: loadMoreThread,
61
+ messages: threadMessages,
62
+ };
63
+ const messageAsThread = thread ?? parentMessage;
64
+ if (!messageAsThread)
38
65
  return null;
39
66
  const threadClass = customClasses?.thread ||
40
67
  clsx('str-chat__thread-container str-chat__thread', {
41
68
  'str-chat__thread--virtualized': virtualized,
42
69
  });
43
- const head = (React.createElement(ThreadHead, { key: thread.id, message: thread, Message: MessageUIComponent, ...additionalParentMessageProps }));
70
+ const head = (React.createElement(ThreadHead, { key: messageAsThread.id, message: messageAsThread, Message: MessageUIComponent, ...additionalParentMessageProps }));
44
71
  return (React.createElement("div", { className: threadClass },
45
- React.createElement(ThreadHeader, { closeThread: closeThread, thread: thread }),
46
- React.createElement(ThreadMessageList, { disableDateSeparator: !enableDateSeparator, hasMore: threadHasMore, head: head, loadingMore: threadLoadingMore, loadMore: loadMoreThread, Message: MessageUIComponent, messageActions: messageActions, messages: threadMessages || [], suppressAutoscroll: threadSuppressAutoscroll, threadList: true, ...(virtualized ? additionalVirtualizedMessageListProps : additionalMessageListProps) }),
47
- React.createElement(MessageInput, { focus: autoFocus, Input: ThreadInput, parent: thread, publishTypingEvent: false, ...additionalMessageInputProps })));
72
+ React.createElement(ThreadHeader, { closeThread: closeThread, thread: messageAsThread }),
73
+ React.createElement(ThreadMessageList, { disableDateSeparator: !enableDateSeparator, head: head, Message: MessageUIComponent, messageActions: messageActions, suppressAutoscroll: threadSuppressAutoscroll, threadList: true, ...threadProps, ...(virtualized ? additionalVirtualizedMessageListProps : additionalMessageListProps) }),
74
+ React.createElement(MessageInput, { focus: autoFocus, Input: ThreadInput, parent: thread ?? parentMessage, publishTypingEvent: false, ...additionalMessageInputProps })));
48
75
  };
@@ -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 '../hooks/useStateStore';
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,2 @@
1
+ import React from 'react';
2
+ export declare const ThreadListEmptyPlaceholder: () => React.JSX.Element;
@@ -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;
@@ -0,0 +1,52 @@
1
+ import React, { createContext, useContext } from 'react';
2
+ import { useComponentContext } from '../../../context';
3
+ import { ThreadListItemUI as DefaultThreadListItemUI } from './ThreadListItemUI';
4
+ const ThreadListItemContext = createContext(undefined);
5
+ export const useThreadListItemContext = () => useContext(ThreadListItemContext);
6
+ export const ThreadListItem = ({ thread, threadListItemUIProps }) => {
7
+ const { ThreadListItemUI = DefaultThreadListItemUI } = useComponentContext();
8
+ return (React.createElement(ThreadListItemContext.Provider, { value: thread },
9
+ React.createElement(ThreadListItemUI, { ...threadListItemUIProps })));
10
+ };
11
+ // const App = () => {
12
+ // const route = useRouter();
13
+ // return (
14
+ // <Chat>
15
+ // {route === '/channels' && (
16
+ // <Channel>
17
+ // <MessageList />
18
+ // <Thread />
19
+ // </Channel>
20
+ // )}
21
+ // {route === '/threads' && (
22
+ // <Threads>
23
+ // <ThreadList />
24
+ // <ThreadProvider>
25
+ // <Thread />
26
+ // </ThreadProvider>
27
+ // </Threads>
28
+ // )}
29
+ // </Chat>
30
+ // );
31
+ // };
32
+ // pre-built layout
33
+ {
34
+ /*
35
+ <Chat client={chatClient}>
36
+ <Views>
37
+ // has default
38
+ <ViewSelector onItemPointerDown={} />
39
+ <View.Chat>
40
+ <Channel>
41
+ <MessageList />
42
+ <MessageInput />
43
+ </Channel>
44
+ </View.Chat>
45
+ <View.Thread> <-- activeThread state
46
+ <ThreadList /> <-- uses context for click handler
47
+ <WrappedThread /> <-- ThreadProvider + Channel combo
48
+ </View.Thread>
49
+ </Views>
50
+ </Chat>;
51
+ */
52
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { ComponentPropsWithoutRef } from 'react';
3
+ export type ThreadListItemUIProps = ComponentPropsWithoutRef<'button'>;
4
+ /**
5
+ * TODO:
6
+ * - maybe hover state? ask design
7
+ * - move styling to CSS library and clean it up (separate layout and theme)
8
+ * - use Moment/DayJs for proper created_at formatting (replace toLocaleTimeString)
9
+ * - handle deleted message [in progress]
10
+ */
11
+ export declare const attachmentTypeIconMap: {
12
+ readonly audio: "🔈";
13
+ readonly file: "📄";
14
+ readonly image: "📷";
15
+ readonly video: "🎥";
16
+ readonly voiceRecording: "🎙️";
17
+ };
18
+ export declare const ThreadListItemUI: (props: ThreadListItemUIProps) => React.JSX.Element;
@@ -0,0 +1,76 @@
1
+ import React, { useCallback } from 'react';
2
+ import clsx from 'clsx';
3
+ import { Timestamp } from '../../Message/Timestamp';
4
+ import { Avatar } from '../../Avatar';
5
+ import { Icon } from '../icons';
6
+ import { UnreadCountBadge } from '../UnreadCountBadge';
7
+ import { useChatContext } from '../../../context';
8
+ import { useThreadsViewContext } from '../../ChatView';
9
+ import { useThreadListItemContext } from './ThreadListItem';
10
+ import { useStateStore } from '../hooks/useStateStore';
11
+ /**
12
+ * TODO:
13
+ * - maybe hover state? ask design
14
+ * - move styling to CSS library and clean it up (separate layout and theme)
15
+ * - use Moment/DayJs for proper created_at formatting (replace toLocaleTimeString)
16
+ * - handle deleted message [in progress]
17
+ */
18
+ export const attachmentTypeIconMap = {
19
+ audio: '🔈',
20
+ file: '📄',
21
+ image: '📷',
22
+ video: '🎥',
23
+ voiceRecording: '🎙️',
24
+ };
25
+ // TODO: translations
26
+ const getTitleFromMessage = ({ currentUserId, message, }) => {
27
+ const attachment = message?.attachments?.at(0);
28
+ let attachmentIcon = '';
29
+ if (attachment) {
30
+ attachmentIcon +=
31
+ attachmentTypeIconMap[attachment.type ?? 'file'] ??
32
+ attachmentTypeIconMap.file;
33
+ }
34
+ const messageBelongsToCurrentUser = message?.user?.id === currentUserId;
35
+ if (message?.deleted_at && message.parent_id)
36
+ return clsx(messageBelongsToCurrentUser && 'You:', 'This reply was deleted.');
37
+ if (message?.deleted_at && !message.parent_id)
38
+ return clsx(messageBelongsToCurrentUser && 'You:', 'The source message was deleted.');
39
+ if (attachment?.type === 'voiceRecording')
40
+ return clsx(attachmentIcon, messageBelongsToCurrentUser && 'You:', 'Voice message');
41
+ return clsx(attachmentIcon, messageBelongsToCurrentUser && 'You:', message?.text || attachment?.fallback || 'N/A');
42
+ };
43
+ export const ThreadListItemUI = (props) => {
44
+ const { client } = useChatContext();
45
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
46
+ const thread = useThreadListItemContext();
47
+ const selector = useCallback((nextValue) => [
48
+ nextValue.replies.at(-1),
49
+ (client.userID && nextValue.read[client.userID]?.unreadMessageCount) || 0,
50
+ nextValue.parentMessage,
51
+ nextValue.channel,
52
+ nextValue.deletedAt,
53
+ ], [client]);
54
+ const [latestReply, ownUnreadMessageCount, parentMessage, channelData, deletedAt] = useStateStore(thread.state, selector);
55
+ const { activeThread, setActiveThread } = useThreadsViewContext();
56
+ const avatarProps = deletedAt ? null : latestReply?.user;
57
+ return (React.createElement("button", { "aria-selected": activeThread === thread, className: 'str-chat__thread-list-item', "data-thread-id": thread.id, onClick: () => setActiveThread(thread), role: 'option', ...props },
58
+ React.createElement("div", { className: 'str-chat__thread-list-item__channel' },
59
+ React.createElement(Icon.MessageBubble, null),
60
+ React.createElement("div", { className: 'str-chat__thread-list-item__channel-text' }, channelData.data?.name || 'N/A')),
61
+ React.createElement("div", { className: 'str-chat__thread-list-item__parent-message' },
62
+ React.createElement("div", { className: 'str-chat__thread-list-item__parent-message-text' },
63
+ "replied to: ",
64
+ getTitleFromMessage({ message: parentMessage })),
65
+ !deletedAt && React.createElement(UnreadCountBadge, { count: ownUnreadMessageCount })),
66
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply' },
67
+ React.createElement(Avatar, { ...avatarProps }),
68
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-details' },
69
+ !deletedAt && (React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-created-by' }, latestReply?.user?.name || latestReply?.user?.id || 'Unknown sender')),
70
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-text-and-timestamp' },
71
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-text' }, deletedAt
72
+ ? 'This thread was deleted'
73
+ : getTitleFromMessage({ currentUserId: client.user?.id, message: latestReply })),
74
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-timestamp' },
75
+ React.createElement(Timestamp, { timestamp: deletedAt ?? latestReply?.created_at })))))));
76
+ };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const ThreadListLoadingIndicator: () => React.JSX.Element | null;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { LoadingIndicator as DefaultLoadingIndicator } from '../../Loading';
3
+ import { useChatContext, useComponentContext } from '../../../context';
4
+ import { useStateStore } from '../hooks/useStateStore';
5
+ const selector = (nextValue) => [nextValue.pagination.isLoadingNext];
6
+ export const ThreadListLoadingIndicator = () => {
7
+ const { LoadingIndicator = DefaultLoadingIndicator } = useComponentContext();
8
+ const { client } = useChatContext();
9
+ const [isLoadingNext] = useStateStore(client.threads.state, selector);
10
+ if (!isLoadingNext)
11
+ return null;
12
+ return (React.createElement("div", { className: 'str-chat__thread-list-loading-indicator' },
13
+ React.createElement(LoadingIndicator, null)));
14
+ };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const ThreadListUnseenThreadsBanner: () => React.JSX.Element | null;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { Icon } from '../icons';
3
+ import { useChatContext } from '../../../context';
4
+ import { useStateStore } from '../hooks/useStateStore';
5
+ const selector = (nextValue) => [nextValue.unseenThreadIds];
6
+ export const ThreadListUnseenThreadsBanner = () => {
7
+ const { client } = useChatContext();
8
+ const [unseenThreadIds] = useStateStore(client.threads.state, selector);
9
+ if (!unseenThreadIds.length)
10
+ return null;
11
+ return (React.createElement("div", { className: 'str-chat__unseen-threads-banner' },
12
+ unseenThreadIds.length,
13
+ " unread threads",
14
+ React.createElement("button", { className: 'str-chat__unseen-threads-banner__button', onClick: () => client.threads.reload() },
15
+ React.createElement(Icon.Reload, null))));
16
+ };
@@ -0,0 +1,3 @@
1
+ export * from './ThreadList';
2
+ export * from './ThreadListItem';
3
+ export * from './ThreadListItemUI';
@@ -0,0 +1,3 @@
1
+ export * from './ThreadList';
2
+ export * from './ThreadListItem';
3
+ export * from './ThreadListItemUI';