stream-chat-react 12.8.2 → 12.10.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 (52) hide show
  1. package/dist/components/Attachment/components/WaveProgressBar.js +1 -1
  2. package/dist/components/Attachment/hooks/useAudioController.d.ts +1 -1
  3. package/dist/components/Attachment/hooks/useAudioController.js +1 -1
  4. package/dist/components/AutoCompleteTextarea/Textarea.js +0 -4
  5. package/dist/components/AutoCompleteTextarea/utils.js +1 -5
  6. package/dist/components/Channel/Channel.js +3 -2
  7. package/dist/components/Channel/channelState.d.ts +1 -3
  8. package/dist/components/Channel/channelState.js +1 -1
  9. package/dist/components/Channel/hooks/useIsMounted.d.ts +1 -1
  10. package/dist/components/ChannelList/hooks/useChannelListShape.d.ts +3 -3
  11. package/dist/components/ChannelList/hooks/useChannelListShape.js +54 -47
  12. package/dist/components/ChannelList/hooks/useChannelMembershipState.d.ts +3 -2
  13. package/dist/components/ChannelList/hooks/useChannelMembershipState.js +6 -15
  14. package/dist/components/ChannelList/hooks/useMobileNavigation.d.ts +1 -1
  15. package/dist/components/ChannelList/hooks/usePaginatedChannels.js +1 -1
  16. package/dist/components/ChannelList/hooks/useSelectedChannelState.d.ts +11 -0
  17. package/dist/components/ChannelList/hooks/useSelectedChannelState.js +20 -0
  18. package/dist/components/ChannelList/utils.d.ts +16 -6
  19. package/dist/components/ChannelList/utils.js +44 -18
  20. package/dist/components/ChannelSearch/SearchBar.d.ts +1 -1
  21. package/dist/components/ChannelSearch/SearchInput.d.ts +1 -1
  22. package/dist/components/ChannelSearch/hooks/useChannelSearch.js +1 -1
  23. package/dist/components/Chat/hooks/useChat.js +1 -1
  24. package/dist/components/ChatAutoComplete/ChatAutoComplete.d.ts +2 -0
  25. package/dist/components/ChatAutoComplete/ChatAutoComplete.js +1 -1
  26. package/dist/components/Gallery/BaseImage.d.ts +1 -3
  27. package/dist/components/Gallery/Gallery.js +10 -2
  28. package/dist/components/Gallery/ModalGallery.js +5 -4
  29. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +4 -4
  30. package/dist/components/MessageActions/hooks/useMessageActionsBoxPopper.d.ts +1 -1
  31. package/dist/components/MessageInput/hooks/useMessageInputText.d.ts +1 -1
  32. package/dist/components/MessageInput/hooks/useMessageInputText.js +2 -2
  33. package/dist/components/MessageInput/hooks/useTimeElapsed.js +1 -1
  34. package/dist/components/MessageList/MessageList.js +0 -1
  35. package/dist/components/MessageList/VirtualizedMessageList.d.ts +1 -1
  36. package/dist/components/MessageList/VirtualizedMessageList.js +2 -2
  37. package/dist/components/MessageList/hooks/MessageList/useMessageListScrollManager.js +1 -1
  38. package/dist/components/MessageList/hooks/VirtualizedMessageList/useMessageSetKey.js +1 -1
  39. package/dist/components/MessageList/hooks/VirtualizedMessageList/useNewMessageNotification.d.ts +1 -1
  40. package/dist/components/MessageList/hooks/VirtualizedMessageList/usePrependMessagesCount.js +2 -2
  41. package/dist/components/MessageList/hooks/VirtualizedMessageList/useScrollToBottomOnNewMessage.js +1 -1
  42. package/dist/components/MessageList/hooks/useMarkRead.d.ts +2 -4
  43. package/dist/components/MessageList/hooks/useMarkRead.js +14 -16
  44. package/dist/context/VirtualizedMessageListContext.d.ts +13 -0
  45. package/dist/context/VirtualizedMessageListContext.js +7 -0
  46. package/dist/experimental/index.browser.cjs.map +2 -2
  47. package/dist/experimental/index.node.cjs.map +2 -2
  48. package/dist/index.browser.cjs +721 -267
  49. package/dist/index.browser.cjs.map +4 -4
  50. package/dist/index.node.cjs +986 -286
  51. package/dist/index.node.cjs.map +4 -4
  52. package/package.json +17 -18
