stream-chat-react 12.0.0-rc.8 → 12.0.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.
Files changed (158) 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 +95 -56
  4. package/dist/components/Channel/hooks/useCreateChannelStateContext.js +2 -1
  5. package/dist/components/ChannelHeader/ChannelHeader.js +4 -5
  6. package/dist/components/ChannelPreview/hooks/useChannelPreviewInfo.js +14 -16
  7. package/dist/components/ChannelPreview/utils.js +9 -20
  8. package/dist/components/ChannelSearch/hooks/useChannelSearch.js +2 -3
  9. package/dist/components/Chat/Chat.d.ts +1 -1
  10. package/dist/components/Chat/hooks/useChat.d.ts +2 -2
  11. package/dist/components/Chat/hooks/useChat.js +11 -8
  12. package/dist/components/ChatView/ChatView.d.ts +18 -0
  13. package/dist/components/ChatView/ChatView.js +103 -0
  14. package/dist/components/ChatView/index.d.ts +1 -0
  15. package/dist/components/ChatView/index.js +1 -0
  16. package/dist/components/DateSeparator/DateSeparator.d.ts +1 -1
  17. package/dist/components/Dialog/DialogAnchor.d.ts +25 -0
  18. package/dist/components/Dialog/DialogAnchor.js +68 -0
  19. package/dist/components/Dialog/DialogManager.d.ts +43 -0
  20. package/dist/components/Dialog/DialogManager.js +98 -0
  21. package/dist/components/Dialog/DialogPortal.d.ts +7 -0
  22. package/dist/components/Dialog/DialogPortal.js +25 -0
  23. package/dist/components/Dialog/hooks/index.d.ts +1 -0
  24. package/dist/components/Dialog/hooks/index.js +1 -0
  25. package/dist/components/Dialog/hooks/useDialog.d.ts +4 -0
  26. package/dist/components/Dialog/hooks/useDialog.js +26 -0
  27. package/dist/components/Dialog/index.d.ts +4 -0
  28. package/dist/components/Dialog/index.js +4 -0
  29. package/dist/components/EventComponent/EventComponent.d.ts +1 -1
  30. package/dist/components/Message/Message.js +5 -6
  31. package/dist/components/Message/MessageOptions.d.ts +1 -2
  32. package/dist/components/Message/MessageOptions.js +14 -11
  33. package/dist/components/Message/MessageSimple.js +6 -14
  34. package/dist/components/Message/MessageTimestamp.d.ts +1 -1
  35. package/dist/components/Message/QuotedMessage.js +2 -1
  36. package/dist/components/Message/Timestamp.d.ts +1 -1
  37. package/dist/components/Message/Timestamp.js +2 -2
  38. package/dist/components/Message/hooks/useReactionHandler.d.ts +1 -7
  39. package/dist/components/Message/hooks/useReactionHandler.js +8 -63
  40. package/dist/components/Message/utils.d.ts +10 -1
  41. package/dist/components/Message/utils.js +19 -7
  42. package/dist/components/MessageActions/MessageActions.d.ts +1 -2
  43. package/dist/components/MessageActions/MessageActions.js +26 -55
  44. package/dist/components/MessageActions/MessageActionsBox.d.ts +1 -1
  45. package/dist/components/MessageActions/MessageActionsBox.js +6 -6
  46. package/dist/components/MessageInput/MessageInputFlat.js +2 -2
  47. package/dist/components/MessageInput/QuotedMessagePreview.js +2 -1
  48. package/dist/components/MessageInput/hooks/useUserTrigger.js +0 -1
  49. package/dist/components/MessageList/MessageList.js +7 -7
  50. package/dist/components/MessageList/VirtualizedMessageList.d.ts +2 -1
  51. package/dist/components/MessageList/VirtualizedMessageList.js +44 -39
  52. package/dist/components/MessageList/VirtualizedMessageListComponents.d.ts +1 -1
  53. package/dist/components/MessageList/VirtualizedMessageListComponents.js +6 -6
  54. package/dist/components/MessageList/renderMessages.d.ts +2 -2
  55. package/dist/components/MessageList/renderMessages.js +4 -1
  56. package/dist/components/MessageList/utils.js +1 -1
  57. package/dist/components/Reactions/ReactionSelector.d.ts +6 -3
  58. package/dist/components/Reactions/ReactionSelector.js +34 -24
  59. package/dist/components/Reactions/ReactionSelectorWithButton.d.ts +13 -0
  60. package/dist/components/Reactions/ReactionSelectorWithButton.js +22 -0
  61. package/dist/components/Reactions/ReactionsList.d.ts +4 -4
  62. package/dist/components/Reactions/hooks/useProcessReactions.js +2 -1
  63. package/dist/components/Thread/Thread.js +38 -10
  64. package/dist/components/Threads/ThreadContext.d.ts +9 -0
  65. package/dist/components/Threads/ThreadContext.js +9 -0
  66. package/dist/components/Threads/ThreadList/ThreadList.d.ts +9 -0
  67. package/dist/components/Threads/ThreadList/ThreadList.js +41 -0
  68. package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.d.ts +2 -0
  69. package/dist/components/Threads/ThreadList/ThreadListEmptyPlaceholder.js +5 -0
  70. package/dist/components/Threads/ThreadList/ThreadListItem.d.ts +9 -0
  71. package/dist/components/Threads/ThreadList/ThreadListItem.js +52 -0
  72. package/dist/components/Threads/ThreadList/ThreadListItemUI.d.ts +15 -0
  73. package/dist/components/Threads/ThreadList/ThreadListItemUI.js +75 -0
  74. package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.d.ts +2 -0
  75. package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.js +14 -0
  76. package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.d.ts +2 -0
  77. package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.js +16 -0
  78. package/dist/components/Threads/ThreadList/index.d.ts +3 -0
  79. package/dist/components/Threads/ThreadList/index.js +3 -0
  80. package/dist/components/Threads/UnreadCountBadge.d.ts +6 -0
  81. package/dist/components/Threads/UnreadCountBadge.js +5 -0
  82. package/dist/components/Threads/hooks/useThreadManagerState.d.ts +2 -0
  83. package/dist/components/Threads/hooks/useThreadManagerState.js +6 -0
  84. package/dist/components/Threads/hooks/useThreadState.d.ts +5 -0
  85. package/dist/components/Threads/hooks/useThreadState.js +11 -0
  86. package/dist/components/Threads/icons.d.ts +8 -0
  87. package/dist/components/Threads/icons.js +13 -0
  88. package/dist/components/Threads/index.d.ts +2 -0
  89. package/dist/components/Threads/index.js +2 -0
  90. package/dist/components/index.d.ts +3 -0
  91. package/dist/components/index.js +3 -0
  92. package/dist/context/ComponentContext.d.ts +15 -40
  93. package/dist/context/ComponentContext.js +7 -9
  94. package/dist/context/DialogManagerContext.d.ts +10 -0
  95. package/dist/context/DialogManagerContext.js +14 -0
  96. package/dist/context/MessageContext.d.ts +3 -11
  97. package/dist/context/MessageContext.js +3 -2
  98. package/dist/context/TranslationContext.d.ts +1 -11
  99. package/dist/context/TranslationContext.js +1 -9
  100. package/dist/context/WithComponents.d.ts +5 -0
  101. package/dist/context/WithComponents.js +7 -0
  102. package/dist/context/index.d.ts +2 -0
  103. package/dist/context/index.js +2 -0
  104. package/dist/css/v2/index.css +2 -2
  105. package/dist/css/v2/index.layout.css +2 -2
  106. package/dist/i18n/Streami18n.d.ts +1 -3
  107. package/dist/i18n/Streami18n.js +1 -2
  108. package/dist/i18n/index.d.ts +2 -1
  109. package/dist/i18n/index.js +2 -0
  110. package/dist/i18n/types.d.ts +26 -0
  111. package/dist/i18n/types.js +1 -0
  112. package/dist/i18n/utils.d.ts +9 -20
  113. package/dist/i18n/utils.js +10 -1
  114. package/dist/index.browser.cjs +10827 -10601
  115. package/dist/index.browser.cjs.map +4 -4
  116. package/dist/index.d.ts +1 -0
  117. package/dist/index.js +1 -0
  118. package/dist/index.node.cjs +10720 -10510
  119. package/dist/index.node.cjs.map +4 -4
  120. package/dist/plugins/Emojis/index.browser.cjs +7 -169
  121. package/dist/plugins/Emojis/index.browser.cjs.map +4 -4
  122. package/dist/plugins/Emojis/index.node.cjs +7 -169
  123. package/dist/plugins/Emojis/index.node.cjs.map +4 -4
  124. package/dist/plugins/encoders/mp3.browser.cjs +2 -4
  125. package/dist/plugins/encoders/mp3.browser.cjs.map +1 -1
  126. package/dist/plugins/encoders/mp3.node.cjs +2 -4
  127. package/dist/plugins/encoders/mp3.node.cjs.map +1 -1
  128. package/dist/scss/v2/Avatar/Avatar-layout.scss +10 -2
  129. package/dist/scss/v2/Avatar/Avatar-theme.scss +5 -0
  130. package/dist/scss/v2/ChatView/ChatView-layout.scss +43 -0
  131. package/dist/scss/v2/ChatView/ChatView-theme.scss +32 -0
  132. package/dist/scss/v2/Dialog/Dialog-layout.scss +8 -0
  133. package/dist/scss/v2/LoadingIndicator/LoadingIndicator-layout.scss +16 -0
  134. package/dist/scss/v2/Message/Message-layout.scss +8 -0
  135. package/dist/scss/v2/MessageActionsBox/MessageActionsBox-theme.scss +8 -0
  136. package/dist/scss/v2/MessageList/MessageList-layout.scss +0 -6
  137. package/dist/scss/v2/MessageList/VirtualizedMessageList-layout.scss +0 -12
  138. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +16 -0
  139. package/dist/scss/v2/MessageReactions/MessageReactionsSelector-theme.scss +6 -0
  140. package/dist/scss/v2/Thread/Thread-layout.scss +15 -1
  141. package/dist/scss/v2/ThreadList/ThreadList-layout.scss +152 -0
  142. package/dist/scss/v2/ThreadList/ThreadList-theme.scss +75 -0
  143. package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-layout.scss +49 -0
  144. package/dist/scss/v2/UnreadCountBadge/UnreadCountBadge-theme.scss +11 -0
  145. package/dist/scss/v2/_base.scss +31 -0
  146. package/dist/scss/v2/index.layout.scss +4 -0
  147. package/dist/scss/v2/index.scss +3 -0
  148. package/dist/store/hooks/index.d.ts +1 -0
  149. package/dist/store/hooks/index.js +1 -0
  150. package/dist/store/hooks/useStateStore.d.ts +3 -0
  151. package/dist/store/hooks/useStateStore.js +15 -0
  152. package/dist/store/index.d.ts +1 -0
  153. package/dist/store/index.js +1 -0
  154. package/package.json +7 -6
  155. package/dist/assets/Poweredby_100px-White_VertText.png +0 -0
  156. package/dist/assets/str-chat__reaction-list-sprite@1x.png +0 -0
  157. package/dist/assets/str-chat__reaction-list-sprite@2x.png +0 -0
  158. package/dist/assets/str-chat__reaction-list-sprite@3x.png +0 -0
