import React, { useCallback, useRef } from 'react'; import { useRouter } from 'next/router'; import { getInitChatSiteInfo, delChatRecordByIndex, putChatHistory } from '@/api/chat'; import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent, useTheme } from '@chakra-ui/react'; import { useGlobalStore } from '@/store/global'; import { useQuery } from '@tanstack/react-query'; import { streamFetch } from '@/api/fetch'; import { useChatStore } from '@/store/chat'; import { useLoading } from '@/hooks/useLoading'; import { useToast } from '@/hooks/useToast'; import { customAlphabet } from 'nanoid'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); import type { ChatHistoryItemType } from '@/types/chat'; import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox'; import PageContainer from '@/components/PageContainer'; import SideBar from '@/components/SideBar'; import ChatHistorySlider from './components/ChatHistorySlider'; import SliderApps from './components/SliderApps'; import ChatHeader from './components/ChatHeader'; import { getErrText } from '@/utils/tools'; import { useUserStore } from '@/store/user'; const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => { const router = useRouter(); const theme = useTheme(); const { toast } = useToast(); const ChatBoxRef = useRef(null); const forbidRefresh = useRef(false); const { lastChatAppId, setLastChatAppId, lastChatId, setLastChatId, history, loadHistory, updateHistory, delHistory, chatData, setChatData } = useChatStore(); const { myApps, userInfo } = useUserStore(); const { isPc } = useGlobalStore(); const { Loading, setIsLoading } = useLoading(); const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure(); const startChat = useCallback( async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { const prompts = messages.slice(-2); const completionChatId = chatId ? chatId : nanoid(); const { responseText, responseData } = await streamFetch({ data: { messages: prompts, variables, appId, chatId: completionChatId }, onMessage: generatingMessage, abortSignal: controller }); const newTitle = prompts[0].content?.slice(0, 20) || '新对话'; // update history if (completionChatId !== chatId && !controller.signal.aborted) { forbidRefresh.current = true; const newHistory: ChatHistoryItemType = { chatId: completionChatId, updateTime: new Date(), title: newTitle, appId, top: false }; updateHistory(newHistory); router.replace({ query: { chatId: completionChatId, appId } }); } else { const currentChat = history.find((item) => item.chatId === chatId); currentChat && updateHistory({ ...currentChat, updateTime: new Date(), title: newTitle }); } // update chat window setChatData((state) => ({ ...state, title: newTitle, history: ChatBoxRef.current?.getChatHistory() || state.history })); return { responseText, responseData }; }, [appId, chatId, history, router, setChatData, updateHistory] ); // 删除一句话 const delOneHistoryItem = useCallback( async ({ contentId, index }: { contentId?: string; index: number }) => { if (!chatId || !contentId) return; try { setChatData((state) => ({ ...state, history: state.history.filter((_, i) => i !== index) })); await delChatRecordByIndex({ chatId, contentId }); } catch (err) { console.log(err); } }, [chatId, setChatData] ); // get chat app info const loadChatInfo = useCallback( async ({ appId, chatId, loading = false }: { appId: string; chatId: string; loading?: boolean; }) => { try { loading && setIsLoading(true); const res = await getInitChatSiteInfo({ appId, chatId }); const history = res.history.map((item) => ({ ...item, status: 'finish' as any })); setChatData({ ...res, history }); // have records. ChatBoxRef.current?.resetHistory(history); ChatBoxRef.current?.resetVariables(res.variables); if (res.history.length > 0) { setTimeout(() => { ChatBoxRef.current?.scrollToBottom('auto'); }, 500); } } catch (e: any) { // reset all chat tore setLastChatAppId(''); setLastChatId(''); toast({ title: getErrText(e, '初始化聊天失败'), status: 'error' }); if (e?.code === 501) { router.replace('/app/list'); } else { router.replace('/chat'); } } setIsLoading(false); return null; }, [setIsLoading, setChatData, router, setLastChatAppId, setLastChatId, toast] ); // 初始化聊天框 useQuery(['init', appId, chatId], () => { // pc: redirect to latest model chat if (!appId && lastChatAppId) { return router.replace({ query: { appId: lastChatAppId, chatId: lastChatId } }); } if (!appId && myApps[0]) { return router.replace({ query: { appId: myApps[0]._id, chatId: lastChatId } }); } // store id appId && setLastChatAppId(appId); setLastChatId(chatId); if (forbidRefresh.current) { forbidRefresh.current = false; return null; } if (appId) { return loadChatInfo({ appId, chatId, loading: appId !== chatData.appId }); } return null; }); useQuery(['loadHistory', appId], () => (appId ? loadHistory({ appId }) : null)); return ( {/* pc show myself apps */} {isPc && ( )} {/* pc always show history. */} {((children: React.ReactNode) => { return isPc || !appId ? ( {children} ) : ( {children} ); })( ({ id: item.chatId, title: item.title, customTitle: item.customTitle, top: item.top }))} onChangeChat={(chatId) => { router.replace({ query: { chatId: chatId || '', appId } }); if (!isPc) { onCloseSlider(); } }} onDelHistory={delHistory} onSetHistoryTop={async (e) => { try { await putChatHistory(e); const historyItem = history.find((item) => item.chatId === e.chatId); if (!historyItem) return; updateHistory({ ...historyItem, top: e.top }); } catch (error) {} }} onSetCustomTitle={async (e) => { try { await putChatHistory({ chatId: e.chatId, customTitle: e.title }); const historyItem = history.find((item) => item.chatId === e.chatId); if (!historyItem) return; updateHistory({ ...historyItem, customTitle: e.title }); } catch (error) {} }} /> )} {/* chat container */} {/* header */} {/* chat box */} {}} onStartChat={startChat} onDelMessage={delOneHistoryItem} /> ); }; export async function getServerSideProps(context: any) { return { props: { appId: context?.query?.appId || '', chatId: context?.query?.chatId || '' } }; } export default Chat;