import React, { useRef, useCallback, useState, useEffect, useMemo } from "react"; import { ChevronsRight, ChevronDown, X } from 'lucide-react'; import { Switch } from 'antd'; import { MessageRenderer } from "./MessageRenderer"; import { InputBar } from "./InputBar"; import { useMessages } from "./useMessages"; import { DateDivider } from "./DateDivider"; import { LoadMoreButton } from "./LoadMoreButton"; import { ChatMessage } from "./types"; import { useDeviceType } from '@/hooks/useDeviceType'; import { useBrowserType } from '@/hooks/useBrowserType'; interface SmartChatBoxProps { isSmartChatBoxOpen: boolean; setIsSmartChatBoxOpen: (v: boolean) => void; projectId: string; userId: number; previewVideoUrl?: string | null; previewVideoId?: string | null; onClearPreview?: () => void; setIsFocusChatInput?: (v: boolean) => void; aiEditingResult?: any; /** 新消息回调:用于外层处理未展开时的气泡提示 */ onNewMessage?: (snippet: string) => void; } interface MessageGroup { date: number; messages: ChatMessage[]; } function BackToLatestButton({ onClick }: { onClick: () => void }) { return ( ); } export default function SmartChatBox({ isSmartChatBoxOpen, setIsSmartChatBoxOpen, projectId, userId, previewVideoUrl, previewVideoId, onClearPreview, setIsFocusChatInput, aiEditingResult, onNewMessage }: SmartChatBoxProps) { // 消息列表引用 const listRef = useRef(null); const [isAtBottom, setIsAtBottom] = useState(true); const { isMobile, isTablet, isDesktop } = useDeviceType(); // 检查是否滚动到底部 const checkIfAtBottom = useCallback(() => { if (listRef.current) { const { scrollHeight, scrollTop, clientHeight } = listRef.current; // 考虑一个小的误差范围(10px) const isBottom = scrollHeight - scrollTop - clientHeight <= 10; setIsAtBottom(isBottom); } }, []); // 处理滚动事件 const handleScroll = useCallback(() => { checkIfAtBottom(); }, [checkIfAtBottom]); useEffect(() => { console.log('previewVideoUrl', previewVideoUrl); console.log('previewVideoId', previewVideoId); }, [previewVideoUrl, previewVideoId]); // 监听滚动事件 useEffect(() => { const listElement = listRef.current; if (listElement) { listElement.addEventListener('scroll', handleScroll); return () => { listElement.removeEventListener('scroll', handleScroll); }; } }, [handleScroll]); // 处理消息更新时的滚动 const handleMessagesUpdate = useCallback((shouldScroll: boolean) => { if (shouldScroll && listRef.current) { listRef.current.scrollTo({ top: listRef.current.scrollHeight, behavior: "smooth" }); } // 更新底部状态 checkIfAtBottom(); }, [checkIfAtBottom]); // 使用消息管理 hook const [ { messages, isLoading, error, hasMore, loadMoreMessages, backToLatest, isViewingHistory }, { sendMessage }, { enabled: systemPush, toggle: toggleSystemPush } ] = useMessages({ config: { projectId, userId }, onMessagesUpdate: handleMessagesUpdate }); // 监听消息新增,向外层抛出前10个字符的文本片段 const prevLenRef = useRef(0); useEffect(() => { const len = messages.length; if (len > prevLenRef.current && len > 0) { const last = messages[len - 1]; // 提取第一个文本块 const textBlock = last.blocks.find(b => (b as any).type === 'text') as any; const text = textBlock?.text || ''; if (text && onNewMessage) { const snippet = text.slice(0, 40); onNewMessage(snippet); } } prevLenRef.current = len; }, [messages, onNewMessage]); // 监听智能剪辑结果,自动发送消息到聊天框 // useEffect(() => { // if (aiEditingResult && isSmartChatBoxOpen) { // const resultMessage = `🎉 AI智能剪辑完成! // 📊 剪辑统计: // • 视频时长:${aiEditingResult.duration || '未知'}秒 // • 剪辑片段:${aiEditingResult.clips || '未知'}个 // • 处理结果:${aiEditingResult.message || '剪辑成功'} // 🎬 最终视频已生成,您可以在工作流中查看和下载。`; // // 自动发送系统消息 // sendMessage([{ // type: 'text', // content: resultMessage // }]); // } // }, [aiEditingResult, isSmartChatBoxOpen, sendMessage]); // 按日期分组消息 const groupedMessages = React.useMemo(() => { const groups: MessageGroup[] = []; messages.forEach(message => { const messageDate = new Date(message.createdAt).setHours(0, 0, 0, 0); const existingGroup = groups.find(group => { const groupDate = new Date(group.date).setHours(0, 0, 0, 0); return groupDate === messageDate; }); if (existingGroup) { existingGroup.messages.push(message); } else { groups.push({ date: messageDate, messages: [message] }); } }); return groups.sort((a, b) => a.date - b.date); }, [messages]); const browserType = useBrowserType(); /** 根据浏览器类型获取最大高度值 */ const maxHeight = useMemo(() => { switch (browserType) { case 'Edge': return 'calc(100vh - 5.5rem)'; case 'Chrome': return 'calc(100vh - 7rem)'; default: return 'calc(100vh - 5.5rem)'; } }, [browserType]); return (
{/* Header */}
Chat {/* System push toggle */}
setIsSmartChatBoxOpen(false)} />
{/* Message list */}
{/* Load more button */} {hasMore && ( )} {/* Messages grouped by date */}
{groupedMessages.map((group) => ( {group.messages.map((message) => ( ))} ))}
{/* Loading indicator */} {isLoading && (
)} {/* Back to latest button */} {isViewingHistory && !isAtBottom && ( )}
{/* Input */} { if (url === previewVideoUrl && id === previewVideoId) { onClearPreview?.(); } }} initialVideoUrl={previewVideoUrl || undefined} initialVideoId={previewVideoId || undefined} setIsFocusChatInput={setIsFocusChatInput} />
); }