import Loading from "@/app/[locale]/loading" import { useChatHandler } from "@/components/chat/chat-hooks/use-chat-handler" import { ChatbotUIContext } from "@/context/context" import { getAssistantToolsByAssistantId } from "@/db/assistant-tools" import { getChatFilesByChatId } from "@/db/chat-files" import { getChatById } from "@/db/chats" import { getMessageFileItemsByMessageId } from "@/db/message-file-items" import { getMessagesByChatId } from "@/db/messages" import { getMessageImageFromStorage } from "@/db/storage/message-images" import { convertBlobToBase64 } from "@/lib/blob-to-b64" import useHotkey from "@/lib/hooks/use-hotkey" import { LLMID, MessageImage } from "@/types" import { useParams } from "next/navigation" import { FC, useContext, useEffect, useState } from "react" import { ChatHelp } from "./chat-help" import { useScroll } from "./chat-hooks/use-scroll" import { ChatInput } from "./chat-input" import { ChatMessages } from "./chat-messages" import { ChatScrollButtons } from "./chat-scroll-buttons" import { ChatSecondaryButtons } from "./chat-secondary-buttons" import { useTranslation } from "react-i18next" // 引入 useTranslation 进行国际化处理 interface ChatUIProps {} export const ChatUI: FC = ({}) => { useHotkey("o", () => handleNewChat()) const { t } = useTranslation() // 使用 t 函数进行国际化 const params = useParams() const { setChatMessages, selectedChat, setSelectedChat, setChatSettings, setChatImages, assistants, setSelectedAssistant, setChatFileItems, setChatFiles, setShowFilesDisplay, setUseRetrieval, setSelectedTools } = useContext(ChatbotUIContext) const { handleNewChat, handleFocusChatInput } = useChatHandler() const { messagesStartRef, messagesEndRef, handleScroll, scrollToBottom, setIsAtBottom, isAtTop, isAtBottom, isOverflowing, scrollToTop } = useScroll() const [loading, setLoading] = useState(true) useEffect(() => { const fetchData = async () => { await fetchMessages() await fetchChat() scrollToBottom() setIsAtBottom(true) } if (params.chatid) { fetchData().then(() => { handleFocusChatInput() setLoading(false) }) } else { setLoading(false) } }, []) const fetchMessages = async () => { const fetchedMessages = await getMessagesByChatId(params.chatid as string) const imagePromises: Promise[] = fetchedMessages.flatMap( message => message.image_paths ? message.image_paths.map(async imagePath => { const url = await getMessageImageFromStorage(imagePath) if (url) { const response = await fetch(url) const blob = await response.blob() const base64 = await convertBlobToBase64(blob) return { messageId: message.id, path: imagePath, base64, url, file: null } } return { messageId: message.id, path: imagePath, base64: "", url, file: null } }) : [] ) const images: MessageImage[] = await Promise.all(imagePromises.flat()) setChatImages(images) const messageFileItemPromises = fetchedMessages.map( async message => await getMessageFileItemsByMessageId(message.id) ) const messageFileItems = await Promise.all(messageFileItemPromises) const uniqueFileItems = messageFileItems.flatMap(item => item.file_items) setChatFileItems(uniqueFileItems) const chatFiles = await getChatFilesByChatId(params.chatid as string) setChatFiles( chatFiles.files.map(file => ({ id: file.id, name: file.name, type: file.type, file: null })) ) setUseRetrieval(true) setShowFilesDisplay(true) const fetchedChatMessages = fetchedMessages.map(message => { return { message, fileItems: messageFileItems .filter(messageFileItem => messageFileItem.id === message.id) .flatMap(messageFileItem => messageFileItem.file_items.map(fileItem => fileItem.id) ) } }) setChatMessages(fetchedChatMessages) } const fetchChat = async () => { const chat = await getChatById(params.chatid as string) if (!chat) return if (chat.assistant_id) { const assistant = assistants.find( assistant => assistant.id === chat.assistant_id ) if (assistant) { setSelectedAssistant(assistant) const assistantTools = ( await getAssistantToolsByAssistantId(assistant.id) ).tools setSelectedTools(assistantTools) } } setSelectedChat(chat) setChatSettings({ model: chat.model as LLMID, prompt: chat.prompt, temperature: chat.temperature, contextLength: chat.context_length, includeProfileContext: chat.include_profile_context, includeWorkspaceInstructions: chat.include_workspace_instructions, embeddingsProvider: chat.embeddings_provider as "openai" | "local" }) } if (loading) { return } return (
{selectedChat?.name || t("chat.defaultChatTitle")}
) }