ragflow/web/src/components/next-message-item/index.tsx
chanx 26042343d8
Fix: Improve Agent templates functionality and fix some UI style issues (#9129)
### What problem does this PR solve?

Fix: Improve Agent templates functionality and fix some UI style issues
#3221

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-07-31 16:09:45 +08:00

210 lines
6.8 KiB
TypeScript

import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
import { MessageType } from '@/constants/chat';
import { useSetModalState } from '@/hooks/common-hooks';
import { IReferenceChunk, IReferenceObject } from '@/interfaces/database/chat';
import classNames from 'classnames';
import {
PropsWithChildren,
memo,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import { IRegenerateMessage, IRemoveMessageById } from '@/hooks/logic-hooks';
import { INodeEvent } from '@/hooks/use-send-message';
import { cn } from '@/lib/utils';
import { AgentChatContext } from '@/pages/agent/context';
import { WorkFlowTimeline } from '@/pages/agent/log-sheet/workFlowTimeline';
import { IMessage } from '@/pages/chat/interface';
import { isEmpty } from 'lodash';
import IndentedTreeModal from '../indented-tree/modal';
import MarkdownContent from '../next-markdown-content';
import { RAGFlowAvatar } from '../ragflow-avatar';
import { useTheme } from '../theme-provider';
import { AssistantGroupButton, UserGroupButton } from './group-button';
import styles from './index.less';
import { ReferenceDocumentList } from './reference-document-list';
import { UploadedMessageFiles } from './uploaded-message-files';
interface IProps
extends Partial<IRemoveMessageById>,
IRegenerateMessage,
PropsWithChildren {
item: IMessage;
conversationId?: string;
currentEventListWithoutMessageById?: (messageId: string) => INodeEvent[];
setCurrentMessageId?: (messageId: string) => void;
reference?: IReferenceObject;
loading?: boolean;
sendLoading?: boolean;
visibleAvatar?: boolean;
nickname?: string;
avatar?: string;
avatarDialog?: string | null;
clickDocumentButton?: (documentId: string, chunk: IReferenceChunk) => void;
index: number;
showLikeButton?: boolean;
showLoudspeaker?: boolean;
showLog?: boolean;
}
function MessageItem({
item,
conversationId,
currentEventListWithoutMessageById,
setCurrentMessageId,
reference,
loading = false,
avatar,
avatarDialog,
sendLoading = false,
clickDocumentButton,
removeMessageById,
regenerateMessage,
showLikeButton = true,
showLoudspeaker = true,
visibleAvatar = true,
children,
showLog,
}: IProps) {
const { theme } = useTheme();
const isAssistant = item.role === MessageType.Assistant;
const isUser = item.role === MessageType.User;
const { visible, hideModal, showModal } = useSetModalState();
const [clickedDocumentId, setClickedDocumentId] = useState('');
const { setLastSendLoadingFunc } = useContext(AgentChatContext);
useEffect(() => {
if (typeof setLastSendLoadingFunc === 'function') {
setLastSendLoadingFunc(loading, item.id);
}
}, [loading, setLastSendLoadingFunc, item.id]);
const referenceDocuments = useMemo(() => {
const docs = reference?.doc_aggs ?? {};
return Object.values(docs);
}, [reference?.doc_aggs]);
const handleRegenerateMessage = useCallback(() => {
regenerateMessage?.(item);
}, [regenerateMessage, item]);
useEffect(() => {
if (typeof setCurrentMessageId === 'function') {
setCurrentMessageId(item.id);
}
}, [item.id, setCurrentMessageId]);
return (
<div
className={classNames(styles.messageItem, {
[styles.messageItemLeft]: item.role === MessageType.Assistant,
[styles.messageItemRight]: item.role === MessageType.User,
})}
>
<section
className={classNames(styles.messageItemSection, {
[styles.messageItemSectionLeft]: item.role === MessageType.Assistant,
[styles.messageItemSectionRight]: item.role === MessageType.User,
})}
>
<div
className={classNames(styles.messageItemContent, {
[styles.messageItemContentReverse]: item.role === MessageType.User,
})}
>
{visibleAvatar &&
(item.role === MessageType.User ? (
<RAGFlowAvatar avatar={avatar ?? '/logo.svg'} />
) : avatarDialog ? (
<RAGFlowAvatar avatar={avatarDialog} />
) : (
<AssistantIcon />
))}
<section className="flex-col gap-2 flex-1">
<div className="space-x-1">
{isAssistant ? (
<AssistantGroupButton
messageId={item.id}
content={item.content}
prompt={item.prompt}
showLikeButton={showLikeButton}
audioBinary={item.audio_binary}
showLoudspeaker={showLoudspeaker}
showLog={showLog}
></AssistantGroupButton>
) : (
<UserGroupButton
content={item.content}
messageId={item.id}
removeMessageById={removeMessageById}
regenerateMessage={
regenerateMessage && handleRegenerateMessage
}
sendLoading={sendLoading}
></UserGroupButton>
)}
{/* <b>{isAssistant ? '' : nickname}</b> */}
</div>
<div
className={cn({
[theme === 'dark'
? styles.messageTextDark
: styles.messageText]: isAssistant,
[styles.messageUserText]: !isAssistant,
'bg-background-card': !isAssistant,
})}
>
{item.data ? (
children
) : sendLoading && isEmpty(item.content) ? (
'searching...'
) : (
<MarkdownContent
loading={loading}
content={item.content}
reference={reference}
clickDocumentButton={clickDocumentButton}
></MarkdownContent>
)}
</div>
{isAssistant && referenceDocuments.length > 0 && (
<ReferenceDocumentList
list={referenceDocuments}
></ReferenceDocumentList>
)}
{isAssistant && currentEventListWithoutMessageById && (
<div className="mt-4">
<WorkFlowTimeline
currentEventListWithoutMessage={currentEventListWithoutMessageById(
item.id,
)}
currentMessageId={item.id}
canvasId={conversationId}
sendLoading={loading}
/>
</div>
)}
{isUser && (
<UploadedMessageFiles files={item.files}></UploadedMessageFiles>
)}
</section>
</div>
</section>
{visible && (
<IndentedTreeModal
visible={visible}
hideModal={hideModal}
documentId={clickedDocumentId}
></IndentedTreeModal>
)}
</div>
);
}
export default memo(MessageItem);