stream-chat-react 12.7.0 → 12.7.1

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.
@@ -5,6 +5,7 @@ import { ChannelPreviewUIComponentProps } from '../ChannelPreview/ChannelPreview
5
5
  import { ChannelSearchProps } from '../ChannelSearch/ChannelSearch';
6
6
  import { EmptyStateIndicatorProps } from '../EmptyStateIndicator';
7
7
  import { LoadMorePaginatorProps } from '../LoadMore/LoadMorePaginator';
8
+ import { ChatContextValue } from '../../context';
8
9
  import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
9
10
  import type { ChannelAvatarProps } from '../Avatar';
10
11
  import type { TranslationContextValue } from '../../context/TranslationContext';
@@ -34,7 +35,7 @@ export type ChannelListProps<StreamChatGenerics extends DefaultStreamChatGeneric
34
35
  /** An object containing channel query filters */
35
36
  filters?: ChannelFilters<StreamChatGenerics>;
36
37
  /** Custom function that generates the message preview in ChannelPreview component */
37
- getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string | JSX.Element;
38
+ getLatestMessagePreview?: (channel: Channel<StreamChatGenerics>, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue['isMessageAIGenerated']) => string | JSX.Element;
38
39
  /** Custom UI component to display the container for the queried channels, defaults to and accepts same props as: [ChannelListMessenger](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListMessenger.tsx) */
39
40
  List?: React.ComponentType<ChannelListMessengerProps<StreamChatGenerics>>;
40
41
  /** Custom UI component to display the loading error indicator, defaults to component that renders null */
@@ -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
  };
@@ -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.7.1-${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>;
@@ -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 */