stream-chat-react 12.7.0 → 12.8.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 (56) hide show
  1. package/dist/components/ChannelList/ChannelList.d.ts +3 -2
  2. package/dist/components/ChannelList/ChannelList.js +29 -34
  3. package/dist/components/ChannelList/hooks/index.d.ts +1 -0
  4. package/dist/components/ChannelList/hooks/index.js +1 -0
  5. package/dist/components/ChannelList/hooks/useChannelListShape.d.ts +60 -0
  6. package/dist/components/ChannelList/hooks/useChannelListShape.js +344 -0
  7. package/dist/components/ChannelList/hooks/useChannelMembershipState.d.ts +2 -0
  8. package/dist/components/ChannelList/hooks/useChannelMembershipState.js +15 -0
  9. package/dist/components/ChannelList/utils.d.ts +41 -5
  10. package/dist/components/ChannelList/utils.js +85 -0
  11. package/dist/components/ChannelPreview/ChannelPreview.js +2 -2
  12. package/dist/components/ChannelPreview/ChannelPreviewActionButtons.d.ts +6 -0
  13. package/dist/components/ChannelPreview/ChannelPreviewActionButtons.js +30 -0
  14. package/dist/components/ChannelPreview/ChannelPreviewMessenger.d.ts +2 -2
  15. package/dist/components/ChannelPreview/ChannelPreviewMessenger.js +14 -9
  16. package/dist/components/ChannelPreview/icons.d.ts +6 -0
  17. package/dist/components/ChannelPreview/icons.js +8 -0
  18. package/dist/components/ChannelPreview/index.d.ts +1 -0
  19. package/dist/components/ChannelPreview/index.js +1 -0
  20. package/dist/components/ChannelPreview/utils.d.ts +2 -1
  21. package/dist/components/ChannelPreview/utils.js +2 -2
  22. package/dist/components/Chat/Chat.d.ts +2 -1
  23. package/dist/components/Chat/Chat.js +2 -1
  24. package/dist/components/Chat/hooks/useChat.js +1 -1
  25. package/dist/components/Chat/hooks/useCreateChatContext.js +3 -1
  26. package/dist/components/Message/Message.js +2 -1
  27. package/dist/components/Message/MessageSimple.js +8 -4
  28. package/dist/components/Message/utils.d.ts +1 -1
  29. package/dist/components/Message/utils.js +1 -1
  30. package/dist/context/ChatContext.d.ts +1 -1
  31. package/dist/context/ComponentContext.d.ts +3 -1
  32. package/dist/context/MessageContext.d.ts +4 -0
  33. package/dist/css/v2/index.css +1 -1
  34. package/dist/css/v2/index.layout.css +1 -1
  35. package/dist/experimental/index.browser.cjs.map +2 -2
  36. package/dist/experimental/index.node.cjs.map +2 -2
  37. package/dist/i18n/Streami18n.d.ts +2 -0
  38. package/dist/i18n/de.json +2 -0
  39. package/dist/i18n/en.json +2 -0
  40. package/dist/i18n/es.json +2 -0
  41. package/dist/i18n/fr.json +2 -0
  42. package/dist/i18n/hi.json +2 -0
  43. package/dist/i18n/it.json +2 -0
  44. package/dist/i18n/ja.json +2 -0
  45. package/dist/i18n/ko.json +2 -0
  46. package/dist/i18n/nl.json +2 -0
  47. package/dist/i18n/pt.json +2 -0
  48. package/dist/i18n/ru.json +2 -0
  49. package/dist/i18n/tr.json +2 -0
  50. package/dist/index.browser.cjs +10304 -9700
  51. package/dist/index.browser.cjs.map +4 -4
  52. package/dist/index.node.cjs +10230 -9618
  53. package/dist/index.node.cjs.map +4 -4
  54. package/dist/scss/v2/ChannelPreview/ChannelPreview-layout.scss +24 -0
  55. package/dist/scss/v2/ChannelPreview/ChannelPreview-theme.scss +18 -0
  56. package/package.json +4 -4
@@ -1,5 +1,8 @@
1
1
  import uniqBy from 'lodash.uniqby';
2
2
  export const MAX_QUERY_CHANNELS_LIMIT = 30;
