stream-chat-react 13.0.3 → 13.0.4

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.
@@ -24,7 +24,7 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
24
24
  useEffect(() => {
25
25
  if (!client)
26
26
  return;
27
- const version = "13.0.3";
27
+ const version = "13.0.4";
28
28
  const userAgent = client.getUserAgent();
29
29
  if (!userAgent.includes('stream-chat-react')) {
30
30
  // result looks like: 'stream-chat-react-2.3.2-stream-chat-javascript-client-browser-2.2.2'
@@ -1,3 +1,4 @@
1
+ import { nanoid } from 'nanoid';
1
2
  import { StateStore } from 'stream-chat';
2
3
  /**
3
4
  * Keeps a map of Dialog objects.
@@ -14,7 +15,7 @@ export class DialogManager {
14
15
  this.state = new StateStore({
15
16
  dialogsById: {},
16
17
  });
17
- this.id = id ?? new Date().getTime().toString();
18
+ this.id = id ?? nanoid();
18
19
  }
19
20
  get openDialogCount() {
20
21
  return Object.values(this.state.getLatestValue().dialogsById).reduce((count, dialog) => {
@@ -1,5 +1,4 @@
1
- import { nanoid } from 'nanoid';
2
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
3
2
  import { UploadIcon as DefaultUploadIcon } from './icons';
4
3
  import { useAttachmentManagerState } from './hooks/useAttachmentManagerState';
5
4
  import { CHANNEL_CONTAINER_ID } from '../Channel/constants';
@@ -11,11 +10,12 @@ import { Portal } from '../Portal/Portal';
11
10
  import { UploadFileInput } from '../ReactFileUtilities';
12
11
  import { useChannelStateContext, useComponentContext, useMessageInputContext, useTranslationContext, } from '../../context';
13
12
  import { AttachmentSelectorContextProvider, useAttachmentSelectorContext, } from '../../context/AttachmentSelectorContext';
13
+ import { useStableId } from '../UtilityComponents/useStableId';
14
14
  export const SimpleAttachmentSelector = () => {
15
15
  const { AttachmentSelectorInitiationButtonContents, FileUploadIcon = DefaultUploadIcon, } = useComponentContext();
16
16
  const inputRef = useRef(null);
17
17
  const [labelElement, setLabelElement] = useState(null);
18
- const id = useMemo(() => nanoid(), []);
18
+ const id = useStableId();
19
19
  useEffect(() => {
20
20
  if (!labelElement)
21
21
  return;
@@ -7,6 +7,7 @@ import { useMessageInputControls } from './hooks/useMessageInputControls';
7
7
  import { useComponentContext } from '../../context/ComponentContext';
8
8
  import { MessageInputContextProvider } from '../../context/MessageInputContext';
9
9
  import { DialogManagerProvider } from '../../context';
10
+ import { useStableId } from '../UtilityComponents/useStableId';
10
11
  import { useRegisterDropHandlers } from './WithDragAndDropUpload';
11
12
  const MessageInputProvider = (props) => {
12
13
  const cooldownTimerState = useCooldownTimer();
@@ -39,10 +40,11 @@ const MessageInputProvider = (props) => {
39
40
  const UnMemoizedMessageInput = (props) => {
40
41
  const { Input: PropInput } = props;
41
42
  const { Input: ContextInput } = useComponentContext('MessageInput');
43
+ const id = useStableId();
42
44
  const Input = PropInput || ContextInput || MessageInputFlat;
43
45
  const dialogManagerId = props.isThreadInput
44
- ? 'message-input-dialog-manager-thread'
45
- : 'message-input-dialog-manager';
46
+ ? `message-input-dialog-manager-thread-${id}`
47
+ : `message-input-dialog-manager-${id}`;
46
48
  return (React.createElement(DialogManagerProvider, { id: dialogManagerId },
47
49
  React.createElement(MessageInputProvider, { ...props },
48
50
  React.createElement(Input, null))));
@@ -18,6 +18,7 @@ import { defaultPinPermissions, MESSAGE_ACTIONS } from '../Message/utils';
18
18
  import { TypingIndicator as DefaultTypingIndicator } from '../TypingIndicator';
19
19
  import { MessageListMainPanel as DefaultMessageListMainPanel } from './MessageListMainPanel';
20
20
  import { defaultRenderMessages } from './renderMessages';
21
+ import { useStableId } from '../UtilityComponents/useStableId';
21
22
  import { DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, DEFAULT_NEXT_CHANNEL_PAGE_SIZE, } from '../../constants/limits';
22
23
  const MessageListWithContext = (props) => {
23
24
  const { channel, channelUnreadUiState, disableDateSeparator = false, groupStyles, hasMoreNewer = false, headerPosition, hideDeletedMessages = false, hideNewMessageSeparator = false, highlightedMessageId, internalInfiniteScrollProps: { threshold: loadMoreScrollThreshold = DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD, ...restInternalInfiniteScrollProps } = {}, jumpToLatestMessage = () => Promise.resolve(), loadMore: loadMoreCallback, loadMoreNewer: loadMoreNewerCallback, // @deprecated in favor of `channelCapabilities` - TODO: remove in next major release
@@ -123,10 +124,11 @@ const MessageListWithContext = (props) => {
123
124
  }
124
125
  // eslint-disable-next-line react-hooks/exhaustive-deps
125
126
  }, [highlightedMessageId]);
127
+ const id = useStableId();
126
128
  const showEmptyStateIndicator = elements.length === 0 && !threadList;
127
129
  const dialogManagerId = threadList
128
- ? 'message-list-dialog-manager-thread'
129
- : 'message-list-dialog-manager';
130
+ ? `message-list-dialog-manager-thread-${id}`
131
+ : `message-list-dialog-manager-${id}`;
130
132
  return (React.createElement(MessageListContextProvider, { value: { listElement, scrollToBottom } },
131
133
  React.createElement(MessageListMainPanel, null,
132
134
  React.createElement(DialogManagerProvider, { id: dialogManagerId },
@@ -21,6 +21,7 @@ import { useChatContext } from '../../context/ChatContext';
21
21
  import { useComponentContext } from '../../context/ComponentContext';
22
22
  import { VirtualizedMessageListContextProvider } from '../../context/VirtualizedMessageListContext';
23
23
  import { DEFAULT_NEXT_CHANNEL_PAGE_SIZE } from '../../constants/limits';
24
+ import { useStableId } from '../UtilityComponents/useStableId';
24
25
  function captureResizeObserverExceededError(e) {
25
26
  if (e.message === 'ResizeObserver loop completed with undelivered notifications.' ||
26
27
  e.message === 'ResizeObserver loop limit exceeded') {
@@ -194,11 +195,12 @@ const VirtualizedMessageListWithContext = (props) => {
194
195
  clearTimeout(scrollTimeout);
195
196
  };
196
197
  }, [highlightedMessageId, processedMessages]);
198
+ const id = useStableId();
197
199
  if (!processedMessages)
198
200
  return null;
199
201
  const dialogManagerId = threadList
200
- ? 'virtualized-message-list-dialog-manager-thread'
201
- : 'virtualized-message-list-dialog-manager';
202
+ ? `virtualized-message-list-dialog-manager-thread-${id}`
203
+ : `virtualized-message-list-dialog-manager-${id}`;
202
204
  return (React.createElement(VirtualizedMessageListContextProvider, { value: { scrollToBottom } },
203
205
  React.createElement(MessageListMainPanel, null,
204
206
  React.createElement(DialogManagerProvider, { id: dialogManagerId },
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The ID is generated using the `nanoid` library and is memoized to ensure
3
+ * that it remains the same across renders unless the key changes.
4
+ */
5
+ export declare const useStableId: (key?: string) => string;
@@ -0,0 +1,11 @@
1
+ import { nanoid } from 'nanoid';
2
+ import { useMemo } from 'react';
3
+ /**
4
+ * The ID is generated using the `nanoid` library and is memoized to ensure
5
+ * that it remains the same across renders unless the key changes.
6
+ */
7
+ export const useStableId = (key) => {
8
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9
+ const id = useMemo(() => nanoid(), [key]);
10
+ return id;
11
+ };