Skip to content

Commit 54640ac

Browse files
authored
fix: add support for custom resolution of whether a message is ai generated (#2572)
1 parent 0d6fe53 commit 54640ac

File tree

11 files changed

+43
-16
lines changed

11 files changed

+43
-16
lines changed

examples/vite/src/App.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const App = () => {
7373
if (!chatClient) return <>Loading...</>;
7474

7575
return (
76-
<Chat client={chatClient}>
76+
<Chat client={chatClient} isMessageAIGenerated={(message) => !!message?.ai_generated}>
7777
<ChatView>
7878
<ChatView.Selector />
7979
<ChatView.Channels>

src/components/ChannelList/ChannelList.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { LoadingChannels } from '../Loading/LoadingChannels';
3030
import { LoadMorePaginator, LoadMorePaginatorProps } from '../LoadMore/LoadMorePaginator';
3131
import { NullComponent } from '../UtilityComponents';
3232

33-
import { ChannelListContextProvider } from '../../context';
33+
import { ChannelListContextProvider, ChatContextValue } from '../../context';
3434
import { useChatContext } from '../../context/ChatContext';
3535

3636
import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
@@ -75,6 +75,7 @@ export type ChannelListProps<
7575
channel: Channel<StreamChatGenerics>,
7676
t: TranslationContextValue['t'],
7777
userLanguage: TranslationContextValue['userLanguage'],
78+
isMessageAIGenerated?: ChatContextValue['isMessageAIGenerated'],
7879
) => string | JSX.Element;
7980
/** 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) */
8081
List?: React.ComponentType<ChannelListMessengerProps<StreamChatGenerics>>;

src/components/ChannelPreview/ChannelPreview.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,12 @@ export const ChannelPreview = <
8282
channelUpdateCount,
8383
getLatestMessagePreview = defaultGetLatestMessagePreview,
8484
} = props;
85-
const { channel: activeChannel, client, setActiveChannel } = useChatContext<StreamChatGenerics>(
86-
'ChannelPreview',
87-
);
85+
const {
86+
channel: activeChannel,
87+
client,
88+
isMessageAIGenerated,
89+
setActiveChannel,
90+
} = useChatContext<StreamChatGenerics>('ChannelPreview');
8891
const { t, userLanguage } = useTranslationContext('ChannelPreview');
8992
const { displayImage, displayTitle, groupChannelDisplayInfo } = useChannelPreviewInfo({
9093
channel,
@@ -162,7 +165,12 @@ export const ChannelPreview = <
162165

163166
if (!Preview) return null;
164167

165-
const latestMessagePreview = getLatestMessagePreview(channel, t, userLanguage);
168+
const latestMessagePreview = getLatestMessagePreview(
169+
channel,
170+
t,
171+
userLanguage,
172+
isMessageAIGenerated,
173+
);
166174

167175
return (
168176
<Preview

src/components/ChannelPreview/utils.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { Channel, PollVote, TranslationLanguages, UserResponse } from 'stre
77
import type { TranslationContextValue } from '../../context/TranslationContext';
88

99
import type { DefaultStreamChatGenerics } from '../../types/types';
10+
import { ChatContextValue } from '../../context';
1011

1112
export const renderPreviewText = (text: string) => <ReactMarkdown skipHtml>{text}</ReactMarkdown>;
1213

@@ -32,6 +33,7 @@ export const getLatestMessagePreview = <
3233
channel: Channel<StreamChatGenerics>,
3334
t: TranslationContextValue['t'],
3435
userLanguage: TranslationContextValue['userLanguage'] = 'en',
36+
isMessageAIGenerated?: ChatContextValue<StreamChatGenerics>['isMessageAIGenerated'],
3537
): string | JSX.Element => {
3638
const latestMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
3739

@@ -77,7 +79,7 @@ export const getLatestMessagePreview = <
7779
}
7880

7981
if (previewTextToRender) {
80-
return latestMessage.ai_generated
82+
return isMessageAIGenerated?.(latestMessage)
8183
? previewTextToRender
8284
: renderPreviewText(previewTextToRender);
8385
}

src/components/Chat/Chat.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { StreamChat } from 'stream-chat';
1212
import type { SupportedTranslations } from '../../i18n/types';
1313
import type { Streami18n } from '../../i18n/Streami18n';
1414
import type { DefaultStreamChatGenerics } from '../../types/types';
15+
import type { MessageContextValue } from '../../context';
1516

1617
export type ChatProps<
1718
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
@@ -36,7 +37,7 @@ export type ChatProps<
3637
* Note: requires importing `stream-chat-react/css/v2/emoji-replacement.css` style sheet
3738
*/
3839
useImageFlagEmojisOnWindows?: boolean;
39-
};
40+
} & Partial<Pick<MessageContextValue<StreamChatGenerics>, 'isMessageAIGenerated'>>;
4041

4142
/**
4243
* Wrapper component for a StreamChat application. Chat needs to be placed around any other chat components
@@ -54,6 +55,7 @@ export const Chat = <
5455
defaultLanguage,
5556
i18nInstance,
5657
initialNavOpen = true,
58+
isMessageAIGenerated,
5759
theme = 'messaging light',
5860
useImageFlagEmojisOnWindows = false,
5961
} = props;
@@ -79,6 +81,7 @@ export const Chat = <
7981
closeMobileNav,
8082
customClasses,
8183
getAppSettings,
84+
isMessageAIGenerated,
8285
latestMessageDatesByChannels,
8386
mutes,
8487
navOpen,

src/components/Chat/hooks/useCreateChatContext.ts

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const useCreateChatContext = <
1515
closeMobileNav,
1616
customClasses,
1717
getAppSettings,
18+
isMessageAIGenerated,
1819
latestMessageDatesByChannels,
1920
mutes,
2021
navOpen,
@@ -41,6 +42,7 @@ export const useCreateChatContext = <
4142
closeMobileNav,
4243
customClasses,
4344
getAppSettings,
45+
isMessageAIGenerated,
4446
latestMessageDatesByChannels,
4547
mutes,
4648
navOpen,
@@ -58,6 +60,7 @@ export const useCreateChatContext = <
5860
getAppSettings,
5961
mutedUsersLength,
6062
navOpen,
63+
isMessageAIGenerated,
6164
],
6265
);
6366

src/components/Message/Message.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const MessageWithContext = <
7676
userRoles,
7777
} = props;
7878

79-
const { client } = useChatContext('Message');
79+
const { client, isMessageAIGenerated } = useChatContext('Message');
8080
const { read } = useChannelStateContext('Message');
8181
const { Message: contextMessage } = useComponentContext<StreamChatGenerics>('Message');
8282

@@ -159,6 +159,7 @@ const MessageWithContext = <
159159
editing,
160160
getMessageActions: messageActionsHandler,
161161
handleEdit: setEdit,
162+
isMessageAIGenerated,
162163
isMyMessage: () => isMyMessage,
163164
messageIsUnread,
164165
onUserClick,

src/components/Message/MessageSimple.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useMemo, useState } from 'react';
22
import clsx from 'clsx';
33

44
import { MessageErrorIcon } from './icons';
@@ -56,6 +56,7 @@ const MessageSimpleWithContext = <
5656
handleOpenThread,
5757
handleRetry,
5858
highlighted,
59+
isMessageAIGenerated,
5960
isMyMessage,
6061
message,
6162
onUserClick,
@@ -88,6 +89,10 @@ const MessageSimpleWithContext = <
8889

8990
const hasAttachment = messageHasAttachments(message);
9091
const hasReactions = messageHasReactions(message);
92+
const isAIGenerated = useMemo(() => isMessageAIGenerated?.(message), [
93+
isMessageAIGenerated,
94+
message,
95+
]);
9196

9297
if (message.customType === CUSTOM_MESSAGE_TYPE.date) {
9398
return null;
@@ -101,7 +106,7 @@ const MessageSimpleWithContext = <
101106
const showReplyCountButton = !threadList && !!message.reply_count;
102107
const allowRetry = message.status === 'failed' && message.errorStatusCode !== 403;
103108
const isBounced = isMessageBounced(message);
104-
const isEdited = isMessageEdited(message);
109+
const isEdited = isMessageEdited(message) && !isAIGenerated;
105110

106111
let handleClick: (() => void) | undefined = undefined;
107112

@@ -187,7 +192,7 @@ const MessageSimpleWithContext = <
187192
{message.attachments?.length && !message.quoted_message ? (
188193
<Attachment actionHandler={handleAction} attachments={message.attachments} />
189194
) : null}
190-
{message.ai_generated ? (
195+
{isAIGenerated ? (
191196
<StreamedMessageText message={message} renderText={renderText} />
192197
) : (
193198
<MessageText message={message} renderText={renderText} />

src/components/Message/utils.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,5 @@ export const isMessageBounced = <
494494
export const isMessageEdited = <
495495
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
496496
>(
497-
message: Pick<StreamMessage<StreamChatGenerics>, 'message_text_updated_at'> &
498-
Partial<Pick<StreamMessage<StreamChatGenerics>, 'ai_generated'>>,
499-
) => !!message.message_text_updated_at && !message.ai_generated;
497+
message: Pick<StreamMessage<StreamChatGenerics>, 'message_text_updated_at'>,
498+
) => !!message.message_text_updated_at;

src/context/ChatContext.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ export type ChatContextValue<
5656
*/
5757
customClasses?: CustomClasses;
5858
navOpen?: boolean;
59-
} & Required<Pick<ChatProps<StreamChatGenerics>, 'theme' | 'client'>>;
59+
} & Partial<Pick<ChatProps<StreamChatGenerics>, 'isMessageAIGenerated'>> &
60+
Required<Pick<ChatProps<StreamChatGenerics>, 'theme' | 'client'>>;
6061

6162
export const ChatContext = React.createContext<ChatContextValue | undefined>(undefined);
6263

src/context/MessageContext.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ export type MessageContextValue<
106106
highlighted?: boolean;
107107
/** Whether the threaded message is the first in the thread list */
108108
initialMessage?: boolean;
109+
/**
110+
* A factory function that determines whether a message is AI generated or not.
111+
*/
112+
isMessageAIGenerated?: (message: StreamMessage<StreamChatGenerics>) => boolean;
109113
/** Latest message id on current channel */
110114
lastReceivedId?: string | null;
111115
/** DOMRect object for parent MessageList component */

0 commit comments

Comments
 (0)