Skip to content

Commit

Permalink
fix: add support for custom resolution of whether a message is ai gen…
Browse files Browse the repository at this point in the history
…erated (#2572)
  • Loading branch information
isekovanic authored Dec 5, 2024
1 parent 0d6fe53 commit 54640ac
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 16 deletions.
2 changes: 1 addition & 1 deletion examples/vite/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const App = () => {
if (!chatClient) return <>Loading...</>;

return (
<Chat client={chatClient}>
<Chat client={chatClient} isMessageAIGenerated={(message) => !!message?.ai_generated}>
<ChatView>
<ChatView.Selector />
<ChatView.Channels>
Expand Down
3 changes: 2 additions & 1 deletion src/components/ChannelList/ChannelList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { LoadingChannels } from '../Loading/LoadingChannels';
import { LoadMorePaginator, LoadMorePaginatorProps } from '../LoadMore/LoadMorePaginator';
import { NullComponent } from '../UtilityComponents';

import { ChannelListContextProvider } from '../../context';
import { ChannelListContextProvider, ChatContextValue } from '../../context';
import { useChatContext } from '../../context/ChatContext';

import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
Expand Down Expand Up @@ -75,6 +75,7 @@ export type ChannelListProps<
channel: Channel<StreamChatGenerics>,
t: TranslationContextValue['t'],
userLanguage: TranslationContextValue['userLanguage'],
isMessageAIGenerated?: ChatContextValue['isMessageAIGenerated'],
) => string | JSX.Element;
/** 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) */
List?: React.ComponentType<ChannelListMessengerProps<StreamChatGenerics>>;
Expand Down
16 changes: 12 additions & 4 deletions src/components/ChannelPreview/ChannelPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,12 @@ export const ChannelPreview = <
channelUpdateCount,
getLatestMessagePreview = defaultGetLatestMessagePreview,
} = props;
const { channel: activeChannel, client, setActiveChannel } = useChatContext<StreamChatGenerics>(
'ChannelPreview',
);
const {
channel: activeChannel,
client,
isMessageAIGenerated,
setActiveChannel,
} = useChatContext<StreamChatGenerics>('ChannelPreview');
const { t, userLanguage } = useTranslationContext('ChannelPreview');
const { displayImage, displayTitle, groupChannelDisplayInfo } = useChannelPreviewInfo({
channel,
Expand Down Expand Up @@ -162,7 +165,12 @@ export const ChannelPreview = <

if (!Preview) return null;

const latestMessagePreview = getLatestMessagePreview(channel, t, userLanguage);
const latestMessagePreview = getLatestMessagePreview(
channel,
t,
userLanguage,
isMessageAIGenerated,
);

return (
<Preview
Expand Down
4 changes: 3 additions & 1 deletion src/components/ChannelPreview/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Channel, PollVote, TranslationLanguages, UserResponse } from 'stre
import type { TranslationContextValue } from '../../context/TranslationContext';

import type { DefaultStreamChatGenerics } from '../../types/types';
import { ChatContextValue } from '../../context';

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

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

Expand Down Expand Up @@ -77,7 +79,7 @@ export const getLatestMessagePreview = <
}

if (previewTextToRender) {
return latestMessage.ai_generated
return isMessageAIGenerated?.(latestMessage)
? previewTextToRender
: renderPreviewText(previewTextToRender);
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { StreamChat } from 'stream-chat';
import type { SupportedTranslations } from '../../i18n/types';
import type { Streami18n } from '../../i18n/Streami18n';
import type { DefaultStreamChatGenerics } from '../../types/types';
import type { MessageContextValue } from '../../context';

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

/**
* Wrapper component for a StreamChat application. Chat needs to be placed around any other chat components
Expand All @@ -54,6 +55,7 @@ export const Chat = <
defaultLanguage,
i18nInstance,
initialNavOpen = true,
isMessageAIGenerated,
theme = 'messaging light',
useImageFlagEmojisOnWindows = false,
} = props;
Expand All @@ -79,6 +81,7 @@ export const Chat = <
closeMobileNav,
customClasses,
getAppSettings,
isMessageAIGenerated,
latestMessageDatesByChannels,
mutes,
navOpen,
Expand Down
3 changes: 3 additions & 0 deletions src/components/Chat/hooks/useCreateChatContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const useCreateChatContext = <
closeMobileNav,
customClasses,
getAppSettings,
isMessageAIGenerated,
latestMessageDatesByChannels,
mutes,
navOpen,
Expand All @@ -41,6 +42,7 @@ export const useCreateChatContext = <
closeMobileNav,
customClasses,
getAppSettings,
isMessageAIGenerated,
latestMessageDatesByChannels,
mutes,
navOpen,
Expand All @@ -58,6 +60,7 @@ export const useCreateChatContext = <
getAppSettings,
mutedUsersLength,
navOpen,
isMessageAIGenerated,
],
);

Expand Down
3 changes: 2 additions & 1 deletion src/components/Message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const MessageWithContext = <
userRoles,
} = props;

const { client } = useChatContext('Message');
const { client, isMessageAIGenerated } = useChatContext('Message');
const { read } = useChannelStateContext('Message');
const { Message: contextMessage } = useComponentContext<StreamChatGenerics>('Message');

Expand Down Expand Up @@ -159,6 +159,7 @@ const MessageWithContext = <
editing,
getMessageActions: messageActionsHandler,
handleEdit: setEdit,
isMessageAIGenerated,
isMyMessage: () => isMyMessage,
messageIsUnread,
onUserClick,
Expand Down
11 changes: 8 additions & 3 deletions src/components/Message/MessageSimple.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import clsx from 'clsx';

import { MessageErrorIcon } from './icons';
Expand Down Expand Up @@ -56,6 +56,7 @@ const MessageSimpleWithContext = <
handleOpenThread,
handleRetry,
highlighted,
isMessageAIGenerated,
isMyMessage,
message,
onUserClick,
Expand Down Expand Up @@ -88,6 +89,10 @@ const MessageSimpleWithContext = <

const hasAttachment = messageHasAttachments(message);
const hasReactions = messageHasReactions(message);
const isAIGenerated = useMemo(() => isMessageAIGenerated?.(message), [
isMessageAIGenerated,
message,
]);

if (message.customType === CUSTOM_MESSAGE_TYPE.date) {
return null;
Expand All @@ -101,7 +106,7 @@ const MessageSimpleWithContext = <
const showReplyCountButton = !threadList && !!message.reply_count;
const allowRetry = message.status === 'failed' && message.errorStatusCode !== 403;
const isBounced = isMessageBounced(message);
const isEdited = isMessageEdited(message);
const isEdited = isMessageEdited(message) && !isAIGenerated;

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

Expand Down Expand Up @@ -187,7 +192,7 @@ const MessageSimpleWithContext = <
{message.attachments?.length && !message.quoted_message ? (
<Attachment actionHandler={handleAction} attachments={message.attachments} />
) : null}
{message.ai_generated ? (
{isAIGenerated ? (
<StreamedMessageText message={message} renderText={renderText} />
) : (
<MessageText message={message} renderText={renderText} />
Expand Down
5 changes: 2 additions & 3 deletions src/components/Message/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,5 @@ export const isMessageBounced = <
export const isMessageEdited = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>(
message: Pick<StreamMessage<StreamChatGenerics>, 'message_text_updated_at'> &
Partial<Pick<StreamMessage<StreamChatGenerics>, 'ai_generated'>>,
) => !!message.message_text_updated_at && !message.ai_generated;
message: Pick<StreamMessage<StreamChatGenerics>, 'message_text_updated_at'>,
) => !!message.message_text_updated_at;
3 changes: 2 additions & 1 deletion src/context/ChatContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export type ChatContextValue<
*/
customClasses?: CustomClasses;
navOpen?: boolean;
} & Required<Pick<ChatProps<StreamChatGenerics>, 'theme' | 'client'>>;
} & Partial<Pick<ChatProps<StreamChatGenerics>, 'isMessageAIGenerated'>> &
Required<Pick<ChatProps<StreamChatGenerics>, 'theme' | 'client'>>;

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

Expand Down
4 changes: 4 additions & 0 deletions src/context/MessageContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ export type MessageContextValue<
highlighted?: boolean;
/** Whether the threaded message is the first in the thread list */
initialMessage?: boolean;
/**
* A factory function that determines whether a message is AI generated or not.
*/
isMessageAIGenerated?: (message: StreamMessage<StreamChatGenerics>) => boolean;
/** Latest message id on current channel */
lastReceivedId?: string | null;
/** DOMRect object for parent MessageList component */
Expand Down

0 comments on commit 54640ac

Please sign in to comment.