3
+ /**
4
+ * @deprecated
5
+ */
3
6
  export const moveChannelUp = ({ activeChannel, channels, cid, }) => {
4
7
  // get index of channel to move up
5
8
  const channelIndex = channels.findIndex((channel) => channel.cid === cid);
@@ -9,3 +12,85 @@ export const moveChannelUp = ({ activeChannel, channels, cid, }) => {
9
12
  const channel = activeChannel || channels[channelIndex];
10
13
  return uniqBy([channel, ...channels], 'cid');
11
14
  };
15
+ /**
16
+ * Expects channel array sorted by `{ pinned_at: -1 }`.
17
+ *
18
+ * TODO: add support for the `{ pinned_at: 1 }`
19
+ */
20
+ export function findLastPinnedChannelIndex({ channels, }) {
21
+ let lastPinnedChannelIndex = null;
22
+ for (const channel of channels) {
23
+ if (!isChannelPinned(channel))
24
+ break;
25
+ if (typeof lastPinnedChannelIndex === 'number') {
26
+ lastPinnedChannelIndex++;
27
+ }
28
+ else {
29
+ lastPinnedChannelIndex = 0;
30
+ }
31
+ }
32
+ return lastPinnedChannelIndex;
33
+ }
34
+ /**
35
+ * This function should not be used to move pinned already channels.
36
+ */
37
+ export const moveChannelUpwards = ({ channels, channelToMove, channelToMoveIndexWithinChannels, sort, }) => {
38
+ // get index of channel to move up
39
+ const targetChannelIndex = channelToMoveIndexWithinChannels ??
40
+ channels.findIndex((channel) => channel.cid === channelToMove.cid);
41
+ const targetChannelExistsWithinList = targetChannelIndex >= 0;
42
+ const targetChannelAlreadyAtTheTop = targetChannelIndex === 0;
43
+ // pinned channels should not move within the list based on recent activity, channels which
44
+ // receive messages and are not pinned should move upwards but only under the last pinned channel
45
+ // in the list
46
+ const considerPinnedChannels = shouldConsiderPinnedChannels(sort);
47
+ if (targetChannelAlreadyAtTheTop)
48
+ return channels;
49
+ const newChannels = [...channels];
50
+ // target channel index is known, remove it from the list
51
+ if (targetChannelExistsWithinList) {
52
+ newChannels.splice(targetChannelIndex, 1);
53
+ }
54
+ // as position of pinned channels has to stay unchanged, we need to
55
+ // find last pinned channel in the list to move the target channel after
56
+ let lastPinnedChannelIndex = null;
57
+ if (considerPinnedChannels) {
58
+ lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels });
59
+ }
60
+ // re-insert it at the new place (to specific index if pinned channels are considered)
61
+ newChannels.splice(typeof lastPinnedChannelIndex === 'number' ? lastPinnedChannelIndex + 1 : 0, 0, channelToMove);
62
+ return newChannels;
63
+ };
64
+ /**
65
+ * Returns true only if `{ pinned_at: -1 }` or `{ pinned_at: 1 }` option is first within the `sort` array.
66
+ */
67
+ export const shouldConsiderPinnedChannels = (sort) => {
68
+ if (!sort)
69
+ return false;
70
+ if (!Array.isArray(sort))
71
+ return false;
72
+ const [option] = sort;
73
+ if (!option?.pinned_at)
74
+ return false;
75
+ return Math.abs(option.pinned_at) === 1;
76
+ };
77
+ /**
78
+ * Returns `true` only if `archived` property is set to `false` within `filters`.
79
+ */
80
+ export const shouldConsiderArchivedChannels = (filters) => {
81
+ if (!filters)
82
+ return false;
83
+ return !filters.archived;
84
+ };
85
+ export const isChannelPinned = (channel) => {
86
+ if (!channel)
87
+ return false;
88
+ const member = channel.state.membership;
89
+ return !!member?.pinned_at;
90
+ };
91
+ export const isChannelArchived = (channel) => {
92
+ if (!channel)
93
+ return false;
94
+ const member = channel.state.membership;
95
+ return !!member?.archived_at;
96
+ };
@@ -9,7 +9,7 @@ import { useTranslationContext } from '../../context/TranslationContext';
9
9
  import { useMessageDeliveryStatus } from './hooks/useMessageDeliveryStatus';
