stream-chat-react 12.0.0-rc.13 → 12.0.0-rc.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Channel/hooks/useCreateChannelStateContext.js +1 -0
- package/dist/components/ChannelHeader/ChannelHeader.js +4 -5
- package/dist/components/ChannelPreview/hooks/useChannelPreviewInfo.js +14 -16
- package/dist/components/ChannelPreview/utils.js +9 -20
- package/dist/components/ChannelSearch/hooks/useChannelSearch.js +2 -3
- package/dist/components/ChatView/ChatView.js +2 -1
- package/dist/components/Dialog/DialogAnchor.d.ts +25 -0
- package/dist/components/Dialog/DialogAnchor.js +68 -0
- package/dist/components/Dialog/DialogManager.d.ts +43 -0
- package/dist/components/Dialog/DialogManager.js +98 -0
- package/dist/components/Dialog/DialogPortal.d.ts +7 -0
- package/dist/components/Dialog/DialogPortal.js +25 -0
- package/dist/components/Dialog/hooks/index.d.ts +1 -0
- package/dist/components/Dialog/hooks/index.js +1 -0
- package/dist/components/Dialog/hooks/useDialog.d.ts +4 -0
- package/dist/components/Dialog/hooks/useDialog.js +26 -0
- package/dist/components/Dialog/index.d.ts +4 -0
- package/dist/components/Dialog/index.js +4 -0
- package/dist/components/Message/Message.js +3 -5
- package/dist/components/Message/MessageOptions.d.ts +1 -2
- package/dist/components/Message/MessageOptions.js +13 -9
- package/dist/components/Message/MessageSimple.js +5 -14
- package/dist/components/Message/hooks/useReactionHandler.d.ts +1 -7
- package/dist/components/Message/hooks/useReactionHandler.js +1 -63
- package/dist/components/Message/utils.js +3 -0
- package/dist/components/MessageActions/MessageActions.d.ts +1 -2
- package/dist/components/MessageActions/MessageActions.js +13 -47
- package/dist/components/MessageActions/MessageActionsBox.d.ts +1 -1
- package/dist/components/MessageActions/MessageActionsBox.js +6 -6
- package/dist/components/MessageInput/hooks/useUserTrigger.js +0 -1
- package/dist/components/MessageList/MessageList.js +7 -5
- package/dist/components/MessageList/VirtualizedMessageList.js +39 -37
- package/dist/components/Reactions/ReactionSelector.d.ts +1 -1
- package/dist/components/Reactions/ReactionSelector.js +33 -24
- package/dist/components/Reactions/ReactionSelectorWithButton.d.ts +13 -0
- package/dist/components/Reactions/ReactionSelectorWithButton.js +22 -0
- package/dist/components/Reactions/ReactionsList.d.ts +0 -3
- package/dist/components/Thread/Thread.js +2 -1
- package/dist/components/Threads/ThreadList/ThreadList.js +1 -1
- package/dist/components/Threads/ThreadList/ThreadListItemUI.js +1 -1
- package/dist/components/Threads/ThreadList/ThreadListLoadingIndicator.js +1 -1
- package/dist/components/Threads/ThreadList/ThreadListUnseenThreadsBanner.js +1 -1
- package/dist/components/Threads/hooks/useThreadManagerState.js +1 -1
- package/dist/components/Threads/hooks/useThreadState.js +1 -1
- package/dist/components/Threads/index.d.ts +0 -1
- package/dist/components/Threads/index.js +0 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/context/DialogManagerContext.d.ts +10 -0
- package/dist/context/DialogManagerContext.js +14 -0
- package/dist/context/MessageContext.d.ts +2 -10
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +1 -0
- package/dist/css/v2/index.css +1 -1
- package/dist/css/v2/index.layout.css +1 -1
- package/dist/index.browser.cjs +2164 -2004
- package/dist/index.browser.cjs.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.node.cjs +2087 -1918
- package/dist/index.node.cjs.map +4 -4
- package/dist/scss/v2/Dialog/Dialog-layout.scss +8 -0
- package/dist/scss/v2/Message/Message-layout.scss +8 -0
- package/dist/scss/v2/MessageReactions/MessageReactionsSelector-layout.scss +16 -0
- package/dist/scss/v2/ThreadList/ThreadList-layout.scss +4 -1
- package/dist/scss/v2/index.layout.scss +1 -0
- package/dist/store/hooks/index.d.ts +1 -0
- package/dist/store/hooks/index.js +1 -0
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.js +1 -0
- package/package.json +2 -2
- /package/dist/{components/Threads → store}/hooks/useStateStore.d.ts +0 -0
- /package/dist/{components/Threads → store}/hooks/useStateStore.js +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
2
|
import throttle from 'lodash.throttle';
|
|
3
3
|
import { useChannelActionContext } from '../../../context/ChannelActionContext';
|
|
4
4
|
import { useChannelStateContext } from '../../../context/ChannelStateContext';
|
|
@@ -114,65 +114,3 @@ export const useReactionHandler = (message) => {
|
|
|
114
114
|
}
|
|
115
115
|
};
|
|
116
116
|
};
|
|
117
|
-
export const useReactionClick = (message, reactionSelectorRef, messageWrapperRef, closeReactionSelectorOnClick) => {
|
|
118
|
-
const { channelCapabilities = {} } = useChannelStateContext('useReactionClick');
|
|
119
|
-
const [showDetailedReactions, setShowDetailedReactions] = useState(false);
|
|
120
|
-
const hasListener = useRef(false);
|
|
121
|
-
const isReactionEnabled = channelCapabilities['send-reaction'];
|
|
122
|
-
const messageDeleted = !!message?.deleted_at;
|
|
123
|
-
const closeDetailedReactions = useCallback((event) => {
|
|
124
|
-
if (event.target instanceof HTMLElement &&
|
|
125
|
-
reactionSelectorRef?.current?.contains(event.target) &&
|
|
126
|
-
!closeReactionSelectorOnClick) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
setShowDetailedReactions(false);
|
|
130
|
-
},
|
|
131
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
132
|
-
[setShowDetailedReactions, reactionSelectorRef]);
|
|
133
|
-
useEffect(() => {
|
|
134
|
-
const messageWrapper = messageWrapperRef?.current;
|
|
135
|
-
if (showDetailedReactions && !hasListener.current) {
|
|
136
|
-
hasListener.current = true;
|
|
137
|
-
document.addEventListener('click', closeDetailedReactions);
|
|
138
|
-
if (messageWrapper) {
|
|
139
|
-
messageWrapper.addEventListener('mouseleave', closeDetailedReactions);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (!showDetailedReactions && hasListener.current) {
|
|
143
|
-
document.removeEventListener('click', closeDetailedReactions);
|
|
144
|
-
if (messageWrapper) {
|
|
145
|
-
messageWrapper.removeEventListener('mouseleave', closeDetailedReactions);
|
|
146
|
-
}
|
|
147
|
-
hasListener.current = false;
|
|
148
|
-
}
|
|
149
|
-
return () => {
|
|
150
|
-
if (hasListener.current) {
|
|
151
|
-
document.removeEventListener('click', closeDetailedReactions);
|
|
152
|
-
if (messageWrapper) {
|
|
153
|
-
messageWrapper.removeEventListener('mouseleave', closeDetailedReactions);
|
|
154
|
-
}
|
|
155
|
-
hasListener.current = false;
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
}, [showDetailedReactions, closeDetailedReactions, messageWrapperRef]);
|
|
159
|
-
useEffect(() => {
|
|
160
|
-
const messageWrapper = messageWrapperRef?.current;
|
|
161
|
-
if (messageDeleted && hasListener.current) {
|
|
162
|
-
document.removeEventListener('click', closeDetailedReactions);
|
|
163
|
-
if (messageWrapper) {
|
|
164
|
-
messageWrapper.removeEventListener('mouseleave', closeDetailedReactions);
|
|
165
|
-
}
|
|
166
|
-
hasListener.current = false;
|
|
167
|
-
}
|
|
168
|
-
}, [messageDeleted, closeDetailedReactions, messageWrapperRef]);
|
|
169
|
-
const onReactionListClick = (event) => {
|
|
170
|
-
event?.stopPropagation?.();
|
|
171
|
-
setShowDetailedReactions((prev) => !prev);
|
|
172
|
-
};
|
|
173
|
-
return {
|
|
174
|
-
isReactionEnabled,
|
|
175
|
-
onReactionListClick,
|
|
176
|
-
showDetailedReactions,
|
|
177
|
-
};
|
|
178
|
-
};
|
|
@@ -194,6 +194,9 @@ export const areMessagePropsEqual = (prevProps, nextProps) => {
|
|
|
194
194
|
if (nextProps.showDetailedReactions !== prevProps.showDetailedReactions) {
|
|
195
195
|
return false;
|
|
196
196
|
}
|
|
197
|
+
if (nextProps.closeReactionSelectorOnClick !== prevProps.closeReactionSelectorOnClick) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
197
200
|
const messagesAreEqual = areMessagesEqual(prevMessage, nextMessage);
|
|
198
201
|
if (!messagesAreEqual)
|
|
199
202
|
return false;
|
|
@@ -6,13 +6,12 @@ export type MessageActionsProps<StreamChatGenerics extends DefaultStreamChatGene
|
|
|
6
6
|
ActionsIcon?: React.ComponentType<IconProps>;
|
|
7
7
|
customWrapperClass?: string;
|
|
8
8
|
inline?: boolean;
|
|
9
|
-
messageWrapperRef?: React.RefObject<HTMLDivElement>;
|
|
10
9
|
mine?: () => boolean;
|
|
11
10
|
};
|
|
12
11
|
export declare const MessageActions: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: MessageActionsProps<StreamChatGenerics>) => React.JSX.Element | null;
|
|
13
12
|
export type MessageActionsWrapperProps = {
|
|
14
|
-
setActionsBoxOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
15
13
|
customWrapperClass?: string;
|
|
16
14
|
inline?: boolean;
|
|
15
|
+
toggleOpen?: () => void;
|
|
17
16
|
};
|
|
18
17
|
export {};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState, } from 'react';
|
|
2
1
|
import clsx from 'clsx';
|
|
2
|
+
import React, { useCallback, useRef } from 'react';
|
|
3
3
|
import { MessageActionsBox } from './MessageActionsBox';
|
|
4
|
+
import { DialogAnchor, useDialog, useDialogIsOpen } from '../Dialog';
|
|
4
5
|
import { ActionsIcon as DefaultActionsIcon } from '../Message/icons';
|
|
5
6
|
import { isUserMuted, shouldRenderMessageActions } from '../Message/utils';
|
|
6
7
|
import { useChatContext } from '../../context/ChatContext';
|
|
7
8
|
import { useMessageContext } from '../../context/MessageContext';
|
|
8
|
-
import { useMessageActionsBoxPopper } from './hooks';
|
|
9
9
|
import { useComponentContext, useTranslationContext } from '../../context';
|
|
10
10
|
export const MessageActions = (props) => {
|
|
11
|
-
const { ActionsIcon = DefaultActionsIcon, customWrapperClass = '', getMessageActions: propGetMessageActions, handleDelete: propHandleDelete, handleFlag: propHandleFlag, handleMarkUnread: propHandleMarkUnread, handleMute: propHandleMute, handlePin: propHandlePin, inline, message: propMessage,
|
|
11
|
+
const { ActionsIcon = DefaultActionsIcon, customWrapperClass = '', getMessageActions: propGetMessageActions, handleDelete: propHandleDelete, handleFlag: propHandleFlag, handleMarkUnread: propHandleMarkUnread, handleMute: propHandleMute, handlePin: propHandlePin, inline, message: propMessage, mine, } = props;
|
|
12
12
|
const { mutes } = useChatContext('MessageActions');
|
|
13
13
|
const { customMessageActions, getMessageActions: contextGetMessageActions, handleDelete: contextHandleDelete, handleFlag: contextHandleFlag, handleMarkUnread: contextHandleMarkUnread, handleMute: contextHandleMute, handlePin: contextHandlePin, isMyMessage, message: contextMessage, setEditingState, threadList, } = useMessageContext('MessageActions');
|
|
14
14
|
const { CustomMessageActionsList } = useComponentContext('MessageActions');
|
|
@@ -21,8 +21,10 @@ export const MessageActions = (props) => {
|
|
|
21
21
|
const handlePin = propHandlePin || contextHandlePin;
|
|
22
22
|
const message = propMessage || contextMessage;
|
|
23
23
|
const isMine = mine ? mine() : isMyMessage();
|
|
24
|
-
const [actionsBoxOpen, setActionsBoxOpen] = useState(false);
|
|
25
24
|
const isMuted = useCallback(() => isUserMuted(message, mutes), [message, mutes]);
|
|
25
|
+
const dialogId = `message-actions--${message.id}`;
|
|
26
|
+
const dialog = useDialog({ id: dialogId });
|
|
27
|
+
const dialogIsOpen = useDialogIsOpen(dialogId);
|
|
26
28
|
const messageActions = getMessageActions();
|
|
27
29
|
const renderMessageActions = shouldRenderMessageActions({
|
|
28
30
|
customMessageActions,
|
|
@@ -30,58 +32,22 @@ export const MessageActions = (props) => {
|
|
|
30
32
|
inThread: threadList,
|
|
31
33
|
messageActions,
|
|
32
34
|
});
|
|
33
|
-
const hideOptions = useCallback((event) => {
|
|
34
|
-
if (event instanceof KeyboardEvent && event.key !== 'Escape') {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
setActionsBoxOpen(false);
|
|
38
|
-
}, []);
|
|
39
|
-
const messageDeletedAt = !!message?.deleted_at;
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (messageWrapperRef?.current) {
|
|
42
|
-
messageWrapperRef.current.addEventListener('mouseleave', hideOptions);
|
|
43
|
-
}
|
|
44
|
-
}, [hideOptions, messageWrapperRef]);
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (messageDeletedAt) {
|
|
47
|
-
document.removeEventListener('click', hideOptions);
|
|
48
|
-
}
|
|
49
|
-
}, [hideOptions, messageDeletedAt]);
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (!actionsBoxOpen)
|
|
52
|
-
return;
|
|
53
|
-
document.addEventListener('click', hideOptions);
|
|
54
|
-
document.addEventListener('keyup', hideOptions);
|
|
55
|
-
return () => {
|
|
56
|
-
document.removeEventListener('click', hideOptions);
|
|
57
|
-
document.removeEventListener('keyup', hideOptions);
|
|
58
|
-
};
|
|
59
|
-
}, [actionsBoxOpen, hideOptions]);
|
|
60
35
|
const actionsBoxButtonRef = useRef(null);
|
|
61
|
-
const { attributes, popperElementRef, styles } = useMessageActionsBoxPopper({
|
|
62
|
-
open: actionsBoxOpen,
|
|
63
|
-
placement: isMine ? 'top-end' : 'top-start',
|
|
64
|
-
referenceElement: actionsBoxButtonRef.current,
|
|
65
|
-
});
|
|
66
36
|
if (!renderMessageActions)
|
|
67
37
|
return null;
|
|
68
|
-
return (React.createElement(MessageActionsWrapper, { customWrapperClass: customWrapperClass, inline: inline,
|
|
69
|
-
React.createElement(
|
|
70
|
-
|
|
38
|
+
return (React.createElement(MessageActionsWrapper, { customWrapperClass: customWrapperClass, inline: inline, toggleOpen: dialog?.toggle },
|
|
39
|
+
React.createElement(DialogAnchor, { id: dialogId, placement: isMine ? 'top-end' : 'top-start', referenceElement: actionsBoxButtonRef.current, trapFocus: true },
|
|
40
|
+
React.createElement(MessageActionsBox, { getMessageActions: getMessageActions, handleDelete: handleDelete, handleEdit: setEditingState, handleFlag: handleFlag, handleMarkUnread: handleMarkUnread, handleMute: handleMute, handlePin: handlePin, isUserMuted: isMuted, mine: isMine, open: dialogIsOpen })),
|
|
41
|
+
React.createElement("button", { "aria-expanded": dialogIsOpen, "aria-haspopup": 'true', "aria-label": t('aria/Open Message Actions Menu'), className: 'str-chat__message-actions-box-button', "data-testid": 'message-actions-toggle-button', ref: actionsBoxButtonRef },
|
|
71
42
|
React.createElement(ActionsIcon, { className: 'str-chat__message-action-icon' }))));
|
|
72
43
|
};
|
|
73
44
|
const MessageActionsWrapper = (props) => {
|
|
74
|
-
const { children, customWrapperClass, inline,
|
|
45
|
+
const { children, customWrapperClass, inline, toggleOpen } = props;
|
|
75
46
|
const defaultWrapperClass = clsx('str-chat__message-simple__actions__action', 'str-chat__message-simple__actions__action--options', 'str-chat__message-actions-container');
|
|
76
|
-
const wrapperClass = customWrapperClass || defaultWrapperClass;
|
|
77
|
-
const onClickOptionsAction = (event) => {
|
|
78
|
-
event.stopPropagation();
|
|
79
|
-
setActionsBoxOpen((prev) => !prev);
|
|
80
|
-
};
|
|
81
47
|
const wrapperProps = {
|
|
82
|
-
className:
|
|
48
|
+
className: customWrapperClass || defaultWrapperClass,
|
|
83
49
|
'data-testid': 'message-actions',
|
|
84
|
-
onClick:
|
|
50
|
+
onClick: toggleOpen,
|
|
85
51
|
};
|
|
86
52
|
if (inline)
|
|
87
53
|
return React.createElement("span", { ...wrapperProps }, children);
|
|
@@ -10,5 +10,5 @@ export type MessageActionsBoxProps<StreamChatGenerics extends DefaultStreamChatG
|
|
|
10
10
|
/**
|
|
11
11
|
* A popup box that displays the available actions on a message, such as edit, delete, pin, etc.
|
|
12
12
|
*/
|
|
13
|
-
export declare const MessageActionsBox:
|
|
13
|
+
export declare const MessageActionsBox: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: MessageActionsBoxProps<StreamChatGenerics>) => React.JSX.Element;
|
|
14
14
|
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import clsx from 'clsx';
|
|
2
|
+
import React from 'react';
|
|
3
3
|
import { MESSAGE_ACTIONS } from '../Message/utils';
|
|
4
4
|
import { useChannelActionContext, useComponentContext, useMessageContext, useTranslationContext, } from '../../context';
|
|
5
5
|
import { CustomMessageActionsList as DefaultCustomMessageActionsList } from './CustomMessageActionsList';
|
|
6
|
-
const UnMemoizedMessageActionsBox =
|
|
7
|
-
const { getMessageActions, handleDelete, handleEdit, handleFlag, handleMarkUnread, handleMute, handlePin, isUserMuted, mine, open
|
|
6
|
+
const UnMemoizedMessageActionsBox = (props) => {
|
|
7
|
+
const { className, getMessageActions, handleDelete, handleEdit, handleFlag, handleMarkUnread, handleMute, handlePin, isUserMuted, mine, open, ...restDivProps } = props;
|
|
8
8
|
const { CustomMessageActionsList = DefaultCustomMessageActionsList, } = useComponentContext('MessageActionsBox');
|
|
9
9
|
const { setQuotedMessage } = useChannelActionContext('MessageActionsBox');
|
|
10
10
|
const { customMessageActions, message, threadList } = useMessageContext('MessageActionsBox');
|
|
@@ -20,11 +20,11 @@ const UnMemoizedMessageActionsBox = React.forwardRef((props, ref) => {
|
|
|
20
20
|
textarea.focus();
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
-
const rootClassName = clsx('str-chat__message-actions-box', {
|
|
23
|
+
const rootClassName = clsx('str-chat__message-actions-box', className, {
|
|
24
24
|
'str-chat__message-actions-box--open': open,
|
|
25
25
|
});
|
|
26
26
|
const buttonClassName = 'str-chat__message-actions-list-item str-chat__message-actions-list-item-button';
|
|
27
|
-
return (React.createElement("div", { ...restDivProps, className: rootClassName, "data-testid": 'message-actions-box'
|
|
27
|
+
return (React.createElement("div", { ...restDivProps, className: rootClassName, "data-testid": 'message-actions-box' },
|
|
28
28
|
React.createElement("div", { "aria-label": t('aria/Message Options'), className: 'str-chat__message-actions-list', role: 'listbox' },
|
|
29
29
|
React.createElement(CustomMessageActionsList, { customMessageActions: customMessageActions, message: message }),
|
|
30
30
|
messageActions.indexOf(MESSAGE_ACTIONS.quote) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleQuote, role: 'option' }, t('Reply'))),
|
|
@@ -34,7 +34,7 @@ const UnMemoizedMessageActionsBox = React.forwardRef((props, ref) => {
|
|
|
34
34
|
messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleMute, role: 'option' }, isUserMuted() ? t('Unmute') : t('Mute'))),
|
|
35
35
|
messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleEdit, role: 'option' }, t('Edit Message'))),
|
|
36
36
|
messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1 && (React.createElement("button", { "aria-selected": 'false', className: buttonClassName, onClick: handleDelete, role: 'option' }, t('Delete'))))));
|
|
37
|
-
}
|
|
37
|
+
};
|
|
38
38
|
/**
|
|
39
39
|
* A popup box that displays the available actions on a message, such as edit, delete, pin, etc.
|
|
40
40
|
*/
|
|
@@ -52,7 +52,6 @@ export const useUserTrigger = (params) => {
|
|
|
52
52
|
// @ts-expect-error
|
|
53
53
|
{
|
|
54
54
|
$or: [{ id: { $autocomplete: query } }, { name: { $autocomplete: query } }],
|
|
55
|
-
id: { $ne: client.userID },
|
|
56
55
|
...(typeof mentionQueryParams.filters === 'function'
|
|
57
56
|
? mentionQueryParams.filters(query)
|
|
58
57
|
: mentionQueryParams.filters),
|
|
@@ -7,6 +7,7 @@ import { MessageListNotifications as DefaultMessageListNotifications } from './M
|
|
|
7
7
|
import { UnreadMessagesNotification as DefaultUnreadMessagesNotification } from './UnreadMessagesNotification';
|
|
8
8
|
import { useChannelActionContext, } from '../../context/ChannelActionContext';
|
|
9
9
|
import { useChannelStateContext, } from '../../context/ChannelStateContext';
|
|
10
|
+
import { DialogManagerProvider } from '../../context';
|
|
10
11
|
import { useChatContext } from '../../context/ChatContext';
|
|
11
12
|
import { useComponentContext } from '../../context/ComponentContext';
|
|
12
13
|
import { MessageListContextProvider } from '../../context/MessageListContext';
|
|
@@ -126,11 +127,12 @@ const MessageListWithContext = (props) => {
|
|
|
126
127
|
const showEmptyStateIndicator = elements.length === 0 && !threadList;
|
|
127
128
|
return (React.createElement(MessageListContextProvider, { value: { listElement, scrollToBottom } },
|
|
128
129
|
React.createElement(MessageListMainPanel, null,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
React.createElement("
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
React.createElement(DialogManagerProvider, { id: 'message-list-dialog-manager' },
|
|
131
|
+
!threadList && showUnreadMessagesNotification && (React.createElement(UnreadMessagesNotification, { unreadCount: channelUnreadUiState?.unread_messages })),
|
|
132
|
+
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
|
+
React.createElement("ul", { className: 'str-chat__ul', ref: setUlElement }, elements),
|
|
134
|
+
React.createElement(TypingIndicator, { threadList: threadList }),
|
|
135
|
+
React.createElement("div", { key: 'bottom' })))))),
|
|
134
136
|
React.createElement(MessageListNotifications, { hasNewMessages: hasNewMessages, isMessageListScrolledToBottom: isMessageListScrolledToBottom, isNotAtLatestMessageSet: hasMoreNewer, MessageNotification: MessageNotification, notifications: notifications, scrollToBottom: scrollToBottomFromNotification, threadList: threadList, unreadCount: threadList ? undefined : channelUnreadUiState?.unread_messages })));
|
|
135
137
|
};
|
|
136
138
|
/**
|
|
@@ -14,6 +14,7 @@ import { calculateFirstItemIndex, calculateItemIndex, EmptyPlaceholder, Header,
|
|
|
14
14
|
import { UnreadMessagesSeparator as DefaultUnreadMessagesSeparator } from '../MessageList';
|
|
15
15
|
import { DateSeparator as DefaultDateSeparator } from '../DateSeparator';
|
|
16
16
|
import { EventComponent as DefaultMessageSystem } from '../EventComponent';
|
|
17
|
+
import { DialogManagerProvider } from '../../context';
|
|
17
18
|
import { useChannelActionContext, } from '../../context/ChannelActionContext';
|
|
18
19
|
import { useChannelStateContext, } from '../../context/ChannelStateContext';
|
|
19
20
|
import { useChatContext } from '../../context/ChatContext';
|
|
@@ -190,43 +191,44 @@ const VirtualizedMessageListWithContext = (props) => {
|
|
|
190
191
|
return null;
|
|
191
192
|
return (React.createElement(React.Fragment, null,
|
|
192
193
|
React.createElement(MessageListMainPanel, null,
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
React.createElement(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
194
|
+
React.createElement(DialogManagerProvider, { id: 'virtualized-message-list-dialog-manager' },
|
|
195
|
+
!threadList && showUnreadMessagesNotification && (React.createElement(UnreadMessagesNotification, { unreadCount: channelUnreadUiState?.unread_messages })),
|
|
196
|
+
React.createElement("div", { className: customClasses?.virtualizedMessageList || 'str-chat__virtual-list' },
|
|
197
|
+
React.createElement(Virtuoso, { atBottomStateChange: atBottomStateChange, atBottomThreshold: 100, atTopStateChange: atTopStateChange, atTopThreshold: 100, className: 'str-chat__message-list-scroll', components: {
|
|
198
|
+
EmptyPlaceholder,
|
|
199
|
+
Header,
|
|
200
|
+
Item,
|
|
201
|
+
...virtuosoComponentsFromProps,
|
|
202
|
+
}, computeItemKey: computeItemKey, context: {
|
|
203
|
+
additionalMessageInputProps,
|
|
204
|
+
closeReactionSelectorOnClick,
|
|
205
|
+
customClasses,
|
|
206
|
+
customMessageActions,
|
|
207
|
+
customMessageRenderer,
|
|
208
|
+
DateSeparator,
|
|
209
|
+
firstUnreadMessageId: channelUnreadUiState?.first_unread_message_id,
|
|
210
|
+
formatDate,
|
|
211
|
+
head,
|
|
212
|
+
lastReadDate: channelUnreadUiState?.last_read,
|
|
213
|
+
lastReadMessageId: channelUnreadUiState?.last_read_message_id,
|
|
214
|
+
lastReceivedMessageId,
|
|
215
|
+
loadingMore,
|
|
216
|
+
Message: MessageUIComponent,
|
|
217
|
+
messageActions,
|
|
218
|
+
messageGroupStyles,
|
|
219
|
+
MessageSystem,
|
|
220
|
+
numItemsPrepended,
|
|
221
|
+
ownMessagesReadByOthers,
|
|
222
|
+
processedMessages,
|
|
223
|
+
reactionDetailsSort,
|
|
224
|
+
shouldGroupByUser,
|
|
225
|
+
sortReactionDetails,
|
|
226
|
+
sortReactions,
|
|
227
|
+
threadList,
|
|
228
|
+
unreadMessageCount: channelUnreadUiState?.unread_messages,
|
|
229
|
+
UnreadMessagesSeparator,
|
|
230
|
+
virtuosoRef: virtuoso,
|
|
231
|
+
}, firstItemIndex: calculateFirstItemIndex(numItemsPrepended), followOutput: followOutput, increaseViewportBy: { bottom: 200, top: 0 }, initialTopMostItemIndex: calculateInitialTopMostItemIndex(processedMessages, highlightedMessageId), itemContent: messageRenderer, itemSize: fractionalItemSize, itemsRendered: handleItemsRendered, key: messageSetKey, overscan: overscan, ref: virtuoso, style: { overflowX: 'hidden' }, totalCount: processedMessages.length, ...overridingVirtuosoProps, ...(scrollSeekPlaceHolder ? { scrollSeek: scrollSeekPlaceHolder } : {}), ...(defaultItemHeight ? { defaultItemHeight } : {}) }))),
|
|
230
232
|
TypingIndicator && React.createElement(TypingIndicator, null)),
|
|
231
233
|
React.createElement(MessageListNotifications, { hasNewMessages: newMessagesNotification, isMessageListScrolledToBottom: isMessageListScrolledToBottom, isNotAtLatestMessageSet: hasMoreNewer, MessageNotification: MessageNotification, notifications: notifications, scrollToBottom: scrollToBottom, threadList: threadList, unreadCount: threadList ? undefined : channelUnreadUiState?.unread_messages }),
|
|
232
234
|
giphyPreviewMessage && React.createElement(GiphyPreviewMessage, { message: giphyPreviewMessage })));
|
|
@@ -32,4 +32,4 @@ export type ReactionSelectorProps<StreamChatGenerics extends DefaultStreamChatGe
|
|
|
32
32
|
/**
|
|
33
33
|
* Component that allows a user to select a reaction.
|
|
34
34
|
*/
|
|
35
|
-
export declare const ReactionSelector:
|
|
35
|
+
export declare const ReactionSelector: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: ReactionSelectorProps<StreamChatGenerics>) => React.JSX.Element;
|
|
@@ -1,14 +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
|
-
|
|
8
|
-
const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
9
|
+
const UnMemoizedReactionSelector = (props) => {
|
|
9
10
|
const { Avatar: propAvatar, detailedView = true, handleReaction: propHandleReaction, latest_reactions: propLatestReactions, own_reactions: propOwnReactions, reaction_groups: propReactionGroups, reactionOptions: propReactionOptions, reverse = false, } = props;
|
|
10
11
|
const { Avatar: contextAvatar, reactionOptions: contextReactionOptions = defaultReactionOptions, } = useComponentContext('ReactionSelector');
|
|
11
|
-
const { handleReaction: contextHandleReaction, message, } = useMessageContext('ReactionSelector');
|
|
12
|
+
const { closeReactionSelectorOnClick, handleReaction: contextHandleReaction, message, } = useMessageContext('ReactionSelector');
|
|
13
|
+
const dialogId = `reaction-selector--${message.id}`;
|
|
14
|
+
const dialog = useDialog({ id: dialogId });
|
|
12
15
|
const reactionOptions = propReactionOptions ?? contextReactionOptions;
|
|
13
16
|
const Avatar = propAvatar || contextAvatar || DefaultAvatar;
|
|
14
17
|
const handleReaction = propHandleReaction || contextHandleReaction;
|
|
@@ -17,6 +20,7 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
17
20
|
const reactionGroups = propReactionGroups || message?.reaction_groups || {};
|
|
18
21
|
const [tooltipReactionType, setTooltipReactionType] = useState(null);
|
|
19
22
|
const [tooltipPositions, setTooltipPositions] = useState(null);
|
|
23
|
+
const rootRef = useRef(null);
|
|
20
24
|
const targetRef = useRef(null);
|
|
21
25
|
const tooltipRef = useRef(null);
|
|
22
26
|
const showTooltip = useCallback((event, reactionType) => {
|
|
@@ -28,22 +32,22 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
28
32
|
setTooltipPositions(null);
|
|
29
33
|
}, []);
|
|
30
34
|
useEffect(() => {
|
|
31
|
-
if (tooltipReactionType)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
}, [tooltipReactionType,
|
|
35
|
+
if (!tooltipReactionType || !rootRef.current)
|
|
36
|
+
return;
|
|
37
|
+
const tooltip = tooltipRef.current?.getBoundingClientRect();
|
|
38
|
+
const target = targetRef.current?.getBoundingClientRect();
|
|
39
|
+
const container = isMutableRef(rootRef) ? rootRef.current?.getBoundingClientRect() : null;
|
|
40
|
+
if (!tooltip || !target || !container)
|
|
41
|
+
return;
|
|
42
|
+
const tooltipPosition = tooltip.width === container.width || tooltip.x < container.x
|
|
43
|
+
? 0
|
|
44
|
+
: target.left + target.width / 2 - container.left - tooltip.width / 2;
|
|
45
|
+
const arrowPosition = target.x - tooltip.x + target.width / 2 - tooltipPosition;
|
|
46
|
+
setTooltipPositions({
|
|
47
|
+
arrow: arrowPosition,
|
|
48
|
+
tooltip: tooltipPosition,
|
|
49
|
+
});
|
|
50
|
+
}, [tooltipReactionType, rootRef]);
|
|
47
51
|
const getUsersPerReactionType = (type) => latestReactions
|
|
48
52
|
.map((reaction) => {
|
|
49
53
|
if (reaction.type === type) {
|
|
@@ -55,9 +59,9 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
55
59
|
const iHaveReactedWithReaction = (reactionType) => ownReactions.find((reaction) => reaction.type === reactionType);
|
|
56
60
|
const getLatestUserForReactionType = (type) => latestReactions.find((reaction) => reaction.type === type && !!reaction.user)?.user ||
|
|
57
61
|
undefined;
|
|
58
|
-
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', {
|
|
59
63
|
'str-chat__reaction-selector--reverse': reverse,
|
|
60
|
-
}), "data-testid": 'reaction-selector', ref:
|
|
64
|
+
}), "data-testid": 'reaction-selector', ref: rootRef },
|
|
61
65
|
!!tooltipReactionType && detailedView && (React.createElement("div", { className: 'str-chat__reaction-selector-tooltip', ref: tooltipRef, style: {
|
|
62
66
|
left: tooltipPositions?.tooltip,
|
|
63
67
|
visibility: tooltipPositions ? 'visible' : 'hidden',
|
|
@@ -70,13 +74,18 @@ const UnMemoizedReactionSelector = React.forwardRef((props, ref) => {
|
|
|
70
74
|
return (React.createElement("li", { key: reactionType },
|
|
71
75
|
React.createElement("button", { "aria-label": `Select Reaction: ${reactionName || reactionType}`, className: clsx('str-chat__message-reactions-list-item str-chat__message-reactions-option', {
|
|
72
76
|
'str-chat__message-reactions-option-selected': iHaveReactedWithReaction(reactionType),
|
|
73
|
-
}), "data-text": reactionType, onClick: (event) =>
|
|
77
|
+
}), "data-testid": 'select-reaction-button', "data-text": reactionType, onClick: (event) => {
|
|
78
|
+
handleReaction(reactionType, event);
|
|
79
|
+
if (closeReactionSelectorOnClick) {
|
|
80
|
+
dialog.close();
|
|
81
|
+
}
|
|
82
|
+
} },
|
|
74
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' })))),
|
|
75
84
|
React.createElement("span", { className: 'str-chat__message-reaction-emoji' },
|
|
76
85
|
React.createElement(Component, null)),
|
|
77
86
|
Boolean(count) && detailedView && (React.createElement("span", { className: 'str-chat__message-reactions-list-item__count' }, count || '')))));
|
|
78
87
|
}))));
|
|
79
|
-
}
|
|
88
|
+
};
|
|
80
89
|
/**
|
|
81
90
|
* Component that allows a user to select a reaction.
|
|
82
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
|
/**
|
|
@@ -6,7 +6,8 @@ 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 {
|
|
9
|
+
import { useThreadContext } from '../Threads';
|
|
10
|
+
import { useStateStore } from '../../store';
|
|
10
11
|
/**
|
|
11
12
|
* The Thread component renders a parent Message with a list of replies
|
|
12
13
|
*/
|
|
@@ -5,7 +5,7 @@ import { ThreadListEmptyPlaceholder as DefaultThreadListEmptyPlaceholder } from
|
|
|
5
5
|
import { ThreadListUnseenThreadsBanner as DefaultThreadListUnseenThreadsBanner } from './ThreadListUnseenThreadsBanner';
|
|
6
6
|
import { ThreadListLoadingIndicator as DefaultThreadListLoadingIndicator } from './ThreadListLoadingIndicator';
|
|
7
7
|
import { useChatContext, useComponentContext } from '../../../context';
|
|
8
|
-
import { useStateStore } from '
|
|
8
|
+
import { useStateStore } from '../../../store';
|
|
9
9
|
const selector = (nextValue) => [nextValue.threads];
|
|
10
10
|
const computeItemKey = (_, item) => item.id;
|
|
11
11
|
export const useThreadList = () => {
|
|
@@ -7,7 +7,7 @@ import { UnreadCountBadge } from '../UnreadCountBadge';
|
|
|
7
7
|
import { useChatContext } from '../../../context';
|
|
8
8
|
import { useThreadsViewContext } from '../../ChatView';
|
|
9
9
|
import { useThreadListItemContext } from './ThreadListItem';
|
|
10
|
-
import { useStateStore } from '
|
|
10
|
+
import { useStateStore } from '../../../store';
|
|
11
11
|
/**
|
|
12
12
|
* TODO:
|
|
13
13
|
* - maybe hover state? ask design
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { LoadingIndicator as DefaultLoadingIndicator } from '../../Loading';
|
|
3
3
|
import { useChatContext, useComponentContext } from '../../../context';
|
|
4
|
-
import { useStateStore } from '
|
|
4
|
+
import { useStateStore } from '../../../store';
|
|
5
5
|
const selector = (nextValue) => [nextValue.pagination.isLoadingNext];
|
|
6
6
|
export const ThreadListLoadingIndicator = () => {
|
|
7
7
|
const { LoadingIndicator = DefaultLoadingIndicator } = useComponentContext();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Icon } from '../icons';
|
|
3
3
|
import { useChatContext } from '../../../context';
|
|
4
|
-
import { useStateStore } from '
|
|
4
|
+
import { useStateStore } from '../../../store';
|
|
5
5
|
const selector = (nextValue) => [nextValue.unseenThreadIds];
|
|
6
6
|
export const ThreadListUnseenThreadsBanner = () => {
|
|
7
7
|
const { client } = useChatContext();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useChatContext } from 'context';
|
|
2
|
-
import { useStateStore } from '
|
|
2
|
+
import { useStateStore } from '../../../store';
|
|
3
3
|
export const useThreadManagerState = (selector) => {
|
|
4
4
|
const { client } = useChatContext();
|
|
5
5
|
return useStateStore(client.threads.state, selector);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useStateStore } from './useStateStore';
|
|
2
1
|
import { useThreadListItemContext } from '../ThreadList';
|
|
3
2
|
import { useThreadContext } from '../ThreadContext';
|
|
3
|
+
import { useStateStore } from '../../../store/';
|
|
4
4
|
/**
|
|
5
5
|
* @description returns thread state, prioritizes `ThreadListItemContext` falls back to `ThreadContext` if not former is not present
|
|
6
6
|
*/
|