fix
This commit is contained in:
parent
6ac3f69e24
commit
918f4da40e
@ -21,8 +21,7 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View
|
View
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
import { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
||||||
import { runOnJS, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
|
||||||
export default function AskScreen() {
|
export default function AskScreen() {
|
||||||
@ -30,6 +29,11 @@ export default function AskScreen() {
|
|||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
const chatListRef = useRef<FlatList>(null);
|
const chatListRef = useRef<FlatList>(null);
|
||||||
|
const isMountedRef = useRef(true);
|
||||||
|
const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
const keyboardTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
const abortControllerRef = useRef<AbortController | null>(null);
|
||||||
|
|
||||||
const [isHello, setIsHello] = useState(true);
|
const [isHello, setIsHello] = useState(true);
|
||||||
const [conversationId, setConversationId] = useState<string | null>(null);
|
const [conversationId, setConversationId] = useState<string | null>(null);
|
||||||
const [userMessages, setUserMessages] = useState<Message[]>([]);
|
const [userMessages, setUserMessages] = useState<Message[]>([]);
|
||||||
@ -43,29 +47,49 @@ export default function AskScreen() {
|
|||||||
newSession: string;
|
newSession: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// 创建一个可复用的滚动函数
|
// 创建一个安全的滚动函数
|
||||||
const scrollToEnd = useCallback((animated = true) => {
|
const scrollToEnd = useCallback((animated = true) => {
|
||||||
if (chatListRef.current) {
|
if (!isMountedRef.current || !chatListRef.current) return;
|
||||||
setTimeout(() => chatListRef.current?.scrollToEnd({ animated }), 100);
|
|
||||||
|
// 清理之前的定时器
|
||||||
|
if (scrollTimeoutRef.current) {
|
||||||
|
clearTimeout(scrollTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTimeoutRef.current = setTimeout(() => {
|
||||||
|
if (isMountedRef.current && chatListRef.current) {
|
||||||
|
try {
|
||||||
|
chatListRef.current.scrollToEnd({ animated });
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('滚动到底部失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
const cleanup = useCallback(() => {
|
||||||
|
isMountedRef.current = false;
|
||||||
|
|
||||||
|
// 清理定时器
|
||||||
|
if (scrollTimeoutRef.current) {
|
||||||
|
clearTimeout(scrollTimeoutRef.current);
|
||||||
|
scrollTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
if (keyboardTimeoutRef.current) {
|
||||||
|
clearTimeout(keyboardTimeoutRef.current);
|
||||||
|
keyboardTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消API请求
|
||||||
|
if (abortControllerRef.current) {
|
||||||
|
abortControllerRef.current.abort();
|
||||||
|
abortControllerRef.current = null;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 右滑
|
|
||||||
const gesture = Gesture.Pan()
|
|
||||||
.onEnd((event) => {
|
|
||||||
const { translationX } = event;
|
|
||||||
const threshold = 100; // 滑动阈值
|
|
||||||
|
|
||||||
if (translationX > threshold) {
|
|
||||||
// 从左向右滑动,跳转页面
|
|
||||||
runOnJS(router.replace)("/memo-list");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.minPointers(1)
|
|
||||||
.activeOffsetX([-10, 10]); // 在 X 方向触发的范围
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isHello && userMessages.length > 0) {
|
if (!isHello && userMessages.length > 0 && isMountedRef.current) {
|
||||||
scrollToEnd();
|
scrollToEnd();
|
||||||
}
|
}
|
||||||
}, [userMessages, isHello, scrollToEnd]);
|
}, [userMessages, isHello, scrollToEnd]);
|
||||||
@ -74,8 +98,12 @@ export default function AskScreen() {
|
|||||||
const keyboardDidShowListener = Keyboard.addListener(
|
const keyboardDidShowListener = Keyboard.addListener(
|
||||||
'keyboardDidShow',
|
'keyboardDidShow',
|
||||||
(e) => {
|
(e) => {
|
||||||
setTimeout(() => {
|
if (keyboardTimeoutRef.current) {
|
||||||
if (!isHello) {
|
clearTimeout(keyboardTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardTimeoutRef.current = setTimeout(() => {
|
||||||
|
if (isMountedRef.current && !isHello) {
|
||||||
scrollToEnd();
|
scrollToEnd();
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -85,8 +113,12 @@ export default function AskScreen() {
|
|||||||
const keyboardDidHideListener = Keyboard.addListener(
|
const keyboardDidHideListener = Keyboard.addListener(
|
||||||
'keyboardDidHide',
|
'keyboardDidHide',
|
||||||
() => {
|
() => {
|
||||||
setTimeout(() => {
|
if (keyboardTimeoutRef.current) {
|
||||||
if (!isHello) {
|
clearTimeout(keyboardTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardTimeoutRef.current = setTimeout(() => {
|
||||||
|
if (isMountedRef.current && !isHello) {
|
||||||
scrollToEnd(false);
|
scrollToEnd(false);
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -96,27 +128,29 @@ export default function AskScreen() {
|
|||||||
return () => {
|
return () => {
|
||||||
keyboardDidShowListener.remove();
|
keyboardDidShowListener.remove();
|
||||||
keyboardDidHideListener.remove();
|
keyboardDidHideListener.remove();
|
||||||
|
if (keyboardTimeoutRef.current) {
|
||||||
|
clearTimeout(keyboardTimeoutRef.current);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [isHello]);
|
}, [isHello, scrollToEnd]);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
let isMounted = true;
|
isMountedRef.current = true;
|
||||||
|
|
||||||
// 使用新的WebSocket订阅hook
|
// 使用新的WebSocket订阅hook
|
||||||
const { subscribeToWebSocket } = useWebSocketSubscription(setUserMessages, isMounted);
|
const { subscribeToWebSocket } = useWebSocketSubscription(setUserMessages, isMountedRef.current);
|
||||||
|
|
||||||
// 订阅WebSocket消息
|
// 订阅WebSocket消息
|
||||||
const unsubscribe = subscribeToWebSocket();
|
const unsubscribe = subscribeToWebSocket();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// 设置组件卸载标志
|
|
||||||
isMounted = false;
|
|
||||||
|
|
||||||
// 取消订阅
|
// 取消订阅
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
|
// 执行清理
|
||||||
|
cleanup();
|
||||||
};
|
};
|
||||||
}, [t])
|
}, [t, cleanup])
|
||||||
);
|
);
|
||||||
|
|
||||||
// 创建动画样式
|
// 创建动画样式
|
||||||
@ -141,39 +175,50 @@ export default function AskScreen() {
|
|||||||
}, [isHello]);
|
}, [isHello]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sessionId) {
|
if (sessionId && isMountedRef.current) {
|
||||||
setConversationId(sessionId);
|
setConversationId(sessionId);
|
||||||
setIsHello(false);
|
setIsHello(false);
|
||||||
fetchApi<Message[]>(`/chats/${sessionId}/message-history`).then((res) => {
|
|
||||||
setUserMessages(res);
|
// 创建新的AbortController
|
||||||
|
abortControllerRef.current = new AbortController();
|
||||||
|
|
||||||
|
fetchApi<Message[]>(`/chats/${sessionId}/message-history`, {
|
||||||
|
signal: abortControllerRef.current.signal
|
||||||
|
}).then((res) => {
|
||||||
|
if (isMountedRef.current) {
|
||||||
|
setUserMessages(res);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
if (error.name !== 'AbortError') {
|
||||||
|
console.error('获取消息历史失败:', error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (newSession) {
|
if (newSession && isMountedRef.current) {
|
||||||
setIsHello(true);
|
setIsHello(true);
|
||||||
setConversationId(null);
|
setConversationId(null);
|
||||||
}
|
}
|
||||||
}, [sessionId, newSession]);
|
}, [sessionId, newSession]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isHello) {
|
if (!isHello && isMountedRef.current) {
|
||||||
// 不再自动关闭键盘,让用户手动控制
|
// 不再自动关闭键盘,让用户手动控制
|
||||||
// 这里可以添加其他需要在隐藏hello界面时执行的逻辑
|
// 这里可以添加其他需要在隐藏hello界面时执行的逻辑
|
||||||
scrollToEnd(false);
|
scrollToEnd(false);
|
||||||
}
|
}
|
||||||
}, [isHello]);
|
}, [isHello, scrollToEnd]);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
if (!sessionId) {
|
if (!sessionId && isMountedRef.current) {
|
||||||
setIsHello(true);
|
setIsHello(true);
|
||||||
setUserMessages([])
|
setUserMessages([]);
|
||||||
}
|
}
|
||||||
}, [sessionId])
|
}, [sessionId])
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GestureDetector gesture={gesture}>
|
<View style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
|
||||||
<View style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
|
|
||||||
{/* 导航栏 */}
|
{/* 导航栏 */}
|
||||||
<View style={[styles.navbar, isHello && styles.hiddenNavbar]}>
|
<View style={[styles.navbar, isHello && styles.hiddenNavbar]}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@ -238,8 +283,7 @@ export default function AskScreen() {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
</View >
|
</View>
|
||||||
</GestureDetector >
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,39 +31,67 @@ const RENDER_INTERVAL = 50; // 渲染间隔,单位毫秒
|
|||||||
export default function SendMessage(props: Props) {
|
export default function SendMessage(props: Props) {
|
||||||
const { setIsHello, conversationId, setUserMessages, setConversationId, selectedImages, setSelectedImages } = props;
|
const { setIsHello, conversationId, setUserMessages, setConversationId, selectedImages, setSelectedImages } = props;
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
|
|
||||||
// 用户询问
|
// 用户询问
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
|
||||||
// 添加一个ref来跟踪键盘状态
|
// 添加组件挂载状态跟踪
|
||||||
|
const isMountedRef = useRef(true);
|
||||||
const isKeyboardVisible = useRef(false);
|
const isKeyboardVisible = useRef(false);
|
||||||
const chunkQueue = useRef<string[]>([]);
|
const chunkQueue = useRef<string[]>([]);
|
||||||
const renderInterval = useRef<ReturnType<typeof setInterval> | null>(null);
|
const renderInterval = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
const cleanup = useCallback(() => {
|
||||||
|
isMountedRef.current = false;
|
||||||
|
|
||||||
|
// 清理定时器
|
||||||
|
if (renderInterval.current) {
|
||||||
|
clearInterval(renderInterval.current);
|
||||||
|
renderInterval.current = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理队列
|
||||||
|
chunkQueue.current = [];
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleChatStream = (message: WsMessage) => {
|
const handleChatStream = (message: WsMessage) => {
|
||||||
if (message.type !== 'ChatStream' || !message.chunk) return;
|
if (!isMountedRef.current || message.type !== 'ChatStream' || !message.chunk) return;
|
||||||
|
|
||||||
chunkQueue.current.push(message.chunk);
|
chunkQueue.current.push(message.chunk);
|
||||||
|
|
||||||
if (!renderInterval.current) {
|
if (!renderInterval.current) {
|
||||||
renderInterval.current = setInterval(() => {
|
renderInterval.current = setInterval(() => {
|
||||||
|
if (!isMountedRef.current) {
|
||||||
|
if (renderInterval.current) {
|
||||||
|
clearInterval(renderInterval.current);
|
||||||
|
renderInterval.current = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (chunkQueue.current.length > 0) {
|
if (chunkQueue.current.length > 0) {
|
||||||
const textToRender = chunkQueue.current.join('');
|
const textToRender = chunkQueue.current.join('');
|
||||||
chunkQueue.current = [];
|
chunkQueue.current = [];
|
||||||
|
|
||||||
setUserMessages(prevMessages => {
|
setUserMessages(prevMessages => {
|
||||||
if (prevMessages.length === 0) return prevMessages;
|
try {
|
||||||
|
if (prevMessages.length === 0) return prevMessages;
|
||||||
|
|
||||||
const lastMessage = prevMessages[prevMessages.length - 1];
|
const lastMessage = prevMessages[prevMessages.length - 1];
|
||||||
if (lastMessage.role !== 'assistant') return prevMessages;
|
if (lastMessage.role !== 'assistant') return prevMessages;
|
||||||
|
|
||||||
const updatedContent = (lastMessage.content === 'keepSearchIng' ? '' : lastMessage.content) + textToRender;
|
const updatedContent = (lastMessage.content === 'keepSearchIng' ? '' : lastMessage.content) + textToRender;
|
||||||
|
|
||||||
const updatedLastMessage = { ...lastMessage, content: updatedContent };
|
const updatedLastMessage = { ...lastMessage, content: updatedContent };
|
||||||
|
|
||||||
return [...prevMessages.slice(0, -1), updatedLastMessage];
|
return [...prevMessages.slice(0, -1), updatedLastMessage];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理流式消息时出错:', error);
|
||||||
|
return prevMessages;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (renderInterval.current) {
|
if (renderInterval.current) {
|
||||||
@ -76,7 +104,7 @@ export default function SendMessage(props: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChatStreamEnd = (message: WsMessage) => {
|
const handleChatStreamEnd = (message: WsMessage) => {
|
||||||
if (message.type !== 'ChatStreamEnd') return;
|
if (!isMountedRef.current || message.type !== 'ChatStreamEnd') return;
|
||||||
|
|
||||||
// Stop the timer and process any remaining chunks
|
// Stop the timer and process any remaining chunks
|
||||||
if (renderInterval.current) {
|
if (renderInterval.current) {
|
||||||
@ -88,37 +116,47 @@ export default function SendMessage(props: Props) {
|
|||||||
chunkQueue.current = [];
|
chunkQueue.current = [];
|
||||||
|
|
||||||
setUserMessages(prevMessages => {
|
setUserMessages(prevMessages => {
|
||||||
if (prevMessages.length === 0) return prevMessages;
|
try {
|
||||||
|
if (prevMessages.length === 0) return prevMessages;
|
||||||
|
|
||||||
const lastMessage = prevMessages[prevMessages.length - 1];
|
const lastMessage = prevMessages[prevMessages.length - 1];
|
||||||
if (lastMessage.role !== 'assistant') return prevMessages;
|
if (lastMessage.role !== 'assistant') return prevMessages;
|
||||||
|
|
||||||
// Apply remaining chunks from the queue
|
// Apply remaining chunks from the queue
|
||||||
const contentWithQueue = (lastMessage.content === 'keepSearchIng' ? '' : lastMessage.content) + remainingText;
|
const contentWithQueue = (lastMessage.content === 'keepSearchIng' ? '' : lastMessage.content) + remainingText;
|
||||||
|
|
||||||
// Create the final updated message object
|
// Create the final updated message object
|
||||||
const updatedLastMessage = {
|
const updatedLastMessage = {
|
||||||
...lastMessage,
|
...lastMessage,
|
||||||
// Use the final message from ChatStreamEnd if available, otherwise use the content with queued text
|
// Use the final message from ChatStreamEnd if available, otherwise use the content with queued text
|
||||||
content: message.message ? message.message.content : contentWithQueue,
|
content: message.message ? message.message.content : contentWithQueue,
|
||||||
timestamp: message.message ? message.message.timestamp : lastMessage.timestamp,
|
timestamp: message.message ? message.message.timestamp : lastMessage.timestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
return [...prevMessages.slice(0, -1), updatedLastMessage];
|
return [...prevMessages.slice(0, -1), updatedLastMessage];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理ChatStreamEnd消息时出错:', error);
|
||||||
|
return prevMessages;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChatResponse = (message: WsMessage) => {
|
const handleChatResponse = (message: WsMessage) => {
|
||||||
if (message.type === 'ChatResponse' && message.message) {
|
if (!isMountedRef.current || message.type !== 'ChatResponse') return;
|
||||||
setUserMessages(prev => {
|
|
||||||
const newMessages = prev.filter(item => item.content !== 'keepSearchIng');
|
if (message.message) {
|
||||||
return [...newMessages, {
|
setUserMessages(prevMessages => {
|
||||||
...(message.message as Message),
|
try {
|
||||||
role: 'assistant',
|
const updatedMessages = [...prevMessages];
|
||||||
}];
|
updatedMessages[updatedMessages.length - 1] = message.message as Message;
|
||||||
|
return updatedMessages;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理聊天响应时出错:', error);
|
||||||
|
return prevMessages;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const typedHandleChatStream = handleChatStream as (message: WsMessage) => void;
|
const typedHandleChatStream = handleChatStream as (message: WsMessage) => void;
|
||||||
const typedHandleChatStreamEnd = handleChatStreamEnd as (message: WsMessage) => void;
|
const typedHandleChatStreamEnd = handleChatStreamEnd as (message: WsMessage) => void;
|
||||||
@ -130,14 +168,21 @@ export default function SendMessage(props: Props) {
|
|||||||
webSocketManager.subscribe('ChatResponse', typedHandleChatResponse);
|
webSocketManager.subscribe('ChatResponse', typedHandleChatResponse);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
webSocketManager.unsubscribe('ChatStream', typedHandleChatStream);
|
webSocketManager.unsubscribe('ChatStream', handleChatStream);
|
||||||
webSocketManager.unsubscribe('ChatStreamEnd', typedHandleChatStreamEnd);
|
webSocketManager.unsubscribe('ChatStreamEnd', handleChatStreamEnd);
|
||||||
webSocketManager.unsubscribe('ChatResponse', typedHandleChatResponse);
|
webSocketManager.unsubscribe('ChatResponse', handleChatResponse);
|
||||||
if (renderInterval.current) {
|
|
||||||
clearInterval(renderInterval.current);
|
// 执行清理
|
||||||
}
|
cleanup();
|
||||||
};
|
};
|
||||||
}, [setUserMessages]);
|
}, [setUserMessages, cleanup]);
|
||||||
|
|
||||||
|
// 组件卸载时的清理
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
cleanup();
|
||||||
|
};
|
||||||
|
}, [cleanup]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 使用keyboardWillShow而不是keyboardDidShow,这样可以在键盘完全显示前更新UI
|
// 使用keyboardWillShow而不是keyboardDidShow,这样可以在键盘完全显示前更新UI
|
||||||
@ -172,11 +217,15 @@ export default function SendMessage(props: Props) {
|
|||||||
|
|
||||||
// 发送询问
|
// 发送询问
|
||||||
const handleSubmit = useCallback(async () => {
|
const handleSubmit = useCallback(async () => {
|
||||||
|
if (!inputValue.trim() || !isMountedRef.current) return;
|
||||||
|
|
||||||
const text = inputValue.trim();
|
const text = inputValue.trim();
|
||||||
// 用户输入信息之后进行后续操作
|
setIsHello(false);
|
||||||
if (text) {
|
|
||||||
// 将用户输入信息添加到消息列表中
|
// 添加用户消息和占位符助手消息
|
||||||
setUserMessages(pre => ([...pre, {
|
setUserMessages(prev => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
id: Math.random().toString(36).substring(2, 9),
|
id: Math.random().toString(36).substring(2, 9),
|
||||||
content: text,
|
content: text,
|
||||||
role: 'user',
|
role: 'user',
|
||||||
@ -188,24 +237,18 @@ export default function SendMessage(props: Props) {
|
|||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
}
|
}
|
||||||
]));
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
let currentSessionId = conversationId;
|
let currentSessionId = conversationId;
|
||||||
// 如果没有对话ID,先创建一个新对话
|
|
||||||
if (!currentSessionId) {
|
if (!currentSessionId) {
|
||||||
currentSessionId = await createNewConversation(text);
|
currentSessionId = await createNewConversation(text);
|
||||||
setConversationId(currentSessionId);
|
if (currentSessionId && isMountedRef.current) {
|
||||||
const webSocketManager = getWebSocketManager();
|
setConversationId(currentSessionId);
|
||||||
webSocketManager.send({
|
}
|
||||||
type: 'Chat',
|
|
||||||
session_id: currentSessionId,
|
|
||||||
message: text,
|
|
||||||
image_material_ids: selectedImages.length > 0 ? selectedImages : undefined,
|
|
||||||
});
|
|
||||||
setSelectedImages([]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过 WebSocket 发送消息
|
if (currentSessionId && isMountedRef.current) {
|
||||||
if (currentSessionId) {
|
|
||||||
const webSocketManager = getWebSocketManager();
|
const webSocketManager = getWebSocketManager();
|
||||||
webSocketManager.send({
|
webSocketManager.send({
|
||||||
type: 'Chat',
|
type: 'Chat',
|
||||||
@ -217,9 +260,19 @@ export default function SendMessage(props: Props) {
|
|||||||
} else {
|
} else {
|
||||||
console.error("无法获取 session_id,消息发送失败。");
|
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'));
|
setUserMessages(prev => prev.filter(item => item.content !== 'keepSearchIng'));
|
||||||
}
|
}
|
||||||
// 将输入框清空
|
}
|
||||||
|
|
||||||
|
// 将输入框清空
|
||||||
|
if (isMountedRef.current) {
|
||||||
setInputValue('');
|
setInputValue('');
|
||||||
// 只有在键盘可见时才关闭键盘
|
// 只有在键盘可见时才关闭键盘
|
||||||
if (isKeyboardVisible.current) {
|
if (isKeyboardVisible.current) {
|
||||||
@ -228,8 +281,10 @@ export default function SendMessage(props: Props) {
|
|||||||
}
|
}
|
||||||
}, [inputValue, conversationId, selectedImages, createNewConversation, setConversationId, setSelectedImages, setUserMessages]);
|
}, [inputValue, conversationId, selectedImages, createNewConversation, setConversationId, setSelectedImages, setUserMessages]);
|
||||||
|
|
||||||
const handleQuitly = (type: string) => {
|
const handleQuitly = useCallback((type: string) => {
|
||||||
setIsHello(false)
|
if (!isMountedRef.current) return;
|
||||||
|
|
||||||
|
setIsHello(false);
|
||||||
setUserMessages(pre => ([
|
setUserMessages(pre => ([
|
||||||
...pre,
|
...pre,
|
||||||
{
|
{
|
||||||
@ -240,8 +295,8 @@ export default function SendMessage(props: Props) {
|
|||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
}
|
}
|
||||||
]))
|
]));
|
||||||
};
|
}, [t, setIsHello, setUserMessages]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getWebSocketManager, WsMessage, getWebSocketErrorMessage } from '@/lib/websocket-util';
|
import { getWebSocketManager, WsMessage, getWebSocketErrorMessage } from '@/lib/websocket-util';
|
||||||
import { Message, Assistant } from '@/types/ask';
|
import { Message, Assistant } from '@/types/ask';
|
||||||
@ -9,10 +9,16 @@ export const useWebSocketSubscription = (
|
|||||||
isMounted: boolean
|
isMounted: boolean
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const isMountedRef = useRef(isMounted);
|
||||||
|
|
||||||
|
// 更新挂载状态
|
||||||
|
useEffect(() => {
|
||||||
|
isMountedRef.current = isMounted;
|
||||||
|
}, [isMounted]);
|
||||||
|
|
||||||
const handleChatStream = useCallback((message: WsMessage) => {
|
const handleChatStream = useCallback((message: WsMessage) => {
|
||||||
// 确保组件仍然挂载
|
// 确保组件仍然挂载
|
||||||
if (!isMounted) return;
|
if (!isMountedRef.current) return;
|
||||||
|
|
||||||
if (message.type === 'ChatStream') {
|
if (message.type === 'ChatStream') {
|
||||||
setUserMessages(prevMessages => {
|
setUserMessages(prevMessages => {
|
||||||
@ -71,7 +77,7 @@ export const useWebSocketSubscription = (
|
|||||||
|
|
||||||
const handleChatStreamEnd = useCallback((message: WsMessage) => {
|
const handleChatStreamEnd = useCallback((message: WsMessage) => {
|
||||||
// 确保组件仍然挂载
|
// 确保组件仍然挂载
|
||||||
if (!isMounted) return;
|
if (!isMountedRef.current) return;
|
||||||
|
|
||||||
if (message.type === 'ChatStreamEnd') {
|
if (message.type === 'ChatStreamEnd') {
|
||||||
setUserMessages(prevMessages => {
|
setUserMessages(prevMessages => {
|
||||||
@ -106,7 +112,7 @@ export const useWebSocketSubscription = (
|
|||||||
|
|
||||||
const handleError = useCallback((message: WsMessage) => {
|
const handleError = useCallback((message: WsMessage) => {
|
||||||
// 确保组件仍然挂载
|
// 确保组件仍然挂载
|
||||||
if (!isMounted) return;
|
if (!isMountedRef.current) return;
|
||||||
|
|
||||||
if (message.type === 'Error') {
|
if (message.type === 'Error') {
|
||||||
console.log(`WebSocket Error: ${message.code} - ${message.message}`);
|
console.log(`WebSocket Error: ${message.code} - ${message.message}`);
|
||||||
@ -139,7 +145,7 @@ export const useWebSocketSubscription = (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [setUserMessages, isMounted, t]);
|
}, [setUserMessages, t]);
|
||||||
|
|
||||||
const subscribeToWebSocket = useCallback(() => {
|
const subscribeToWebSocket = useCallback(() => {
|
||||||
const webSocketManager = getWebSocketManager();
|
const webSocketManager = getWebSocketManager();
|
||||||
@ -155,8 +161,8 @@ export const useWebSocketSubscription = (
|
|||||||
webSocketManager.unsubscribe('ChatStreamEnd', handleChatStreamEnd);
|
webSocketManager.unsubscribe('ChatStreamEnd', handleChatStreamEnd);
|
||||||
webSocketManager.unsubscribe('Error', handleError);
|
webSocketManager.unsubscribe('Error', handleError);
|
||||||
|
|
||||||
// 可以在这里选择断开连接,或者保持连接以加快下次进入页面的速度
|
// 不要在这里断开连接,因为其他组件可能还在使用
|
||||||
webSocketManager.disconnect();
|
// webSocketManager.disconnect();
|
||||||
};
|
};
|
||||||
}, [handleChatStream, handleChatStreamEnd, handleError]);
|
}, [handleChatStream, handleChatStreamEnd, handleError]);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user