10
10
  export const ChannelPreview = (props) => {
11
11
  const { channel, Preview = ChannelPreviewMessenger, channelUpdateCount, getLatestMessagePreview = defaultGetLatestMessagePreview, } = props;
12
- const { channel: activeChannel, client, setActiveChannel } = useChatContext('ChannelPreview');
12
+ const { channel: activeChannel, client, isMessageAIGenerated, setActiveChannel, } = useChatContext('ChannelPreview');
13
13
  const { t, userLanguage } = useTranslationContext('ChannelPreview');
14
14
  const { displayImage, displayTitle, groupChannelDisplayInfo } = useChannelPreviewInfo({
15
15
  channel,
@@ -75,6 +75,6 @@ export const ChannelPreview = (props) => {
75
75
  }, [channel, refreshUnreadCount, channelUpdateCount]);
76
76
  if (!Preview)
77
77
  return null;
78
- const latestMessagePreview = getLatestMessagePreview(channel, t, userLanguage);
78
+ const latestMessagePreview = getLatestMessagePreview(channel, t, userLanguage, isMessageAIGenerated);
79
79
  return (React.createElement(Preview, { ...props, active: isActive, displayImage: displayImage, displayTitle: displayTitle, groupChannelDisplayInfo: groupChannelDisplayInfo, lastMessage: lastMessage, latestMessage: latestMessagePreview, latestMessagePreview: latestMessagePreview, messageDeliveryStatus: messageDeliveryStatus, setActiveChannel: setActiveChannel, unread: unread }));
80
80
  };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { Channel, ExtendableGenerics } from 'stream-chat';
3
+ export type ChannelPreviewActionButtonsProps<SCG extends ExtendableGenerics> = {
4
+ channel: Channel<SCG>;
5
+ };
6
+ export declare function ChannelPreviewActionButtons<SCG extends ExtendableGenerics>({ channel, }: ChannelPreviewActionButtonsProps<SCG>): React.JSX.Element;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import clsx from 'clsx';
3
+ import { useChannelMembershipState } from '../ChannelList';
4
+ import { Icon } from './icons';
5
+ import { useTranslationContext } from '../../context';
6
+ export function ChannelPreviewActionButtons({ channel, }) {
7
+ const membership = useChannelMembershipState(channel);
8
+ const { t } = useTranslationContext();
9
+ return (React.createElement("div", { className: 'str-chat__channel-preview__action-buttons' },
10
+ React.createElement("button", { "aria-label": membership.pinned_at ? t('Unpin') : t('Pin'), className: clsx('str-chat__channel-preview__action-button', 'str-chat__channel-preview__action-button--pin', membership.pinned_at && 'str-chat__channel-preview__action-button--active'), onClick: (e) => {
11
+ e.stopPropagation();
12
+ if (membership.pinned_at) {
13
+ channel.unpin();
14
+ }
15
+ else {
16
+ channel.pin();
17
+ }
18
+ }, title: membership.pinned_at ? t('Unpin') : t('Pin') },
19
+ React.createElement(Icon.Pin, null)),
20
+ React.createElement("button", { "aria-label": membership.archived_at ? t('Unarchive') : t('Archive'), className: clsx('str-chat__channel-preview__action-button', 'str-chat__channel-preview__action-button--archive', membership.archived_at && 'str-chat__channel-preview__action-button--active'), onClick: (e) => {
21
+ e.stopPropagation();
22
+ if (membership.archived_at) {
23
+ channel.unarchive();
24
+ }
25
+ else {
26
+ channel.archive();
27
+ }
28
+ }, title: membership.archived_at ? t('Unarchive') : t('Archive') },
29
+ React.createElement(Icon.ArchiveBox, null))));
30
+ }
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import type { ChannelPreviewUIComponentProps } from './ChannelPreview';
3
2
  import type { DefaultStreamChatGenerics } from '../../types/types';
3
+ import type { ChannelPreviewUIComponentProps } from './ChannelPreview';
4
4
  /**
5
5
  * Used as preview component for channel item in [ChannelList](#channellist) component.
6
6
  * Its best suited for messenger type chat.
7
7
  */
8
- export declare const ChannelPreviewMessenger: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: ChannelPreviewUIComponentProps<StreamChatGenerics>) => React.JSX.Element;
8
+ export declare const ChannelPreviewMessenger: <SCG extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: ChannelPreviewUIComponentProps<SCG>) => React.JSX.Element;
@@ -1,8 +1,11 @@
1
1
  import React, { useRef } from 'react';
2
2
  import clsx from 'clsx';
3
+ import { ChannelPreviewActionButtons as DefaultChannelPreviewActionButtons } from './ChannelPreviewActionButtons';
3
4
  import { Avatar as DefaultAvatar } from '../Avatar';
