chatbox--修复重复消息

This commit is contained in:
北枳 2025-08-23 19:46:51 +08:00
parent 55b5e6e570
commit 65c21e1c40

View File

@ -30,6 +30,21 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
const timeoutIdRef = useRef<NodeJS.Timeout | null>(null); const timeoutIdRef = useRef<NodeJS.Timeout | null>(null);
const isViewingHistoryRef = useRef(false); const isViewingHistoryRef = useRef(false);
const prevTotalCountRef = useRef(totalCount); const prevTotalCountRef = useRef(totalCount);
const isPollingRef = useRef(false);
// 合并和去重消息
const mergeMessages = useCallback((oldMessages: ChatMessage[], newMessages: ChatMessage[]) => {
const messageMap = new Map<string, ChatMessage>();
// 使用 Map 自动去重,后面的会覆盖前面的
[...oldMessages, ...newMessages].forEach(msg => {
messageMap.set(msg.id, msg);
});
// 转回数组并按 id 排序
return Array.from(messageMap.values())
.sort((a, b) => Number(a.id) - Number(b.id));
}, []);
// 更新 config 引用 // 更新 config 引用
useEffect(() => { useEffect(() => {
@ -46,7 +61,7 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
}, [totalCount, onMessagesUpdate]); }, [totalCount, onMessagesUpdate]);
// 获取最新消息 // 获取最新消息
const fetchLatestMessages = useCallback(async (showLoading: boolean = false) => { const updateMessages = useCallback(async (showLoading: boolean = false) => {
try { try {
if (showLoading) { if (showLoading) {
setIsLoading(true); setIsLoading(true);
@ -79,7 +94,9 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
setIsLoading(true); setIsLoading(true);
const response = await fetchMessages(configRef.current, offset, PAGE_SIZE); const response = await fetchMessages(configRef.current, offset, PAGE_SIZE);
setDisplayMessages(prev => [...response.messages, ...prev]);
// 合并并去重消息
setDisplayMessages(prev => mergeMessages(prev, response.messages));
setHasMore(response.hasMore); setHasMore(response.hasMore);
setOffset(prev => prev + PAGE_SIZE); setOffset(prev => prev + PAGE_SIZE);
setTotalCount(response.totalCount); setTotalCount(response.totalCount);
@ -89,7 +106,7 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}, [offset, hasMore, isLoading]); }, [offset, hasMore, isLoading, mergeMessages]);
// 返回最新消息 // 返回最新消息
const backToLatest = useCallback(async () => { const backToLatest = useCallback(async () => {
@ -116,20 +133,20 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
// 无论是否在查看历史,发送消息后都切换到最新视图 // 无论是否在查看历史,发送消息后都切换到最新视图
isViewingHistoryRef.current = false; isViewingHistoryRef.current = false;
setDisplayMessages(prev => [...prev, userMessage]); setDisplayMessages(prev => mergeMessages(prev, [userMessage]));
// 发送到服务器 // 发送到服务器
await sendMessage(blocks, configRef.current); await sendMessage(blocks, configRef.current);
// 立即获取最新的消息列表 // 立即获取最新的消息列表
await fetchLatestMessages(false); await updateMessages(false);
} catch (err) { } catch (err) {
console.error("发送消息失败:", err); console.error("发送消息失败:", err);
setError(err instanceof Error ? err : new Error("发送消息失败")); setError(err instanceof Error ? err : new Error("发送消息失败"));
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}, [fetchLatestMessages]); }, [updateMessages, mergeMessages]);
// 重试消息 // 重试消息
const handleRetryMessage = useCallback(async (messageId: string) => { const handleRetryMessage = useCallback(async (messageId: string) => {
@ -140,14 +157,14 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
// 重试时切换到最新视图 // 重试时切换到最新视图
isViewingHistoryRef.current = false; isViewingHistoryRef.current = false;
await retryMessage(messageId, configRef.current); await retryMessage(messageId, configRef.current);
await fetchLatestMessages(false); await updateMessages(false);
} catch (err) { } catch (err) {
console.error("重试消息失败:", err); console.error("重试消息失败:", err);
setError(err instanceof Error ? err : new Error("重试消息失败")); setError(err instanceof Error ? err : new Error("重试消息失败"));
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}, [fetchLatestMessages]); }, [updateMessages]);
// 清空消息 // 清空消息
const handleClearMessages = useCallback(() => { const handleClearMessages = useCallback(() => {
@ -168,8 +185,17 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
if (!systemPush) return; if (!systemPush) return;
const poll = async () => { const poll = async () => {
await fetchLatestMessages(false); if (isPollingRef.current) return;
timeoutIdRef.current = setTimeout(poll, POLLING_INTERVAL);
try {
isPollingRef.current = true;
await updateMessages(false);
} finally {
isPollingRef.current = false;
if (systemPush) {
timeoutIdRef.current = setTimeout(poll, POLLING_INTERVAL);
}
}
}; };
poll(); poll();
@ -179,15 +205,15 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me
clearTimeout(timeoutIdRef.current); clearTimeout(timeoutIdRef.current);
} }
}; };
}, [systemPush, fetchLatestMessages]); }, [systemPush, updateMessages]);
// 初始加载 // 初始加载
useEffect(() => { useEffect(() => {
if (isInitialLoadRef.current) { if (isInitialLoadRef.current) {
isInitialLoadRef.current = false; isInitialLoadRef.current = false;
fetchLatestMessages(true); updateMessages(true);
} }
}, [fetchLatestMessages]); }, [updateMessages]);
return [ return [
{ {