From 4e58bc4b2c2cc12460b1aa966814d48bb6ae8edb Mon Sep 17 00:00:00 2001 From: BiggerRain <15911122312@163.COM> Date: Sat, 11 Oct 2025 10:33:09 +0800 Subject: [PATCH] fix: duplicate chat content (#916) * style: add dark drop shadow to images * docs: add release note * style: add dark * fix: duplicate chat content * docs: add release note * chore: update history list --- docs/content.en/docs/release-notes/_index.md | 1 + src/components/Assistant/Chat.tsx | 7 +- src/components/Assistant/ChatContent.tsx | 16 +- src/components/Search/ExtensionStore.tsx | 2 +- src/components/Search/InputBox.tsx | 2 +- src/hooks/useChatActions.ts | 118 +++++++------- src/hooks/useChatPanel.ts | 160 +++++++++++++++++++ src/hooks/useMessageHandler.ts | 4 +- src/hooks/useSearch.ts | 2 +- src/hooks/useSettingsWindow.ts | 2 +- src/hooks/useStreamChat.ts | 2 +- src/pages/chat/index.tsx | 60 +------ 12 files changed, 252 insertions(+), 124 deletions(-) create mode 100644 src/hooks/useChatPanel.ts diff --git a/docs/content.en/docs/release-notes/_index.md b/docs/content.en/docs/release-notes/_index.md index 16850b6f..97289085 100644 --- a/docs/content.en/docs/release-notes/_index.md +++ b/docs/content.en/docs/release-notes/_index.md @@ -19,6 +19,7 @@ feat: support opening logs from about page #915 ### 🐛 Bug fix fix: automatic update of service list #913 +fix: duplicate chat content #916 ### ✈️ Improvements diff --git a/src/components/Assistant/Chat.tsx b/src/components/Assistant/Chat.tsx index 48ee3891..fbee2794 100644 --- a/src/components/Assistant/Chat.tsx +++ b/src/components/Assistant/Chat.tsx @@ -41,6 +41,7 @@ interface ChatAIProps { startPage?: StartPage; formatUrl?: (data: any) => string; instanceId?: string; + getChatHistoryChatPage?: () => void; } export interface SendMessageParams { @@ -52,6 +53,7 @@ export interface ChatAIRef { init: (params: SendMessageParams) => void; cancelChat: () => void; clearChat: () => void; + onSelectChat: (chat: Chat) => void; } const ChatAI = memo( @@ -73,6 +75,7 @@ const ChatAI = memo( startPage, formatUrl, instanceId, + getChatHistoryChatPage, }, ref ) => { @@ -80,6 +83,7 @@ const ChatAI = memo( init: init, cancelChat: () => cancelChat(activeChat), clearChat: clearChat, + onSelectChat: onSelectChat, })); const curChatEnd = useChatStore((state) => state.curChatEnd); @@ -193,7 +197,8 @@ const ChatAI = memo( isDeepThinkActive, isMCPActive, changeInput, - showChatHistory + showChatHistory, + getChatHistoryChatPage, ); const { dealMsg } = useMessageHandler( diff --git a/src/components/Assistant/ChatContent.tsx b/src/components/Assistant/ChatContent.tsx index 9b9999a0..2dfcdb6b 100644 --- a/src/components/Assistant/ChatContent.tsx +++ b/src/components/Assistant/ChatContent.tsx @@ -5,7 +5,6 @@ import { ChatMessage } from "@/components/ChatMessage"; import { Greetings } from "./Greetings"; import AttachmentList from "@/components/Assistant/AttachmentList"; import { useChatScroll } from "@/hooks/useChatScroll"; - import type { Chat, IChunkData } from "@/types/chat"; import { useConnectStore } from "@/stores/connectStore"; // import SessionFile from "./SessionFile"; @@ -45,20 +44,23 @@ export const ChatContent = ({ handleSendMessage, formatUrl, }: ChatContentProps) => { - const { currentSessionId, setCurrentSessionId } = useConnectStore(); - const { t } = useTranslation(); - const { uploadAttachments } = useChatStore(); + const currentSessionId = useConnectStore((state) => state.currentSessionId); + const setCurrentSessionId = useConnectStore( + (state) => state.setCurrentSessionId + ); + const visibleStartPage = useConnectStore((state) => state.visibleStartPage); + + const uploadAttachments = useChatStore((state) => state.uploadAttachments); + const curChatEnd = useChatStore((state) => state.curChatEnd); const messagesEndRef = useRef(null); const { scrollToBottom } = useChatScroll(messagesEndRef); + const scrollRef = useRef(null); const [isAtBottom, setIsAtBottom] = useState(true); - const visibleStartPage = useConnectStore((state) => state.visibleStartPage); - - const curChatEnd = useChatStore((state) => state.curChatEnd); useEffect(() => { setIsAtBottom(true); diff --git a/src/components/Search/ExtensionStore.tsx b/src/components/Search/ExtensionStore.tsx index ef900569..ef58a914 100644 --- a/src/components/Search/ExtensionStore.tsx +++ b/src/components/Search/ExtensionStore.tsx @@ -139,7 +139,7 @@ const ExtensionStore = ({ extensionId }: { extensionId?: string }) => { } ); - console.log("search_extension", result); + // console.log("search_extension", result); setList(result ?? []); diff --git a/src/components/Search/InputBox.tsx b/src/components/Search/InputBox.tsx index 7b45a7f8..5193746a 100644 --- a/src/components/Search/InputBox.tsx +++ b/src/components/Search/InputBox.tsx @@ -121,7 +121,7 @@ export default function ChatInput({ const handleSubmit = useCallback(() => { const trimmedValue = inputValue.trim(); - console.log("handleSubmit", trimmedValue, disabled); + // console.log("handleSubmit", trimmedValue, disabled); if ((trimmedValue || !isEmpty(uploadAttachments)) && !disabled) { changeInput(""); diff --git a/src/hooks/useChatActions.ts b/src/hooks/useChatActions.ts index ca8b2268..71748624 100644 --- a/src/hooks/useChatActions.ts +++ b/src/hooks/useChatActions.ts @@ -29,7 +29,8 @@ export function useChatActions( isDeepThinkActive?: boolean, isMCPActive?: boolean, changeInput?: (val: string) => void, - showChatHistory?: boolean + showChatHistory?: boolean, + getChatHistoryChatPage?: () => void, ) { const isCurrentLogin = useAuthStore((state) => state.isCurrentLogin); @@ -197,17 +198,17 @@ export function useChatActions( ) { try { const response = JSON.parse(msg); - console.log("first", response); + // console.log("first", response); let updatedChat: Chat; if (Array.isArray(response)) { curIdRef.current = response[0]?._id; curSessionIdRef.current = response[0]?._source?.session_id; - console.log( - "curIdRef-curSessionIdRef-Array", - curIdRef.current, - curSessionIdRef.current - ); + // console.log( + // "curIdRef-curSessionIdRef-Array", + // curIdRef.current, + // curSessionIdRef.current + // ); updatedChat = { ...updatedChatRef.current, messages: [ @@ -215,16 +216,16 @@ export function useChatActions( ...(response || []), ], }; - console.log("array", updatedChat, updatedChatRef.current?.messages); + // console.log("array", updatedChat, updatedChatRef.current?.messages); } else { const newChat: Chat = response; curIdRef.current = response?.payload?.id; curSessionIdRef.current = response?.payload?.session_id; - console.log( - "curIdRef-curSessionIdRef", - curIdRef.current, - curSessionIdRef.current - ); + // console.log( + // "curIdRef-curSessionIdRef", + // curIdRef.current, + // curSessionIdRef.current + // ); newChat._source = { ...response?.payload, @@ -252,7 +253,7 @@ export function useChatActions( async (timestamp: number) => { cleanupListeners(); - console.log("setupListeners", clientId, timestamp); + // console.log("setupListeners", clientId, timestamp); const unlisten_chat_message = await platformAdapter.listenEvent( `chat-stream-${clientId}-${timestamp}`, (event) => { @@ -300,12 +301,45 @@ export function useChatActions( [setupListeners] ); + const getChatHistory = useCallback(async () => { + let response: any; + if (isTauri) { + if (await unrequitable()) { + return setChats([]); + } + + response = await platformAdapter.commands("chat_history", { + serverId: currentService?.id, + from: 0, + size: 100, + query: keyword, + }); + + response = response ? JSON.parse(response) : null; + } else { + const [_error, res] = await Get(`/chat/_history`, { + from: 0, + size: 100, + }); + response = res; + } + + const hits = response?.hits?.hits || []; + setChats(hits); + }, [ + currentService?.id, + keyword, + isTauri, + currentService?.enabled, + isCurrentLogin, + ]); + const createNewChat = useCallback( async (params?: SendMessageParams) => { const { message, attachments } = params || {}; - console.log("message", message); - console.log("attachments", attachments); + // console.log("message", message); + // console.log("attachments", attachments); if (!message && isEmpty(attachments)) return; @@ -325,7 +359,7 @@ export function useChatActions( if (isTauri) { if (!currentService?.id) return; - console.log("chat_create", clientId, timestamp); + // console.log("chat_create", clientId, timestamp); await platformAdapter.commands("chat_create", { serverId: currentService?.id, @@ -334,7 +368,7 @@ export function useChatActions( queryParams, clientId: `chat-stream-${clientId}-${timestamp}`, }); - console.log("_create end", message); + // console.log("_create end", message); resetChatState(); } else { await streamPost({ @@ -342,12 +376,17 @@ export function useChatActions( body: { message }, queryParams, onMessage: (line) => { - console.log("⏳", line); + // console.log("⏳", line); handleChatCreateStreamMessage(line); // append to chat box }, }); } + // console.log("showChatHistory", showChatHistory); + + if (showChatHistory) { + getChatHistoryChatPage ? getChatHistoryChatPage() : getChatHistory(); + } }, [ isTauri, @@ -360,6 +399,8 @@ export function useChatActions( currentAssistant, chatClose, clientId, + showChatHistory, + getChatHistory, ] ); @@ -386,7 +427,7 @@ export function useChatActions( if (isTauri) { if (!currentService?.id) return; - console.log("chat_chat", clientId, timestamp); + // console.log("chat_chat", clientId, timestamp); await platformAdapter.commands("chat_chat", { serverId: currentService?.id, sessionId: newChat?._id, @@ -395,7 +436,7 @@ export function useChatActions( attachments, clientId: `chat-stream-${clientId}-${timestamp}`, }); - console.log("chat_chat end", message, clientId); + // console.log("chat_chat end", message, clientId); resetChatState(); } else { await streamPost({ @@ -403,7 +444,7 @@ export function useChatActions( body: { message }, queryParams, onMessage: (line) => { - console.log("line", line); + // console.log("line", line); handleChatCreateStreamMessage(line); // append to chat box }, @@ -468,39 +509,6 @@ export function useChatActions( [currentService?.id, isTauri] ); - const getChatHistory = useCallback(async () => { - let response: any; - if (isTauri) { - if (await unrequitable()) { - return setChats([]); - } - - response = await platformAdapter.commands("chat_history", { - serverId: currentService?.id, - from: 0, - size: 100, - query: keyword, - }); - - response = response ? JSON.parse(response) : null; - } else { - const [_error, res] = await Get(`/chat/_history`, { - from: 0, - size: 100, - }); - response = res; - } - - const hits = response?.hits?.hits || []; - setChats(hits); - }, [ - currentService?.id, - keyword, - isTauri, - currentService?.enabled, - isCurrentLogin, - ]); - useEffect(() => { if (showChatHistory) { getChatHistory(); diff --git a/src/hooks/useChatPanel.ts b/src/hooks/useChatPanel.ts new file mode 100644 index 00000000..0c51cd1a --- /dev/null +++ b/src/hooks/useChatPanel.ts @@ -0,0 +1,160 @@ +import { useCallback, useState } from "react"; + +import type { Chat } from "@/types/chat"; +import { useConnectStore } from "@/stores/connectStore"; +import platformAdapter from "@/utils/platformAdapter"; +import { unrequitable } from "@/utils"; + +export function useChatPanel() { + const { + assistantList, + setCurrentAssistant, + setVisibleStartPage, + currentService, + } = useConnectStore(); + + const [chats, setChats] = useState([]); + const [activeChat, setActiveChat] = useState(); + const [keyword, setKeyword] = useState(""); + + const getChatHistory = useCallback(async () => { + try { + if (await unrequitable()) { + return setChats([]); + } + let response: any = await platformAdapter.commands("chat_history", { + serverId: currentService?.id, + from: 0, + size: 100, + query: keyword, + }); + response = response ? JSON.parse(response) : null; + const hits = response?.hits?.hits || []; + setChats(hits); + } catch (error) { + console.error("chat_history:", error); + } + }, [keyword, currentService?.id]); + + const chatHistory = useCallback( + async (chat: Chat) => { + try { + let response: any = await platformAdapter.commands( + "session_chat_history", + { + serverId: currentService?.id, + sessionId: chat?._id || "", + from: 0, + size: 500, + } + ); + response = response ? JSON.parse(response) : null; + const hits = response?.hits?.hits || []; + + // set current assistant based on last message + const lastAssistantId = hits[hits.length - 1]?._source?.assistant_id; + const matchedAssistant = assistantList?.find( + (assistant) => assistant._id === lastAssistantId + ); + if (matchedAssistant) { + setCurrentAssistant(matchedAssistant); + } + + const updatedChat: Chat = { + ...chat, + messages: hits, + }; + setActiveChat(updatedChat); + } catch (error) { + console.error("session_chat_history:", error); + } + }, + [assistantList, currentService?.id, setCurrentAssistant] + ); + + const onSelectChat = useCallback( + async (chat: any) => { + try { + let response: any = await platformAdapter.commands( + "open_session_chat", + { + serverId: currentService?.id, + sessionId: chat?._id, + } + ); + response = response ? JSON.parse(response) : null; + chatHistory(response); + setVisibleStartPage(false); + } catch (error) { + console.error("open_session_chat:", error); + } + }, + [currentService?.id, chatHistory, setVisibleStartPage] + ); + + const deleteChat = useCallback( + async (chatId: string) => { + if (!currentService?.id) return; + + await platformAdapter.commands( + "delete_session_chat", + currentService.id, + chatId + ); + + setChats((prev) => prev.filter((chat) => chat._id !== chatId)); + if (activeChat?._id === chatId) { + const remainingChats = chats.filter((chat) => chat._id !== chatId); + setActiveChat(remainingChats[0]); + } + }, + [currentService?.id, activeChat?._id, chats] + ); + + const handleSearch = useCallback((kw: string) => { + setKeyword(kw); + }, []); + + const handleRename = useCallback( + (chatId: string, title: string) => { + if (!currentService?.id) return; + + setChats((prev) => { + const updatedChats = prev.map((item) => { + if (item._id !== chatId) return item; + return { ...item, _source: { ...item._source, title } }; + }); + return updatedChats; + }); + + if (activeChat?._id === chatId) { + setActiveChat((prev) => { + if (!prev) return prev; + return { ...prev, _source: { ...prev._source, title } }; + }); + } + + platformAdapter.commands("update_session_chat", { + serverId: currentService.id, + sessionId: chatId, + title, + }); + }, + [currentService?.id, activeChat?._id] + ); + + return { + chats, + setChats, + activeChat, + setActiveChat, + keyword, + setKeyword, + getChatHistory, + chatHistory, + onSelectChat, + deleteChat, + handleSearch, + handleRename, + }; +} \ No newline at end of file diff --git a/src/hooks/useMessageHandler.ts b/src/hooks/useMessageHandler.ts index b92d76e4..be12ea1e 100644 --- a/src/hooks/useMessageHandler.ts +++ b/src/hooks/useMessageHandler.ts @@ -35,7 +35,7 @@ export function useMessageHandler( } messageTimeoutRef.current = setTimeout(() => { - console.log("AI response timeout"); + // console.log("AI response timeout"); setTimedoutShow(true); onCancel(); }, (connectionTimeout ?? 120) * 1000); @@ -108,7 +108,7 @@ export function useMessageHandler( clearTimeout(messageTimeoutRef.current); } setCurChatEnd(true); - console.log("AI finished output"); + // console.log("AI finished output"); return; } } catch (error) { diff --git a/src/hooks/useSearch.ts b/src/hooks/useSearch.ts index 38140d06..7b854c34 100644 --- a/src/hooks/useSearch.ts +++ b/src/hooks/useSearch.ts @@ -198,7 +198,7 @@ export function useSearch() { } } - console.log("_suggest", searchInput, response); + //console.log("_suggest", searchInput, response); if (timerRef.current) { clearTimeout(timerRef.current); diff --git a/src/hooks/useSettingsWindow.ts b/src/hooks/useSettingsWindow.ts index 45557ade..f8bd7f6d 100644 --- a/src/hooks/useSettingsWindow.ts +++ b/src/hooks/useSettingsWindow.ts @@ -68,7 +68,7 @@ export default function useSettingsWindow() { useEffect(() => { const unlisten = platformAdapter.listenEvent("open_settings", async (event) => { - console.log("open_settings event received:", event); + //console.log("open_settings event received:", event); const tab = event.payload as string | ""; platformAdapter.emitEvent("tab_index", tab); diff --git a/src/hooks/useStreamChat.ts b/src/hooks/useStreamChat.ts index 29f36bce..d54eaa7a 100644 --- a/src/hooks/useStreamChat.ts +++ b/src/hooks/useStreamChat.ts @@ -52,7 +52,7 @@ export const useStreamChat = (options: Options) => { unlistenRef.current = await platformAdapter.listenEvent( clientId, ({ payload }) => { - console.log(clientId, JSON.parse(payload)); + //console.log(clientId, JSON.parse(payload)); const chunkData = JSON.parse(payload); diff --git a/src/pages/chat/index.tsx b/src/pages/chat/index.tsx index 09a538e5..c6a47c37 100644 --- a/src/pages/chat/index.tsx +++ b/src/pages/chat/index.tsx @@ -35,12 +35,7 @@ export default function StandaloneChat({}: StandaloneChatProps) { setIsTauri(true); }, []); - const { - setCurrentAssistant, - assistantList, - setVisibleStartPage, - currentService, - } = useConnectStore(); + const currentService = useConnectStore((state) => state.currentService); const chatAIRef = useRef(null); @@ -77,7 +72,7 @@ export default function StandaloneChat({}: StandaloneChatProps) { query: keyword, }); response = response ? JSON.parse(response) : null; - console.log("_history", response); + // console.log("_history", response); const hits = response?.hits?.hits || []; setChats(hits); } catch (error) { @@ -112,39 +107,6 @@ export default function StandaloneChat({}: StandaloneChatProps) { chatAIRef.current?.init(params); }; - const chatHistory = async (chat: typeChat) => { - try { - let response: any = await platformAdapter.commands( - "session_chat_history", - { - serverId: currentService?.id, - sessionId: chat?._id || "", - from: 0, - size: 500, - } - ); - response = response ? JSON.parse(response) : null; - console.log("id_history", response); - const hits = response?.hits?.hits || []; - // set current assistant - const lastAssistantId = hits[hits.length - 1]?._source?.assistant_id; - const matchedAssistant = assistantList?.find( - (assistant) => assistant._id === lastAssistantId - ); - if (matchedAssistant) { - setCurrentAssistant(matchedAssistant); - } - // - const updatedChat: typeChat = { - ...chat, - messages: hits, - }; - setActiveChat(updatedChat); - } catch (error) { - console.error("session_chat_history:", error); - } - }; - const chatClose = async () => { if (!activeChat?._id) return; try { @@ -153,26 +115,14 @@ export default function StandaloneChat({}: StandaloneChatProps) { sessionId: activeChat?._id, }); response = response ? JSON.parse(response) : null; - console.log("_close", response); + // console.log("_close", response); } catch (error) { console.error("close_session_chat:", error); } }; const onSelectChat = async (chat: any) => { - chatClose(); - try { - let response: any = await platformAdapter.commands("open_session_chat", { - serverId: currentService?.id, - sessionId: chat?._id, - }); - response = response ? JSON.parse(response) : null; - console.log("_open", response); - chatHistory(response); - setVisibleStartPage(false); - } catch (error) { - console.error("open_session_chat:", error); - } + chatAIRef.current?.onSelectChat(chat); }; const cancelChat = async () => { @@ -317,6 +267,8 @@ export default function StandaloneChat({}: StandaloneChatProps) { isChatPage={isChatPage} getFileUrl={getFileUrl} changeInput={setInput} + showChatHistory={true} + getChatHistoryChatPage={getChatHistory} />