5
+ import { useComponentContext } from '../../context';
4
6
  const UnMemoizedChannelPreviewMessenger = (props) => {
5
7
  const { active, Avatar = DefaultAvatar, channel, className: customClassName = '', displayImage, displayTitle, groupChannelDisplayInfo, latestMessagePreview, onSelect: customOnSelectChannel, setActiveChannel, unread, watchers, } = props;
8
+ const { ChannelPreviewActionButtons = DefaultChannelPreviewActionButtons, } = useComponentContext();
6
9
  const channelPreviewButton = useRef(null);
7
10
  const avatarName = displayTitle || channel.state.messages[channel.state.messages.length - 1]?.user?.id;
8
11
  const onSelectChannel = (e) => {
@@ -16,15 +19,17 @@ const UnMemoizedChannelPreviewMessenger = (props) => {
16
19
  channelPreviewButton.current.blur();
17
20
  }
18
21
  };
19
- return (React.createElement("button", { "aria-label": `Select Channel: ${displayTitle || ''}`, "aria-selected": active, className: clsx(`str-chat__channel-preview-messenger str-chat__channel-preview`, active && 'str-chat__channel-preview-messenger--active', unread && unread >= 1 && 'str-chat__channel-preview-messenger--unread', customClassName), "data-testid": 'channel-preview-button', onClick: onSelectChannel, ref: channelPreviewButton, role: 'option' },
20
- React.createElement("div", { className: 'str-chat__channel-preview-messenger--left' },
21
- React.createElement(Avatar, { className: 'str-chat__avatar--channel-preview', groupChannelDisplayInfo: groupChannelDisplayInfo, image: displayImage, name: avatarName })),
22
- React.createElement("div", { className: 'str-chat__channel-preview-end' },
23
- React.createElement("div", { className: 'str-chat__channel-preview-end-first-row' },
24
- React.createElement("div", { className: 'str-chat__channel-preview-messenger--name' },
25
- React.createElement("span", null, displayTitle)),
26
- !!unread && (React.createElement("div", { className: 'str-chat__channel-preview-unread-badge', "data-testid": 'unread-badge' }, unread))),
27
- React.createElement("div", { className: 'str-chat__channel-preview-messenger--last-message' }, latestMessagePreview))));
22
+ return (React.createElement("div", { className: 'str-chat__channel-preview-container' },
23
+ React.createElement(ChannelPreviewActionButtons, { channel: channel }),
24
+ React.createElement("button", { "aria-label": `Select Channel: ${displayTitle || ''}`, "aria-selected": active, className: clsx(`str-chat__channel-preview-messenger str-chat__channel-preview`, active && 'str-chat__channel-preview-messenger--active', unread && unread >= 1 && 'str-chat__channel-preview-messenger--unread', customClassName), "data-testid": 'channel-preview-button', onClick: onSelectChannel, ref: channelPreviewButton, role: 'option' },
25
+ React.createElement("div", { className: 'str-chat__channel-preview-messenger--left' },
26
+ React.createElement(Avatar, { className: 'str-chat__avatar--channel-preview', groupChannelDisplayInfo: groupChannelDisplayInfo, image: displayImage, name: avatarName })),
27
+ React.createElement("div", { className: 'str-chat__channel-preview-end' },
28
+ React.createElement("div", { className: 'str-chat__channel-preview-end-first-row' },
29
+ React.createElement("div", { className: 'str-chat__channel-preview-messenger--name' },
30
+ React.createElement("span", null, displayTitle)),
31
+ !!unread && (React.createElement("div", { className: 'str-chat__channel-preview-unread-badge', "data-testid": 'unread-badge' }, unread))),
32
+ React.createElement("div", { className: 'str-chat__channel-preview-messenger--last-message' }, latestMessagePreview)))));
28
33
  };
29
34
  /**
30
35
  * Used as preview component for channel item in [ChannelList](#channellist) component.
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import { ComponentPropsWithoutRef } from 'react';
3
+ export declare const Icon: {
4
+ ArchiveBox: (props: ComponentPropsWithoutRef<'svg'>) => React.JSX.Element;
5
+ Pin: (props: ComponentPropsWithoutRef<'svg'>) => React.JSX.Element;
6
+ };
@@ -0,0 +1,8 @@
1
+ /* eslint-disable react/display-name */
2
+ import React from 'react';
3
+ export const Icon = {
4
+ ArchiveBox: (props) => (React.createElement("svg", { className: 'str-chat__icon str-chat__icon--archive-box', fill: 'currentColor', viewBox: '0 0 512 512', xmlns: 'http://www.w3.org/2000/svg', ...props },
5
+ React.createElement("path", { d: 'M32 32l448 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96L0 64C0 46.3 14.3 32 32 32zm0 128l448 0 0 256c0 35.3-28.7 64-64 64L96 480c-35.3 0-64-28.7-64-64l0-256zm128 80c0 8.8 7.2 16 16 16l160 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-160 0c-8.8 0-16 7.2-16 16z' }))),
6
+ Pin: (props) => (React.createElement("svg", { className: 'str-chat__icon str-chat__icon--pin', fill: 'currentColor', viewBox: '0 0 384 512', xmlns: 'http://www.w3.org/2000/svg', ...props },
7
+ React.createElement("path", { d: 'M32 32C32 14.3 46.3 0 64 0L320 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-29.5 0 11.4 148.2c36.7 19.9 65.7 53.2 79.5 94.7l1 3c3.3 9.8 1.6 20.5-4.4 28.8s-15.7 13.3-26 13.3L32 352c-10.3 0-19.9-4.9-26-13.3s-7.7-19.1-4.4-28.8l1-3c13.8-41.5 42.8-74.8 79.5-94.7L93.5 64 64 64C46.3 64 32 49.7 32 32zM160 384l64 0 0 96c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-96z' }))),
8
+ };
@@ -1,4 +1,5 @@
1
1
  export * from './ChannelPreview';
