2025-07-30 11:30:34 +08:00

200 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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 {
EventSubscription,
Keyboard,
ScrollView,
StyleSheet,
TextInput,
TouchableOpacity,
View
} from 'react-native';
import { fetchApi } from '@/lib/server-api-util';
import { Message } from '@/types/ask';
import { ThemedText } from '../ThemedText';
interface Props {
setIsHello: Dispatch<SetStateAction<boolean>>,
conversationId: string | null,
setUserMessages: Dispatch<SetStateAction<Message[]>>;
setConversationId: (conversationId: string) => void,
selectedImages: string[];
setSelectedImages: Dispatch<SetStateAction<string[]>>;
}
export default function SendMessage(props: Props) {
const { setIsHello, conversationId, setUserMessages, setConversationId, selectedImages, setSelectedImages } = props;
// 用户询问
const [inputValue, setInputValue] = useState('');
// 创建新对话并获取消息
const createNewConversation = useCallback(async (user_text: string) => {
const data = await fetchApi<string>("/chat/new", {
method: "POST",
});
setConversationId(data);
await getConversation({ session_id: data, user_text, material_ids: [] });
}, []);
// 获取对话信息
const getConversation = useCallback(async ({ session_id, user_text, material_ids }: { session_id: string, user_text: string, material_ids: string[] }) => {
// 获取对话信息必须要有对话id
if (!session_id) return;
const response = await fetchApi<Message>(`/chat`, {
method: "POST",
body: JSON.stringify({
session_id,
user_text,
material_ids
})
});
setSelectedImages([]);
setUserMessages((prev: Message[]) => [...prev, response]?.filter((item: Message) => item.content.text !== '正在寻找,请稍等...'));
}, []);
// 添加一个ref来跟踪键盘状态
const keyboardDidShowListener = useRef<EventSubscription | null>(null);
const keyboardDidHideListener = useRef<EventSubscription | null>(null);
const isKeyboardVisible = useRef(false);
useEffect(() => {
// 使用keyboardWillShow而不是keyboardDidShow这样可以在键盘完全显示前更新UI
const showSubscription = Keyboard.addListener('keyboardWillShow', () => {
isKeyboardVisible.current = true;
if (!conversationId) {
// 确保在下一个事件循环中更新状态,避免可能的渲染问题
requestAnimationFrame(() => {
setIsHello(false);
});
}
});
const hideSubscription = Keyboard.addListener('keyboardWillHide', () => {
isKeyboardVisible.current = false;
});
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, [conversationId]);
// 发送询问
const handleSubmit = useCallback(() => {
const text = inputValue.trim();
// 用户输入信息之后进行后续操作
if (text) {
// 将用户输入信息添加到消息列表中
setUserMessages(pre => ([...pre, {
content: {
text: text
},
role: 'User',
timestamp: new Date().toISOString()
},
{
content: {
text: "正在寻找,请稍等..."
},
role: 'Assistant',
timestamp: new Date().toISOString()
}
]));
// 如果没有对话ID创建新对话并获取消息否则直接获取消息
if (!conversationId) {
createNewConversation(text);
} else {
getConversation({
session_id: conversationId,
user_text: text,
material_ids: selectedImages
});
}
// 将输入框清空
setInputValue('');
// 只有在键盘可见时才关闭键盘
if (isKeyboardVisible.current) {
Keyboard.dismiss();
}
}
}, [inputValue, conversationId, selectedImages, createNewConversation, getConversation]);
return (
<View style={styles.container}>
<View className="relative w-full">
<ScrollView horizontal={true}>
<TouchableOpacity style={[styles.button, { borderColor: '#FFB645' }]}>
<SunSvg width={18} height={18} />
<ThemedText></ThemedText>
</TouchableOpacity><TouchableOpacity style={[styles.button, { borderColor: '#E2793F' }]}>
<VideoSvg width={18} height={18} />
<ThemedText></ThemedText>
</TouchableOpacity>
</ScrollView>
<TextInput
style={styles.input}
placeholder="Ask MeMo Anything..."
placeholderTextColor="#999"
value={inputValue}
onChangeText={(text: string) => {
setInputValue(text);
}}
onSubmitEditing={handleSubmit}
// 调起的键盘类型
returnKeyType="send"
/>
<TouchableOpacity
style={styles.voiceButton}
onPress={handleSubmit}
className={`absolute right-0 bottom-0`} // 使用绝对定位将按钮放在输入框内右侧
>
<View style={{ transform: [{ rotate: '330deg' }] }}>
<SendSvg color={'white'} width={24} height={24} />
</View>
</TouchableOpacity>
</View>
</View>
);
}
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: 12,
fontSize: 16,
width: '100%', // 确保输入框宽度撑满
paddingRight: 50
},
voiceButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#FF9500',
justifyContent: 'center',
alignItems: 'center',
marginRight: 8, // 添加一点
},
});