import { useCallback, useEffect, useRef, useState, useMemo } from "react"; import { ChatMessage, MessageBlock, MessagesState, MessagesActions, SystemPushState, ChatConfig } from "./types"; import { fetchMessages, sendMessage, retryMessage } from "./api"; import { uid } from "./utils"; const POLLING_INTERVAL = 10000; // 10秒轮询一次 const PAGE_SIZE = 20; interface UseMessagesProps { config: ChatConfig; onMessagesUpdate?: (shouldScroll: boolean) => void; } export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [MessagesState, MessagesActions, SystemPushState] { // 消息状态 const [displayMessages, setDisplayMessages] = useState([]); const [latestMessages, setLatestMessages] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [hasMore, setHasMore] = useState(false); const [offset, setOffset] = useState(0); const [totalCount, setTotalCount] = useState(0); // 系统推送状态 const [systemPush, setSystemPush] = useState(true); const systemPushDisabledTimeRef = useRef(null); // 状态引用 const configRef = useRef(config); const isInitialLoadRef = useRef(true); const timeoutIdRef = useRef(null); const isPollingRef = useRef(false); const isViewingHistoryRef = useRef(false); const prevTotalCountRef = useRef(totalCount); // 过滤消息 const filterMessages = useCallback((messages: ChatMessage[]) => { if (systemPush || !systemPushDisabledTimeRef.current) { return messages; } // 系统推送关闭时,只显示关闭前的系统消息和所有非系统消息 return messages.filter(msg => msg.role !== 'system' || msg.createdAt <= systemPushDisabledTimeRef.current! ); }, [systemPush]); // 合并和去重消息 const mergeMessages = useCallback((oldMessages: ChatMessage[], newMessages: ChatMessage[]) => { const messageMap = new Map(); // 使用 Map 自动去重,后面的会覆盖前面的 [...oldMessages, ...newMessages].forEach(msg => { messageMap.set(msg.id, msg); }); // 转回数组并按 id 排序 const merged = Array.from(messageMap.values()) .sort((a, b) => Number(a.id) - Number(b.id)); // 过滤系统消息 return filterMessages(merged); }, [filterMessages]); // 更新 config 引用 useEffect(() => { configRef.current = config; }, [config]); // 监听总消息数变化 useEffect(() => { if (!isViewingHistoryRef.current && totalCount !== prevTotalCountRef.current) { // 有新消息,需要滚动 onMessagesUpdate?.(true); } prevTotalCountRef.current = totalCount; }, [totalCount, onMessagesUpdate]); // 获取最新消息 const updateMessages = useCallback(async (showLoading: boolean = false) => { try { if (showLoading) { setIsLoading(true); } const response = await fetchMessages(configRef.current, 0, PAGE_SIZE); const filteredMessages = filterMessages(response.messages); setLatestMessages(response.messages); // 保存完整的消息列表 if (!isViewingHistoryRef.current) { setDisplayMessages(filteredMessages); // 显示过滤后的消息 } setTotalCount(response.totalCount); setHasMore(response.hasMore); setOffset(PAGE_SIZE); } catch (err) { console.error("获取消息失败:", err); setError(err instanceof Error ? err : new Error("获取消息失败")); } finally { if (showLoading) { setIsLoading(false); } } }, [filterMessages]); // 加载更多历史消息 const loadMoreMessages = useCallback(async () => { if (isLoading || !hasMore) return; try { isViewingHistoryRef.current = true; setIsLoading(true); const response = await fetchMessages(configRef.current, offset, PAGE_SIZE); // 合并并去重消息 setDisplayMessages(prev => mergeMessages(prev, response.messages)); setHasMore(response.hasMore); setOffset(prev => prev + PAGE_SIZE); setTotalCount(response.totalCount); } catch (err) { console.error("加载历史消息失败:", err); setError(err instanceof Error ? err : new Error("加载历史消息失败")); } finally { setIsLoading(false); } }, [offset, hasMore, isLoading, mergeMessages]); // 返回最新消息 const backToLatest = useCallback(async () => { isViewingHistoryRef.current = false; setDisplayMessages(filterMessages(latestMessages)); onMessagesUpdate?.(true); }, [latestMessages, filterMessages, onMessagesUpdate]); // 发送消息 const handleSendMessage = useCallback(async (blocks: MessageBlock[], videoId?: string) => { setIsLoading(true); setError(null); // 暂停轮询 if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); } isPollingRef.current = true; // 设置轮询标志,防止其他轮询启动 try { // 立即添加用户消息(临时显示) const userMessage: ChatMessage = { id: uid(), role: "user", createdAt: Date.now(), blocks, chatType: 'chat', status: 'pending', }; // 无论是否在查看历史,发送消息后都切换到最新视图 isViewingHistoryRef.current = false; setDisplayMessages(prev => { const newMessages = mergeMessages(prev, [userMessage]); // 在 setState 的回调中触发滚动,确保消息已更新到界面 setTimeout(() => onMessagesUpdate?.(true), 0); return newMessages; }); // 发送到服务器 await sendMessage(blocks, configRef.current, videoId); // 立即获取最新的消息列表 await updateMessages(false); onMessagesUpdate?.(true); } catch (err) { console.error("发送消息失败:", err); setError(err instanceof Error ? err : new Error("发送消息失败")); } finally { setIsLoading(false); // 恢复轮询 if (systemPush) { isPollingRef.current = false; // 延迟一个轮询间隔后再开始轮询 timeoutIdRef.current = setTimeout(() => { poll(); }, POLLING_INTERVAL); } } }, [updateMessages, mergeMessages]); // 重试消息 const handleRetryMessage = useCallback(async (messageId: string) => { setIsLoading(true); setError(null); try { // 重试时切换到最新视图 isViewingHistoryRef.current = false; await retryMessage(messageId, configRef.current); await updateMessages(false); } catch (err) { console.error("重试消息失败:", err); setError(err instanceof Error ? err : new Error("重试消息失败")); } finally { setIsLoading(false); } }, [updateMessages]); // 清空消息 const handleClearMessages = useCallback(() => { setDisplayMessages([]); setLatestMessages([]); setHasMore(false); setOffset(0); setTotalCount(0); }, []); // 系统推送开关 const toggleSystemPush = useCallback(() => { setSystemPush(prev => { if (prev) { // 关闭系统推送时,记录当前时间 systemPushDisabledTimeRef.current = Date.now(); } else { // 开启系统推送时,清除时间记录并更新显示 systemPushDisabledTimeRef.current = null; // 立即更新显示的消息 setDisplayMessages(filterMessages(latestMessages)); } return !prev; }); }, [latestMessages, filterMessages]); // 定义轮询函数 const poll = useCallback(async () => { if (isPollingRef.current) return; try { isPollingRef.current = true; await updateMessages(false); } finally { isPollingRef.current = false; if (systemPush) { timeoutIdRef.current = setTimeout(poll, POLLING_INTERVAL); } } }, [systemPush, updateMessages]); // 轮询获取最新消息 useEffect(() => { if (!systemPush) return; poll(); return () => { if (timeoutIdRef.current) { clearTimeout(timeoutIdRef.current); } }; }, [systemPush, poll]); // 初始加载 useEffect(() => { if (isInitialLoadRef.current) { isInitialLoadRef.current = false; updateMessages(true); } }, [updateMessages]); return [ { messages: displayMessages, isLoading, error, hasMore, loadMoreMessages, backToLatest, isViewingHistory: isViewingHistoryRef.current }, { sendMessage: handleSendMessage, clearMessages: handleClearMessages, retryMessage: handleRetryMessage }, { enabled: systemPush, toggle: toggleSystemPush } ]; }