2
2
  export * from './ChannelPreviewMessenger';
3
+ export * from './ChannelPreviewActionButtons';
3
4
  export * from './hooks';
4
5
  export * from './utils';
@@ -1,4 +1,5 @@
1
1
  export * from './ChannelPreview';
2
2
  export * from './ChannelPreviewMessenger';
3
+ export * from './ChannelPreviewActionButtons';
3
4
  export * from './hooks';
4
5
  export * from './utils';
@@ -2,8 +2,9 @@ import React from 'react';
2
2
  import type { Channel, UserResponse } from 'stream-chat';
3
3
  import type { TranslationContextValue } from '../../context/TranslationContext';
4
4
  import type { DefaultStreamChatGenerics } from '../../types/types';
5
+ import { ChatContextValue } from '../../context';
5
6
  export declare const renderPreviewText: (text: string) => React.JSX.Element;
6
- export declare const getLatestMessagePreview: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage?: TranslationContextValue['userLanguage']) => string | JSX.Element;
7
+ export declare const getLatestMessagePreview: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage?: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue<StreamChatGenerics>['isMessageAIGenerated']) => string | JSX.Element;
7
8
  export type GroupChannelDisplayInfo = {
8
9
  image?: string;
9
10
  name?: string;
@@ -12,7 +12,7 @@ const getLatestPollVote = (latestVotesByOption) => {
12
12
  }
13
13
  return latestVote;
14
14
  };
15
- export const getLatestMessagePreview = (channel, t, userLanguage = 'en') => {
15
+ export const getLatestMessagePreview = (channel, t, userLanguage = 'en', isMessageAIGenerated) => {
16
16
  const latestMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
17
17
  const previewTextToRender = latestMessage?.i18n?.[`${userLanguage}_text`] ||
18
18
  latestMessage?.text;
@@ -47,7 +47,7 @@ export const getLatestMessagePreview = (channel, t, userLanguage = 'en') => {
47
47
  }
48
48
  }
49
49
  if (previewTextToRender) {
50
- return latestMessage.ai_generated
50
+ return isMessageAIGenerated?.(latestMessage)
51
51
  ? previewTextToRender
52
52
  : renderPreviewText(previewTextToRender);
53
53
  }
@@ -4,6 +4,7 @@ import type { StreamChat } from 'stream-chat';
4
4
  import type { SupportedTranslations } from '../../i18n/types';
5
5
  import type { Streami18n } from '../../i18n/Streami18n';
6
6
  import type { DefaultStreamChatGenerics } from '../../types/types';
7
+ import type { MessageContextValue } from '../../context';
7
8
  export type ChatProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
8
9
  /** The StreamChat client object */
9
10
  client: StreamChat<StreamChatGenerics>;
@@ -25,7 +26,7 @@ export type ChatProps<StreamChatGenerics extends DefaultStreamChatGenerics = Def
25
26
  * Note: requires importing `stream-chat-react/css/v2/emoji-replacement.css` style sheet
26
27
  */
27
28
  useImageFlagEmojisOnWindows?: boolean;
28
- };
29
+ } & Partial<Pick<MessageContextValue<StreamChatGenerics>, 'isMessageAIGenerated'>>;
29
30
  /**
30
31
  * Wrapper component for a StreamChat application. Chat needs to be placed around any other chat components
31
32
  * as it provides the ChatContext.
@@ -9,7 +9,7 @@ import { TranslationProvider } from '../../context/TranslationContext';
9
9
  * as it provides the ChatContext.
10
10
  */
