196 lines
6.4 KiB
TypeScript
196 lines
6.4 KiB
TypeScript
import ReturnArrow from "@/assets/icons/svg/returnArrow.svg";
|
|
import Chat from "@/components/ask/chat";
|
|
import AskHello from "@/components/ask/hello";
|
|
import SendMessage from "@/components/ask/send";
|
|
import { ThemedText } from "@/components/ThemedText";
|
|
import { fetchApi } from "@/lib/server-api-util";
|
|
import { Message } from "@/types/ask";
|
|
import { router, useLocalSearchParams } from "expo-router";
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
import { KeyboardAvoidingView, Platform, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
|
|
export default function AskScreen() {
|
|
const insets = useSafeAreaInsets();
|
|
// 在组件内部添加 ref
|
|
const scrollViewRef = useRef<ScrollView>(null);
|
|
// 用于控制是否显示问候页面
|
|
const [isHello, setIsHello] = useState(true);
|
|
|
|
// 获取对话id
|
|
const [conversationId, setConversationId] = useState<string | null>(null);
|
|
|
|
// 用户对话信息收集
|
|
const [userMessages, setUserMessages] = useState<Message[]>([]);
|
|
|
|
const createNewConversation = useCallback(async () => {
|
|
// TODO 用户未输入时,显示提示信息
|
|
setUserMessages([{
|
|
content: {
|
|
text: "请输入您的问题,寻找,请稍等..."
|
|
},
|
|
role: 'Assistant',
|
|
timestamp: new Date().toISOString()
|
|
}]);
|
|
const data = await fetchApi<string>("/chat/new", {
|
|
method: "POST",
|
|
});
|
|
setConversationId(data);
|
|
}, []);
|
|
|
|
// 获取路由参数
|
|
const { sessionId, newSession } = useLocalSearchParams<{
|
|
sessionId: string;
|
|
newSession: string;
|
|
}>();
|
|
// 添加自动滚动到底部的效果
|
|
useEffect(() => {
|
|
if (scrollViewRef.current && !isHello) {
|
|
scrollViewRef.current.scrollToEnd({ animated: true });
|
|
}
|
|
}, [userMessages, isHello]);
|
|
|
|
useEffect(() => {
|
|
if (sessionId) {
|
|
setConversationId(sessionId)
|
|
setIsHello(false)
|
|
fetchApi<Message[]>(`/chats/${sessionId}/message-history`).then((res) => {
|
|
setUserMessages(res)
|
|
})
|
|
}
|
|
if (newSession) {
|
|
setIsHello(false)
|
|
createNewConversation()
|
|
}
|
|
}, [sessionId, newSession])
|
|
|
|
return (
|
|
<View style={{ flex: 1, backgroundColor: 'white', paddingTop: insets.top }}>
|
|
{/* 导航栏 - 保持在顶部 */}
|
|
<View style={isHello ? "" : styles.navbar} className="relative w-full flex flex-row items-center justify-between pb-3 pt-[2rem]">
|
|
{/* 点击去memo list 页面 */}
|
|
<TouchableOpacity
|
|
style={styles.backButton}
|
|
onPress={() => {
|
|
router.replace('/memo-list');
|
|
}}
|
|
>
|
|
<ReturnArrow />
|
|
</TouchableOpacity>
|
|
<ThemedText className={`!text-textSecondary font-semibold text-3xl w-full text-center flex-1 ${isHello ? "opacity-0" : ""}`}>MemoWake</ThemedText>
|
|
<View />
|
|
</View>
|
|
|
|
<KeyboardAvoidingView
|
|
style={{ flex: 1 }}
|
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
keyboardVerticalOffset={Platform.OS === "ios" ? 0 : 20}
|
|
>
|
|
<ScrollView
|
|
ref={scrollViewRef}
|
|
contentContainerStyle={{ flexGrow: 1 }}
|
|
keyboardShouldPersistTaps="handled"
|
|
showsVerticalScrollIndicator={false}
|
|
bounces={false}
|
|
onContentSizeChange={() => {
|
|
if (scrollViewRef.current && !isHello) {
|
|
scrollViewRef.current.scrollToEnd({ animated: true });
|
|
}
|
|
}}
|
|
>
|
|
{/* 内容区域 */}
|
|
<View className="flex-1">
|
|
{isHello ? <AskHello /> : <Chat userMessages={userMessages} sessionId={sessionId} />}
|
|
</View>
|
|
</ScrollView>
|
|
|
|
{/* 功能区 - 放在 KeyboardAvoidingView 内但在 ScrollView 外 */}
|
|
<View className="w-full px-[1.5rem] mb-[2rem]">
|
|
<SendMessage setUserMessages={setUserMessages} setConversationId={setConversationId} setIsHello={setIsHello} conversationId={conversationId} />
|
|
</View>
|
|
</KeyboardAvoidingView>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
navbar: {
|
|
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
|
|
backgroundColor: 'white',
|
|
zIndex: 10,
|
|
},
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: 'white',
|
|
borderTopLeftRadius: 20,
|
|
borderTopRightRadius: 20,
|
|
paddingTop: 60
|
|
},
|
|
backButton: {
|
|
marginLeft: 16,
|
|
padding: 12
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
padding: 20,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
description: {
|
|
fontSize: 16,
|
|
color: '#666',
|
|
textAlign: 'center',
|
|
marginBottom: 40,
|
|
paddingHorizontal: 20,
|
|
lineHeight: 24,
|
|
},
|
|
chipsContainer: {
|
|
width: "100%",
|
|
flexDirection: 'row',
|
|
flexWrap: 'nowrap',
|
|
justifyContent: 'center',
|
|
marginBottom: 40,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
overflow: "scroll",
|
|
},
|
|
chip: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: '#FFF5E6',
|
|
paddingVertical: 10,
|
|
paddingHorizontal: 16,
|
|
borderRadius: 20,
|
|
margin: 5,
|
|
},
|
|
chipText: {
|
|
marginLeft: 6,
|
|
color: '#FF9500',
|
|
fontSize: 14,
|
|
},
|
|
inputContainer: {
|
|
flexDirection: 'row',
|
|
padding: 16,
|
|
paddingBottom: 30,
|
|
backgroundColor: 'white',
|
|
},
|
|
input: {
|
|
flex: 1,
|
|
borderColor: '#FF9500',
|
|
borderWidth: 1,
|
|
borderRadius: 25,
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 12,
|
|
fontSize: 16,
|
|
width: '100%', // 确保输入框宽度撑满
|
|
},
|
|
voiceButton: {
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 20,
|
|
backgroundColor: '#FF9500',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
marginRight: 8, // 添加一点右边距
|
|
},
|
|
}); |