'use client'; import SendSvg from '@/assets/icons/svg/send.svg'; import SunSvg from '@/assets/icons/svg/sun.svg'; import VideoSvg from '@/assets/icons/svg/video.svg'; import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react'; import { Keyboard, ScrollView, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native'; import { useWebSocketStreamHandler } from '@/hooks/useWebSocketStreamHandler'; import { getWebSocketManager } from '@/lib/websocket-util'; import { Message } from '@/types/ask'; import { useTranslation } from 'react-i18next'; import { ThemedText } from '../ThemedText'; import { createNewConversation } from './utils'; interface Props { setIsHello: Dispatch>, conversationId: string | null, setUserMessages: Dispatch>; setConversationId: (conversationId: string) => void, selectedImages: string[]; setSelectedImages: Dispatch>; } const RENDER_INTERVAL = 50; // 渲染间隔,单位毫秒 export default function SendMessage(props: Props) { const { setIsHello, conversationId, setUserMessages, setConversationId, selectedImages, setSelectedImages } = props; const { t } = useTranslation(); // 用户询问 const [inputValue, setInputValue] = useState(''); // 添加组件挂载状态跟踪 const isMountedRef = useRef(true); const isKeyboardVisible = useRef(false); // 使用新的WebSocket流处理hook,启用批量处理模式 const { subscribeToWebSocket, cleanup } = useWebSocketStreamHandler({ setUserMessages, isMounted: true, // 传递静态值,hook内部会使用ref跟踪 enableBatching: true, renderInterval: RENDER_INTERVAL }); // 使用WebSocket订阅 useEffect(() => { const unsubscribe = subscribeToWebSocket(); return () => { unsubscribe(); }; }, [subscribeToWebSocket]); // 组件卸载时的清理 useEffect(() => { return () => { isMountedRef.current = false; cleanup(); }; }, [cleanup]); useEffect(() => { // 使用keyboardWillShow而不是keyboardDidShow,这样可以在键盘完全显示前更新UI const showSubscription = Keyboard.addListener('keyboardWillShow', () => { isKeyboardVisible.current = true; if (!conversationId) { // 确保在下一个事件循环中更新状态,避免可能的渲染问题 requestAnimationFrame(() => { setIsHello(false); setUserMessages([ { id: Math.random().toString(36).substring(2, 9), content: t("ask:ask.introduction1"), role: 'assistant', timestamp: new Date().toISOString() } ]) }); } }); const hideSubscription = Keyboard.addListener('keyboardWillHide', () => { isKeyboardVisible.current = false; }); return () => { showSubscription.remove(); hideSubscription.remove(); }; }, [conversationId, setIsHello, setUserMessages, t]); // 发送询问 const handleSubmit = useCallback(async () => { if (!inputValue.trim() || !isMountedRef.current) return; const text = inputValue.trim(); setIsHello(false); // 添加用户消息和占位符助手消息 setUserMessages(prev => [ ...prev, { id: Math.random().toString(36).substring(2, 9), content: text, role: 'user', timestamp: new Date().toISOString() }, { id: Math.random().toString(36).substring(2, 9), content: "keepSearchIng", role: 'assistant', timestamp: new Date().toISOString() } ]); try { let currentSessionId = conversationId; if (!currentSessionId) { currentSessionId = await createNewConversation(text); if (currentSessionId && isMountedRef.current) { setConversationId(currentSessionId); } } if (currentSessionId && isMountedRef.current) { const webSocketManager = getWebSocketManager(); webSocketManager.send({ type: 'Chat', session_id: currentSessionId, message: text, image_material_ids: selectedImages.length > 0 ? selectedImages : undefined, }); setSelectedImages([]); } else { console.error("无法获取 session_id,消息发送失败。"); // 可以在这里处理错误,例如显示一个提示 if (isMountedRef.current) { setUserMessages(prev => prev.filter(item => item.content !== 'keepSearchIng')); } } } catch (error) { console.error('发送消息时出错:', error); if (isMountedRef.current) { setUserMessages(prev => prev.filter(item => item.content !== 'keepSearchIng')); } } // 将输入框清空 if (isMountedRef.current) { setInputValue(''); // 只有在键盘可见时才关闭键盘 if (isKeyboardVisible.current) { Keyboard.dismiss(); } } }, [inputValue, conversationId, selectedImages, createNewConversation, setConversationId, setSelectedImages, setUserMessages]); const handleQuitly = useCallback((type: string) => { if (!isMountedRef.current) return; setIsHello(false); setUserMessages(pre => ([ ...pre, { id: Math.random().toString(36).substring(2, 9), content: type === "search" ? t("ask:ask.introduction2") : t("ask:ask.introduction3"), role: 'assistant', timestamp: new Date().toISOString() } ])); }, [t, setIsHello, setUserMessages]); return ( handleQuitly('search')}> {t("ask:ask.search")} handleQuitly('video')}> {t("ask:ask.video")} { setInputValue(text); }} onSubmitEditing={handleSubmit} // 调起的键盘类型 returnKeyType="send" /> ); } const styles = StyleSheet.create({ button: { paddingHorizontal: 8, paddingVertical: 4, margin: 5, borderRadius: 25, alignItems: 'center', borderWidth: 2, display: 'flex', flexDirection: 'row', gap: 5 }, container: { justifyContent: 'center', backgroundColor: '#transparent', }, input: { borderColor: '#FF9500', borderWidth: 1, borderRadius: 25, paddingHorizontal: 20, paddingVertical: 13, fontSize: 16, width: '100%', // 确保输入框宽度撑满 paddingRight: 50 }, voiceButton: { padding: 8, borderRadius: 20, backgroundColor: '#FF9500', justifyContent: 'center', alignItems: 'center', position: 'absolute', transform: [{ translateY: -12 }], }, });