11
11
  export const Chat = (props) => {
12
- const { children, client, customClasses, defaultLanguage, i18nInstance, initialNavOpen = true, theme = 'messaging light', useImageFlagEmojisOnWindows = false, } = props;
12
+ const { children, client, customClasses, defaultLanguage, i18nInstance, initialNavOpen = true, isMessageAIGenerated, theme = 'messaging light', useImageFlagEmojisOnWindows = false, } = props;
13
13
  const { channel, closeMobileNav, getAppSettings, latestMessageDatesByChannels, mutes, navOpen, openMobileNav, setActiveChannel, translators, } = useChat({ client, defaultLanguage, i18nInstance, initialNavOpen });
14
14
  const channelsQueryState = useChannelsQueryState();
15
15
  const chatContextValue = useCreateChatContext({
@@ -19,6 +19,7 @@ export const Chat = (props) => {
19
19
  closeMobileNav,
20
20
  customClasses,
21
21
  getAppSettings,
22
+ isMessageAIGenerated,
22
23
  latestMessageDatesByChannels,
23
24
  mutes,
24
25
  navOpen,
@@ -28,7 +28,7 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
28
28
  if (!userAgent.includes('stream-chat-react')) {
29
29
  // result looks like: 'stream-chat-react-2.3.2-stream-chat-javascript-client-browser-2.2.2'
30
30
  // the upper-case text between double underscores is replaced with the actual semantic version of the library
31
- client.setUserAgent(`stream-chat-react-12.7.0-${userAgent}`);
31
+ client.setUserAgent(`stream-chat-react-12.8.0-${userAgent}`);
32
32
  }
33
33
  client.threads.registerSubscriptions();
34
34
  client.polls.registerSubscriptions();
@@ -1,6 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  export const useCreateChatContext = (value) => {
3
- const { channel, channelsQueryState, client, closeMobileNav, customClasses, getAppSettings, latestMessageDatesByChannels, mutes, navOpen, openMobileNav, setActiveChannel, theme, useImageFlagEmojisOnWindows, } = value;
3
+ const { channel, channelsQueryState, client, closeMobileNav, customClasses, getAppSettings, isMessageAIGenerated, latestMessageDatesByChannels, mutes, navOpen, openMobileNav, setActiveChannel, theme, useImageFlagEmojisOnWindows, } = value;
4
4
  const channelCid = channel?.cid;
5
5
  const channelsQueryError = channelsQueryState.error;
6
6
  const channelsQueryInProgress = channelsQueryState.queryInProgress;
@@ -14,6 +14,7 @@ export const useCreateChatContext = (value) => {
14
14
  closeMobileNav,
15
15
  customClasses,
16
16
  getAppSettings,
17
+ isMessageAIGenerated,
17
18
  latestMessageDatesByChannels,
18
19
  mutes,
19
20
  navOpen,
@@ -31,6 +32,7 @@ export const useCreateChatContext = (value) => {
31
32
  getAppSettings,
32
33
  mutedUsersLength,
33
34
  navOpen,
35
+ isMessageAIGenerated,
34
36
  ]);
35
37
  return chatContext;
36
38
  };
@@ -5,7 +5,7 @@ import { MessageProvider, useChannelActionContext, useChannelStateContext, useCh
5
5
  import { MessageSimple as DefaultMessage } from './MessageSimple';
6
6
  const MessageWithContext = (props) => {
7
7
  const { canPin, groupedByUser, Message: propMessage, message, messageActions = Object.keys(MESSAGE_ACTIONS), onUserClick: propOnUserClick, onUserHover: propOnUserHover, userRoles, } = props;
8
- const { client } = useChatContext('Message');
8
+ const { client, isMessageAIGenerated } = useChatContext('Message');
9
9
  const { read } = useChannelStateContext('Message');
10
10
  const { Message: contextMessage } = useComponentContext('Message');
11
11
  const actionsEnabled = message.type === 'regular' && message.status === 'received';
@@ -58,6 +58,7 @@ const MessageWithContext = (props) => {
58
58
  editing,
59
59
  getMessageActions: messageActionsHandler,
60
60
  handleEdit: setEdit,
61
+ isMessageAIGenerated,
61
62
  isMyMessage: () => isMyMessage,
62
63
  messageIsUnread,
63
64
  onUserClick,
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { MessageErrorIcon } from './icons';
4
4
  import { MessageBouncePrompt as DefaultMessageBouncePrompt } from '../MessageBounce';
@@ -24,7 +24,7 @@ import { useChatContext, useTranslationContext } from '../../context';
24
24
  import { MessageEditedTimestamp } from './MessageEditedTimestamp';
25
25
  import { StreamedMessageText as DefaultStreamedMessageText } from './StreamedMessageText';
26
26
  const MessageSimpleWithContext = (props) => {
27
- const { additionalMessageInputProps, clearEditingState, editing, endOfGroup, firstOfGroup, groupedByUser, handleAction, handleOpenThread, handleRetry, highlighted, isMyMessage, message, onUserClick, onUserHover, renderText, threadList, } = props;
27
+ const { additionalMessageInputProps, clearEditingState, editing, endOfGroup, firstOfGroup, groupedByUser, handleAction, handleOpenThread, handleRetry, highlighted, isMessageAIGenerated, isMyMessage, message, onUserClick, onUserHover, renderText, threadList, } = props;
28
28
  const { client } = useChatContext('MessageSimple');
29
29
  const { t } = useTranslationContext('MessageSimple');
30
30
  const [isBounceDialogOpen, setIsBounceDialogOpen] = useState(false);
@@ -35,6 +35,10 @@ const MessageSimpleWithContext = (props) => {
35
35
  MessageActions = MessageOptions, MessageDeleted = DefaultMessageDeleted, MessageBouncePrompt = DefaultMessageBouncePrompt, MessageRepliesCountButton = DefaultMessageRepliesCountButton, MessageStatus = DefaultMessageStatus, MessageTimestamp = DefaultMessageTimestamp, ReactionsList = DefaultReactionList, StreamedMessageText = DefaultStreamedMessageText, PinIndicator, } = useComponentContext('MessageSimple');
36
36
  const hasAttachment = messageHasAttachments(message);
37
37
  const hasReactions = messageHasReactions(message);
38
+ const isAIGenerated = useMemo(() => isMessageAIGenerated?.(message), [
39
+ isMessageAIGenerated,
40
+ message,
41
+ ]);
38
42
  if (message.customType === CUSTOM_MESSAGE_TYPE.date) {
39
43
  return null;
40
44
  }
@@ -45,7 +49,7 @@ const MessageSimpleWithContext = (props) => {
45
49
  const showReplyCountButton = !threadList && !!message.reply_count;
46
50
  const allowRetry = message.status === 'failed' && message.errorStatusCode !== 403;
47
51
  const isBounced = isMessageBounced(message);
48
- const isEdited = isMessageEdited(message);
52
+ const isEdited = isMessageEdited(message) && !isAIGenerated;
49
53
  let handleClick = undefined;
50
54
  if (allowRetry) {
51
55
  handleClick = () => handleRetry(message);
@@ -85,7 +89,7 @@ const MessageSimpleWithContext = (props) => {
85
89
  React.createElement("div", { className: 'str-chat__message-bubble' },
86
90
  poll && React.createElement(Poll, { poll: poll }),
87
91
  message.attachments?.length && !message.quoted_message ? (React.createElement(Attachment, { actionHandler: handleAction, attachments: message.attachments })) : null,
88
- message.ai_generated ? (React.createElement(StreamedMessageText, { message: message, renderText: renderText })) : (React.createElement(MessageText, { message: message, renderText: renderText })),
92
+ isAIGenerated ? (React.createElement(StreamedMessageText, { message: message, renderText: renderText })) : (React.createElement(MessageText, { message: message, renderText: renderText })),
89
93
  message.mml && (React.createElement(MML, { actionHandler: handleAction, align: isMyMessage() ? 'right' : 'left', source: message.mml })),
90
94
  React.createElement(MessageErrorIcon, null))),
91
95
  showReplyCountButton && (React.createElement(MessageRepliesCountButton, { onClick: handleOpenThread, reply_count: message.reply_count })),
@@ -77,4 +77,4 @@ export declare const mapToUserNameOrId: TooltipUsernameMapper;
77
77
  export declare const getReadByTooltipText: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(users: UserResponse<StreamChatGenerics>[], t: TFunction, client: StreamChat<StreamChatGenerics>, tooltipUserNameMapper: TooltipUsernameMapper) => string;
78
78
  export declare const isOnlyEmojis: (text?: string) => boolean;
79
79
  export declare const isMessageBounced: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: Pick<StreamMessage<StreamChatGenerics>, 'type' | 'moderation' | 'moderation_details'>) => boolean;
80
- export declare const isMessageEdited: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: Pick<StreamMessage<StreamChatGenerics>, 'message_text_updated_at'> & Partial<Pick<StreamMessage<StreamChatGenerics>, 'ai_generated'>>) => boolean;
80
+ export declare const isMessageEdited: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(message: Pick<StreamMessage<StreamChatGenerics>, 'message_text_updated_at'>) => boolean;
@@ -310,4 +310,4 @@ export const isOnlyEmojis = (text) => {
310
310
  export const isMessageBounced = (message) => message.type === 'error' &&
311
311
  (message.moderation_details?.action === 'MESSAGE_RESPONSE_ACTION_BOUNCE' ||
312
312
  message.moderation?.action === 'bounce');
313
- export const isMessageEdited = (message) => !!message.message_text_updated_at && !message.ai_generated;
313
+ export const isMessageEdited = (message) => !!message.message_text_updated_at;
@@ -36,7 +36,7 @@ export type ChatContextValue<StreamChatGenerics extends DefaultStreamChatGeneric
36
36
  */
37
37
  customClasses?: CustomClasses;
38
38
  navOpen?: boolean;
39
- } & Required<Pick<ChatProps<StreamChatGenerics>, 'theme' | 'client'>>;
39
+ } & Partial<Pick<ChatProps<StreamChatGenerics>, 'isMessageAIGenerated'>> & Required<Pick<ChatProps<StreamChatGenerics>, 'theme' | 'client'>>;
40
40
  export declare const ChatContext: React.Context<ChatContextValue<DefaultStreamChatGenerics> | undefined>;
41
41
  export declare const ChatProvider: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ children, value, }: PropsWithChildren<{
42
42
  value: ChatContextValue<StreamChatGenerics>;
@@ -1,5 +1,5 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
- import { AttachmentPreviewListProps, AttachmentProps, AvatarProps, BaseImageProps, CooldownTimerProps, CustomMessageActionsListProps, DateSeparatorProps, EmojiSearchIndex, EmptyStateIndicatorProps, EventComponentProps, FixedHeightMessageProps, GiphyPreviewMessageProps, LinkPreviewListProps, LoadingIndicatorProps, MessageBouncePromptProps, MessageDeletedProps, MessageInputProps, MessageListNotificationsProps, MessageNotificationProps, MessageOptionsProps, MessageProps, MessageRepliesCountButtonProps, MessageStatusProps, MessageTimestampProps, MessageUIComponentProps, ModalGalleryProps, PinIndicatorProps, PollCreationDialogProps, PollOptionSelectorProps, QuotedMessagePreviewProps, ReactionOptions, ReactionSelectorProps, ReactionsListProps, RecordingPermissionDeniedNotificationProps, SendButtonProps, StartRecordingAudioButtonProps, StreamedMessageTextProps, SuggestionItemProps, SuggestionListProps, ThreadHeaderProps, ThreadListItemProps, ThreadListItemUIProps, TimestampProps, TypingIndicatorProps, UnreadMessagesNotificationProps, UnreadMessagesSeparatorProps } from '../components';
2
+ import { AttachmentPreviewListProps, AttachmentProps, AvatarProps, BaseImageProps, ChannelPreviewActionButtonsProps, CooldownTimerProps, CustomMessageActionsListProps, DateSeparatorProps, EmojiSearchIndex, EmptyStateIndicatorProps, EventComponentProps, FixedHeightMessageProps, GiphyPreviewMessageProps, LinkPreviewListProps, LoadingIndicatorProps, MessageBouncePromptProps, MessageDeletedProps, MessageInputProps, MessageListNotificationsProps, MessageNotificationProps, MessageOptionsProps, MessageProps, MessageRepliesCountButtonProps, MessageStatusProps, MessageTimestampProps, MessageUIComponentProps, ModalGalleryProps, PinIndicatorProps, PollCreationDialogProps, PollOptionSelectorProps, QuotedMessagePreviewProps, ReactionOptions, ReactionSelectorProps, ReactionsListProps, RecordingPermissionDeniedNotificationProps, SendButtonProps, StartRecordingAudioButtonProps, StreamedMessageTextProps, SuggestionItemProps, SuggestionListProps, ThreadHeaderProps, ThreadListItemProps, ThreadListItemUIProps, TimestampProps, TypingIndicatorProps, UnreadMessagesNotificationProps, UnreadMessagesSeparatorProps } from '../components';
3
3
  import type { CustomTrigger, DefaultStreamChatGenerics, PropsWithChildrenOnly, UnknownType } from '../types/types';
4
4
  import type { StopAIGenerationButtonProps } from '../components/MessageInput/StopAIGenerationButton';
5
5
  export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger> = {
@@ -21,6 +21,8 @@ export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGe
21
21
  Avatar?: React.ComponentType<AvatarProps<StreamChatGenerics>>;
22
22
  /** Custom UI component to display <img/> elements resp. a fallback in case of load error, defaults to and accepts same props as: [BaseImage](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Gallery/BaseImage.tsx) */
23
23
  BaseImage?: React.ComponentType<BaseImageProps>;
24
+ /** Custom UI component to display set of action buttons within `ChannelPreviewMessenger` component, accepts same props as: [ChannelPreviewActionButtons](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelPreviewActionButtons.tsx) */
25
+ ChannelPreviewActionButtons?: React.ComponentType<ChannelPreviewActionButtonsProps<StreamChatGenerics>>;
24
26
  /** Custom UI component to display the slow mode cooldown timer, defaults to and accepts same props as: [CooldownTimer](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/CooldownTimer.tsx) */
25
27
  CooldownTimer?: React.ComponentType<CooldownTimerProps>;
26
28
  /** Custom UI component to render set of buttons to be displayed in the MessageActionsBox, defaults to and accepts same props as: [CustomMessageActionsList](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageActions/CustomMessageActionsList.tsx) */
@@ -86,6 +86,10 @@ export type MessageContextValue<StreamChatGenerics extends DefaultStreamChatGene
86
86
  highlighted?: boolean;
87
87
  /** Whether the threaded message is the first in the thread list */
88
88
  initialMessage?: boolean;
89
+ /**
90
+ * A factory function that determines whether a message is AI generated or not.
91
+ */
92
+ isMessageAIGenerated?: (message: StreamMessage<StreamChatGenerics>) => boolean;
89
93
  /** Latest message id on current channel */
90
94
  lastReceivedId?: string | null;
91
95
  /** DOMRect object for parent MessageList component */