@@ -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,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
- /** 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;
@@ -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: React.ForwardRefExoticComponent<ReactionSelectorProps<DefaultStreamChatGenerics> & React.RefAttributes<HTMLDivElement | null>>;
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 = React.forwardRef((props, ref) => {
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
- const tooltip = tooltipRef.current?.getBoundingClientRect();
32
- const target = targetRef.current?.getBoundingClientRect();
33
- const container = isMutableRef(ref) ? ref.current?.getBoundingClientRect() : null;
34
- if (!tooltip || !target || !container)
35
- return;
36
- const tooltipPosition = tooltip.width === container.width || tooltip.x < container.x
37
- ? 0
38
- : target.left + target.width / 2 - container.left - tooltip.width / 2;
39
- const arrowPosition = target.x - tooltip.x + target.width / 2 - tooltipPosition;
40
- setTooltipPositions({
41
- arrow: arrowPosition,
42
- tooltip: tooltipPosition,
43
- });
44
- }
45
- }, [tooltipReactionType, ref]);
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: 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) => handleReaction(reactionType, 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
- /** A list of the currently supported reactions on a message */
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
- if (!thread || channelConfig?.replies === false)
16
+ const threadInstance = useThreadContext();
17
+ if ((!thread && !threadInstance) || channelConfig?.replies === false)
15
18
  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}` });
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 { thread, threadHasMore, threadLoadingMore, threadMessages, threadSuppressAutoscroll, } = useChannelStateContext('Thread');
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
- if (!thread)
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: thread.id, message: thread, Message: MessageUIComponent, ...additionalParentMessageProps }));
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: 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 })));
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,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,15 @@
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
+ */
8
+ export declare const attachmentTypeIconMap: {
9
+ readonly audio: "🔈";
10
+ readonly file: "📄";
11
+ readonly image: "📷";
12
+ readonly video: "🎥";
13
+ readonly voiceRecording: "🎙️";
14
+ };
15
+ export declare const ThreadListItemUI: (props: ThreadListItemUIProps) => React.JSX.Element;
@@ -0,0 +1,75 @@
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 { useChannelPreviewInfo } from '../../ChannelPreview';
8
+ import { useChatContext } from '../../../context';
9
+ import { useThreadsViewContext } from '../../ChatView';
10
+ import { useThreadListItemContext } from './ThreadListItem';
11
+ import { useStateStore } from '../../../store';
12
+ /**
13
+ * TODO:
14
+ * - maybe hover state? ask design
15
+ */
16
+ export const attachmentTypeIconMap = {
17
+ audio: '🔈',
18
+ file: '📄',
19
+ image: '📷',
20
+ video: '🎥',
21
+ voiceRecording: '🎙️',
22
+ };
23
+ // TODO: translations
24
+ const getTitleFromMessage = ({ currentUserId, message, }) => {
25
+ const attachment = message?.attachments?.at(0);
26
+ let attachmentIcon = '';
27
+ if (attachment) {
28
+ attachmentIcon +=
29
+ attachmentTypeIconMap[attachment.type ?? 'file'] ??
30
+ attachmentTypeIconMap.file;
31
+ }
32
+ const messageBelongsToCurrentUser = message?.user?.id === currentUserId;
33
+ if (message?.deleted_at && message.parent_id)
34
+ return clsx(messageBelongsToCurrentUser && 'You:', 'This reply was deleted.');
35
+ if (message?.deleted_at && !message.parent_id)
36
+ return clsx(messageBelongsToCurrentUser && 'You:', 'The source message was deleted.');
37
+ if (attachment?.type === 'voiceRecording')
38
+ return clsx(attachmentIcon, messageBelongsToCurrentUser && 'You:', 'Voice message');
39
+ return clsx(attachmentIcon, messageBelongsToCurrentUser && 'You:', message?.text || attachment?.fallback || 'N/A');
40
+ };
41
+ export const ThreadListItemUI = (props) => {
42
+ const { client } = useChatContext();
43
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
44
+ const thread = useThreadListItemContext();
45
+ const selector = useCallback((nextValue) => [
46
+ nextValue.replies.at(-1),
47
+ (client.userID && nextValue.read[client.userID]?.unreadMessageCount) || 0,
48
+ nextValue.parentMessage,
49
+ nextValue.channel,
50
+ nextValue.deletedAt,
51
+ ], [client]);
52
+ const [latestReply, ownUnreadMessageCount, parentMessage, channel, deletedAt] = useStateStore(thread.state, selector);
53
+ const { displayTitle: channelDisplayTitle } = useChannelPreviewInfo({ channel });
54
+ const { activeThread, setActiveThread } = useThreadsViewContext();
55
+ const avatarProps = deletedAt ? null : latestReply?.user;
56
+ 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 },
57
+ React.createElement("div", { className: 'str-chat__thread-list-item__channel' },
58
+ React.createElement(Icon.MessageBubble, null),
59
+ React.createElement("div", { className: 'str-chat__thread-list-item__channel-text' }, channelDisplayTitle)),
60
+ React.createElement("div", { className: 'str-chat__thread-list-item__parent-message' },
61
+ React.createElement("div", { className: 'str-chat__thread-list-item__parent-message-text' },
62
+ "replied to: ",
63
+ getTitleFromMessage({ message: parentMessage })),
64
+ !deletedAt && React.createElement(UnreadCountBadge, { count: ownUnreadMessageCount })),
65
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply' },
66
+ React.createElement(Avatar, { ...avatarProps }),
67
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-details' },
68
+ !deletedAt && (React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-created-by' }, latestReply?.user?.name || latestReply?.user?.id || 'Unknown sender')),
69
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-text-and-timestamp' },
70
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-text' }, deletedAt
71
+ ? 'This thread was deleted'
72
+ : getTitleFromMessage({ currentUserId: client.user?.id, message: latestReply })),
73
+ React.createElement("div", { className: 'str-chat__thread-list-item__latest-reply-timestamp' },
74
+ React.createElement(Timestamp, { timestamp: deletedAt ?? latestReply?.created_at })))))));
75
+ };
@@ -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 '../../../store';
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
+ };