@@ -29,11 +29,19 @@ const UnMemoizedGallery = (props) => {
29
29
  images[lastImageIndexInPreview].image_url ||
30
30
  images[lastImageIndexInPreview].thumb_url})`,
31
31
  ...image.style,
32
- }, ...(innerRefs?.current && { ref: (r) => (innerRefs.current[i] = r) }) },
32
+ }, ...(innerRefs?.current && {
33
+ ref: (r) => {
34
+ innerRefs.current[i] = r;
35
+ },
36
+ }) },
33
37
  React.createElement("p", null, t('{{ imageCount }} more', {
34
38
  imageCount: images.length - countImagesDisplayedInPreview,
35
39
  })))) : (React.createElement("button", { className: 'str-chat__gallery-image', "data-testid": 'gallery-image', key: `gallery-image-${i}`, onClick: () => toggleModal(i) },
36
- React.createElement(BaseImage, { alt: image?.fallback || imageFallbackTitle, src: sanitizeUrl(image.previewUrl || image.image_url || image.thumb_url), style: image.style, title: image?.fallback || imageFallbackTitle, ...(innerRefs?.current && { ref: (r) => (innerRefs.current[i] = r) }) }))));
40
+ React.createElement(BaseImage, { alt: image?.fallback || imageFallbackTitle, src: sanitizeUrl(image.previewUrl || image.image_url || image.thumb_url), style: image.style, title: image?.fallback || imageFallbackTitle, ...(innerRefs?.current && {
41
+ ref: (r) => {
42
+ innerRefs.current[i] = r;
43
+ },
44
+ }) }))));
37
45
  const className = clsx('str-chat__gallery', {
38
46
  'str-chat__gallery--square': images.length > lastImageIndexInPreview,
39
47
  'str-chat__gallery-two-rows': images.length > 2,
@@ -18,8 +18,9 @@ export const ModalGallery = (props) => {
18
18
  originalAlt: t('User uploaded content'),
19
19
  source: imageSrc,
20
20
  };
21
- }),
22
- // eslint-disable-next-line react-hooks/exhaustive-deps
23
- [images]);
24
- return (React.createElement(ImageGallery, { items: formattedArray, renderItem: renderItem, showIndex: true, showPlayButton: false, showThumbnails: false, startIndex: index }));
21
+ }), [images, t]);
22
+ return (
23
+ // ignore the TS error as react-image-gallery was on @types/react@18 while stream-chat-react being upgraded to React 19 (https://github.com/xiaolin/react-image-gallery/issues/809)
24
+ // @ts-expect-error
25
+ React.createElement(ImageGallery, { items: formattedArray, renderItem: renderItem, showIndex: true, showPlayButton: false, showThumbnails: false, startIndex: index }));
25
26
  };
@@ -26,10 +26,10 @@ export const InfiniteScroll = (props) => {
26
26
  const loadPreviousPageFn = loadPreviousPage || loadMore;
27
27
  const hasNextPageFlag = hasNextPage || hasMoreNewer;
28
28
  const hasPreviousPageFlag = hasPreviousPage || hasMore;
29
- const scrollComponent = useRef();
30
- const previousOffset = useRef();
31
- const previousReverseOffset = useRef();
32
- const scrollListenerRef = useRef();
29
+ const scrollComponent = useRef(undefined);
30
+ const previousOffset = useRef(undefined);
31
+ const previousReverseOffset = useRef(undefined);
32
+ const scrollListenerRef = useRef(undefined);
33
33
  scrollListenerRef.current = () => {
34
34
  const element = scrollComponent.current;
35
35
  if (!element || element.offsetParent === null) {
@@ -11,7 +11,7 @@ export declare function useMessageActionsBoxPopper<T extends HTMLElement>({ open
11
11
  [key: string]: string;
12
12
  } | undefined;
13
13
  };
14
- popperElementRef: import("react").RefObject<T>;
14
+ popperElementRef: import("react").RefObject<T | null>;
15
15
  styles: {
16
16
  [key: string]: import("react").CSSProperties;
17
17
  };
@@ -6,5 +6,5 @@ import type { EnrichURLsController } from './useLinkPreviews';
6
6
  export declare const useMessageInputText: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger>(props: MessageInputProps<StreamChatGenerics, V>, state: MessageInputState<StreamChatGenerics>, dispatch: React.Dispatch<MessageInputReducerAction<StreamChatGenerics>>, findAndEnqueueURLsToEnrich?: EnrichURLsController['findAndEnqueueURLsToEnrich']) => {
7
7
  handleChange: import("react").ChangeEventHandler<HTMLTextAreaElement>;
8
8
  insertText: (textToInsert: string) => void;
9
- textareaRef: import("react").MutableRefObject<HTMLTextAreaElement | undefined>;
9
+ textareaRef: import("react").RefObject<HTMLTextAreaElement | undefined>;
10
10
  };
@@ -5,7 +5,7 @@ export const useMessageInputText = (props, state, dispatch, findAndEnqueueURLsTo
5
5
  const { channel } = useChannelStateContext('useMessageInputText');
6
6
  const { additionalTextareaProps, focus, parent, publishTypingEvent = true } = props;
7
7
  const { text } = state;
8
- const textareaRef = useRef();
8
+ const textareaRef = useRef(undefined);
9
9
  // Focus
10
10
  useEffect(() => {
11
11
  if (focus && textareaRef.current) {
@@ -13,7 +13,7 @@ export const useMessageInputText = (props, state, dispatch, findAndEnqueueURLsTo
13
13
  }
14
14
  }, [focus]);
15
15
  // Text + cursor position
16
- const newCursorPosition = useRef();
16
+ const newCursorPosition = useRef(undefined);
17
17
  const insertText = useCallback((textToInsert) => {
18
18
  const { maxLength } = additionalTextareaProps || {};
19
19
  if (!textareaRef.current) {
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  // todo: provide start timestamp
3
3
  export const useTimeElapsed = ({ startOnMount } = {}) => {
4
4
  const [secondsElapsed, setSecondsElapsed] = useState(0);
5
- const updateInterval = useRef();
5
+ const updateInterval = useRef(undefined);
6
6
  const startCounter = useCallback(() => {
7
7
  if (updateInterval.current)
8
8
  return;
@@ -42,7 +42,6 @@ const MessageListWithContext = (props) => {
42
42
  useMarkRead({
43
43
  isMessageListScrolledToBottom,
44
44
  messageListIsThread: threadList,
45
- unreadCount: channelUnreadUiState?.unread_messages ?? 0,
46
45
  wasMarkedUnread: !!channelUnreadUiState?.first_unread_message_id,
47
46
  });
48
47
  const { messageGroupStyles, messages: enrichedMessages } = useEnrichedMessages({
@@ -25,7 +25,7 @@ export type VirtuosoContext<StreamChatGenerics extends DefaultStreamChatGenerics
25
25
  /** The original message list enriched with date separators, omitted deleted messages or giphy previews. */
26
26
  processedMessages: StreamMessage<StreamChatGenerics>[];
27
27
  /** Instance of VirtuosoHandle object providing the API to navigate in the virtualized list by various scroll actions. */
28
- virtuosoRef: RefObject<VirtuosoHandle>;
28
+ virtuosoRef: RefObject<VirtuosoHandle | null>;
29
29
  /** Message id which was marked as unread. ALl the messages following this message are considered unrea. */
30
30
  firstUnreadMessageId?: string;
31
31
  lastReadDate?: Date;
@@ -19,6 +19,7 @@ import { useChannelActionContext, } from '../../context/ChannelActionContext';
19
19
  import { useChannelStateContext, } from '../../context/ChannelStateContext';
20
20
  import { useChatContext } from '../../context/ChatContext';
21
21
  import { useComponentContext } from '../../context/ComponentContext';
22
+ import { VirtualizedMessageListContextProvider } from '../../context/VirtualizedMessageListContext';
22
23
  import { DEFAULT_NEXT_CHANNEL_PAGE_SIZE } from '../../constants/limits';
23
24
  function captureResizeObserverExceededError(e) {
24
25
  if (e.message === 'ResizeObserver loop completed with undelivered notifications.' ||
@@ -122,7 +123,6 @@ const VirtualizedMessageListWithContext = (props) => {
122
123
  useMarkRead({
123
124
  isMessageListScrolledToBottom,
124
125
  messageListIsThread: !!threadList,
125
- unreadCount: channelUnreadUiState?.unread_messages ?? 0,
126
126
  wasMarkedUnread: !!channelUnreadUiState?.first_unread_message_id,
127
127
  });
128
128
  const scrollToBottom = useCallback(async () => {
@@ -192,7 +192,7 @@ const VirtualizedMessageListWithContext = (props) => {
192
192
  const dialogManagerId = threadList
193
193
  ? 'virtualized-message-list-dialog-manager-thread'
194
194
  : 'virtualized-message-list-dialog-manager';
195
- return (React.createElement(React.Fragment, null,
195
+ return (React.createElement(VirtualizedMessageListContextProvider, { value: { scrollToBottom } },
196
196
  React.createElement(MessageListMainPanel, null,
197
197
  React.createElement(DialogManagerProvider, { id: dialogManagerId },
198
198
  !threadList && showUnreadMessagesNotification && (React.createElement(UnreadMessagesNotification, { unreadCount: channelUnreadUiState?.unread_messages })),
@@ -8,7 +8,7 @@ export function useMessageListScrollManager(params) {
8
8
  offsetHeight: 0,
9
9
  scrollHeight: 0,
10
10
  });
11
- const messages = useRef();
11
+ const messages = useRef(undefined);
12
12
  const scrollTop = useRef(0);
13
13
  useLayoutEffect(() => {
14
14
  const prevMeasures = measures.current;
@@ -4,7 +4,7 @@ export const useMessageSetKey = ({ messages, }) => {
4
4
  * Logic to update the key of the virtuoso component when the list jumps to a new location.
5
5
  */
6
6
  const [messageSetKey, setMessageSetKey] = useState(+new Date());
7
- const firstMessageId = useRef();
7
+ const firstMessageId = useRef(undefined);
8
8
  useEffect(() => {
9
9
  const continuousSet = messages?.find((message) => message.id === firstMessageId.current);
10
10
  if (!continuousSet) {
@@ -2,7 +2,7 @@
2
2
  import type { StreamMessage } from '../../../../context/ChannelStateContext';
3
3
  import type { DefaultStreamChatGenerics } from '../../../../types/types';
4
4
  export declare function useNewMessageNotification<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(messages: StreamMessage<StreamChatGenerics>[], currentUserId: string | undefined, hasMoreNewer?: boolean): {
5
- atBottom: import("react").MutableRefObject<boolean>;
5
+ atBottom: import("react").RefObject<boolean>;
6
6
  isMessageListScrolledToBottom: boolean;
7
7
  newMessagesNotification: boolean;
8
8
  setIsMessageListScrolledToBottom: import("react").Dispatch<import("react").SetStateAction<boolean>>;
@@ -5,8 +5,8 @@ const STATUSES_EXCLUDED_FROM_PREPEND = {
5
5
  };
6
6
  export function usePrependedMessagesCount(messages, hasDateSeparator) {
7
7
  const firstRealMessageIndex = hasDateSeparator ? 1 : 0;
8
- const firstMessageOnFirstLoadedPage = useRef();
9
- const previousFirstMessageOnFirstLoadedPage = useRef();
8
+ const firstMessageOnFirstLoadedPage = useRef(undefined);
9
+ const previousFirstMessageOnFirstLoadedPage = useRef(undefined);
10
10
  const previousNumItemsPrepended = useRef(0);
11
11
  const numItemsPrepended = useMemo(() => {
12
12
  if (!messages || !messages.length) {
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
  export const useScrollToBottomOnNewMessage = ({ messages, scrollToBottom, scrollToLatestMessageOnFocus, }) => {
3
3
  const [newMessagesReceivedInBackground, setNewMessagesReceivedInBackground] = useState(false);
4
- const scrollToBottomIfConfigured = useRef();
4
+ const scrollToBottomIfConfigured = useRef(undefined);
5
5
  scrollToBottomIfConfigured.current = (event) => {
6
6
  if (!scrollToLatestMessageOnFocus ||
7
7
  !newMessagesReceivedInBackground ||
@@ -1,8 +1,7 @@
1
- import { DefaultStreamChatGenerics } from '../../../types';
1
+ import type { DefaultStreamChatGenerics } from '../../../types';
2
2
  type UseMarkReadParams = {
3
3
  isMessageListScrolledToBottom: boolean;
4
4
  messageListIsThread: boolean;
5
- unreadCount: number;
6
5
  wasMarkedUnread?: boolean;
7
6
  };
8
7
  /**
@@ -12,8 +11,7 @@ type UseMarkReadParams = {
12
11
  * 3. the channel was not marked unread by the user
13
12
  * @param isMessageListScrolledToBottom
14
13
  * @param messageListIsThread
15
- * @param unreadCount
16
14
  * @param wasChannelMarkedUnread
17
15
  */
18
- export declare const useMarkRead: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ isMessageListScrolledToBottom, messageListIsThread, unreadCount, wasMarkedUnread, }: UseMarkReadParams) => void;
16
+ export declare const useMarkRead: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ isMessageListScrolledToBottom, messageListIsThread, wasMarkedUnread, }: UseMarkReadParams) => void;
19
17
  export {};
@@ -1,5 +1,10 @@
1
- import { useEffect, useRef } from 'react';
1
+ import { useEffect } from 'react';
2
2
  import { useChannelActionContext, useChannelStateContext, useChatContext, } from '../../../context';
3
+ const hasReadLastMessage = (channel, userId) => {
4
+ const latestMessageIdInChannel = channel.state.latestMessages.slice(-1)[0]?.id;
5
+ const lastReadMessageIdServer = channel.state.read[userId]?.last_read_message_id;
6
+ return latestMessageIdInChannel === lastReadMessageIdServer;
7
+ };
3
8
  /**
4
9
  * Takes care of marking a channel read. The channel is read only if all the following applies:
5
10
  * 1. the message list is not rendered in a thread
@@ -7,29 +12,25 @@ import { useChannelActionContext, useChannelStateContext, useChatContext, } from
7
12
  * 3. the channel was not marked unread by the user
8
13
  * @param isMessageListScrolledToBottom
9
14
  * @param messageListIsThread
10
- * @param unreadCount
11
15
  * @param wasChannelMarkedUnread
12
16
  */
13
- export const useMarkRead = ({ isMessageListScrolledToBottom, messageListIsThread, unreadCount, wasMarkedUnread, }) => {
17
+ export const useMarkRead = ({ isMessageListScrolledToBottom, messageListIsThread, wasMarkedUnread, }) => {
14
18
  const { client } = useChatContext('useMarkRead');
15
19
  const { markRead, setChannelUnreadUiState } = useChannelActionContext('useMarkRead');
16
20
  const { channel } = useChannelStateContext('useMarkRead');
17
- const previousRenderMessageListScrolledToBottom = useRef(isMessageListScrolledToBottom);
18
21
  useEffect(() => {
19
- const shouldMarkRead = (unreadMessages) => !document.hidden &&
22
+ const shouldMarkRead = () => !document.hidden &&
20
23
  !wasMarkedUnread &&
21
24
  !messageListIsThread &&
22
25
  isMessageListScrolledToBottom &&
23
- unreadMessages > 0;
26
+ client.user?.id &&
27
+ !hasReadLastMessage(channel, client.user.id);
24
28
  const onVisibilityChange = () => {
25
- if (shouldMarkRead(channel.countUnread()))
29
+ if (shouldMarkRead())
26
30
  markRead();
27
31
  };
28
32
  const handleMessageNew = (event) => {
29
- const isOwnMessage = event.user?.id && event.user.id === client.user?.id;
30
33
  const mainChannelUpdated = !event.message?.parent_id || event.message?.show_in_channel;
31
- if (isOwnMessage)
32
- return;
33
34
  if (!isMessageListScrolledToBottom || wasMarkedUnread || document.hidden) {
34
35
  setChannelUnreadUiState((prev) => {
35
36
  const previousUnreadCount = prev?.unread_messages ?? 0;
@@ -44,17 +45,15 @@ export const useMarkRead = ({ isMessageListScrolledToBottom, messageListIsThread
44
45
  };
45
46
  });
46
47
  }
47
- else if (mainChannelUpdated && shouldMarkRead(channel.countUnread())) {
48
+ else if (mainChannelUpdated && shouldMarkRead()) {
48
49
  markRead();
49
50
  }
50
51
  };
51
52
  channel.on('message.new', handleMessageNew);
52
53
  document.addEventListener('visibilitychange', onVisibilityChange);
53
- const hasScrolledToBottom = previousRenderMessageListScrolledToBottom.current !== isMessageListScrolledToBottom &&
54
- isMessageListScrolledToBottom;
55
- if (hasScrolledToBottom && shouldMarkRead(channel.countUnread()))
54
+ if (shouldMarkRead()) {
56
55
  markRead();
57
- previousRenderMessageListScrolledToBottom.current = isMessageListScrolledToBottom;
56
+ }
58
57
  return () => {
59
58
  channel.off('message.new', handleMessageNew);
60
59
  document.removeEventListener('visibilitychange', onVisibilityChange);
@@ -66,7 +65,6 @@ export const useMarkRead = ({ isMessageListScrolledToBottom, messageListIsThread
66
65
  markRead,
67
66
  messageListIsThread,
68
67
  setChannelUnreadUiState,
69
- unreadCount,
70
68
  wasMarkedUnread,
71
69
  ]);
72
70
  };
@@ -0,0 +1,13 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ export type VirtualizedMessageListContextValue = {
3
+ /** Function that scrolls the list to the bottom. */
4
+ scrollToBottom: () => void;
5
+ };
6
+ export declare const VirtualizedMessageListContext: React.Context<VirtualizedMessageListContextValue | undefined>;
7
+ /**
8
+ * Context provider for components rendered within the `VirtualizedMessageList`
9
+ */
10
+ export declare const VirtualizedMessageListContextProvider: ({ children, value, }: PropsWithChildren<{
11
+ value: VirtualizedMessageListContextValue;
12
+ }>) => React.JSX.Element;
13
+ export declare const useVirtualizedMessageListContext: () => VirtualizedMessageListContextValue;
@@ -0,0 +1,7 @@
1
+ import React, { createContext, useContext } from 'react';
2
+ export const VirtualizedMessageListContext = createContext(undefined);
3
+ /**
4
+ * Context provider for components rendered within the `VirtualizedMessageList`
5
+ */
6
+ export const VirtualizedMessageListContextProvider = ({ children, value, }) => (React.createElement(VirtualizedMessageListContext.Provider, { value: value }, children));
7
+ export const useVirtualizedMessageListContext = () => useContext(VirtualizedMessageListContext);