This commit is contained in:
parent
ae14e05533
commit
11b0c051d1
@ -1,7 +1,7 @@
|
||||
import ReturnArrow from "@/assets/icons/svg/returnArrow.svg";
|
||||
import Chat from "@/components/ask/chat";
|
||||
import AskHello from "@/components/ask/hello";
|
||||
import AudioRecordPlay from "@/components/ask/voice";
|
||||
import SendMessage from "@/components/ask/send";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { fetchApi } from "@/lib/server-api-util";
|
||||
import { Message } from "@/types/ask";
|
||||
@ -105,23 +105,7 @@ export default function AskScreen() {
|
||||
|
||||
{/* 功能区 - 放在 KeyboardAvoidingView 内但在 ScrollView 外 */}
|
||||
<View className="w-full px-[1.5rem] mb-[2rem]">
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{
|
||||
paddingRight: 16,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
gap: 8
|
||||
}}
|
||||
className="pb-2"
|
||||
>
|
||||
<Chip icon="sunny" text="Tdy's vlog" />
|
||||
<Chip icon="happy" text="Smiles" />
|
||||
<Chip icon="image" text="Snapshots" />
|
||||
<Chip icon="time" text="Moments" />
|
||||
</ScrollView>
|
||||
<AudioRecordPlay getConversation={getConversation} setIsHello={setIsHello} setInputValue={setInputValue} conversationId={conversationId} inputValue={inputValue} createNewConversation={createNewConversation} />
|
||||
<SendMessage setUserMessages={setUserMessages} setConversationId={setConversationId} setIsHello={setIsHello} conversationId={conversationId} />
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
|
||||
@ -95,7 +95,7 @@ const renderMessage = ({ item, sessionId, setModalVisible, modalVisible }: Rende
|
||||
<View className="mt-2 flex flex-row gap-2 w-full">
|
||||
{mergeArrays(item.content.image_material_infos || [], item.content.video_material_infos || [])?.slice(0, 3)?.map((image, index, array) => (
|
||||
<Pressable
|
||||
key={image.id}
|
||||
key={`${image.role}-${image.timestamp}`}
|
||||
onPress={() => {
|
||||
setModalVisible({ visible: true, data: image });
|
||||
}}
|
||||
@ -127,7 +127,7 @@ const renderMessage = ({ item, sessionId, setModalVisible, modalVisible }: Rende
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
{item.askAgain && item.askAgain.length > 0 && (
|
||||
{/* {item.askAgain && item.askAgain.length > 0 && (
|
||||
<View className={`mr-10`}>
|
||||
{item.askAgain.map((suggestion, index, array) => (
|
||||
<TouchableOpacity
|
||||
@ -138,7 +138,7 @@ const renderMessage = ({ item, sessionId, setModalVisible, modalVisible }: Rende
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
)} */}
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Message, Video } from '@/types/ask';
|
||||
import { MaterialItem } from '@/types/personal-info';
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import {
|
||||
FlatList,
|
||||
SafeAreaView
|
||||
@ -18,7 +18,7 @@ function ChatComponent({ userMessages, sessionId }: ChatProps) {
|
||||
const [modalVisible, setModalVisible] = React.useState({ visible: false, data: {} as Video | MaterialItem });
|
||||
|
||||
// 使用 useCallback 缓存 keyExtractor 函数
|
||||
const keyExtractor = useCallback((item: Message) => item.timestamp, []);
|
||||
const keyExtractor = useCallback((item: Message) => `${item.role}-${item.timestamp}`, []);
|
||||
|
||||
// 使用 useMemo 缓存样式对象
|
||||
const contentContainerStyle = useMemo(() => ({ padding: 16 }), []);
|
||||
@ -49,4 +49,7 @@ function ChatComponent({ userMessages, sessionId }: ChatProps) {
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 React.memo 包装组件,避免不必要的重渲染
|
||||
export default memo(ChatComponent);
|
||||
135
components/ask/send.tsx
Normal file
135
components/ask/send.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
'use client';
|
||||
import VoiceSvg from '@/assets/icons/svg/vioce.svg';
|
||||
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import { fetchApi } from '@/lib/server-api-util';
|
||||
import { Message } from '@/types/ask';
|
||||
|
||||
interface Props {
|
||||
setIsHello: (isHello: boolean) => void,
|
||||
conversationId: string | null,
|
||||
setUserMessages: Dispatch<SetStateAction<Message[]>>;
|
||||
setConversationId: (conversationId: string) => void,
|
||||
}
|
||||
export default function SendMessage(props: Props) {
|
||||
const { setIsHello, conversationId, setUserMessages, setConversationId } = 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 });
|
||||
}, []);
|
||||
|
||||
// 获取对话信息
|
||||
const getConversation = useCallback(async ({ session_id, user_text }: { session_id: string, user_text: string }) => {
|
||||
// 获取对话信息必须要有对话id
|
||||
if (!session_id) return;
|
||||
const response = await fetchApi<Message>(`/chat`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
session_id,
|
||||
user_text
|
||||
})
|
||||
});
|
||||
setUserMessages((prev: Message[]) => [...prev, response]?.filter((item: Message) => item.content.text !== '正在寻找,请稍等...'));
|
||||
}, []);
|
||||
|
||||
// 发送询问
|
||||
const handleSubmit = () => {
|
||||
const text = inputValue;
|
||||
// 用户输入信息之后进行后续操作
|
||||
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);
|
||||
setIsHello(false);
|
||||
} else {
|
||||
getConversation({
|
||||
session_id: conversationId,
|
||||
user_text: text
|
||||
});
|
||||
}
|
||||
// 将输入框清空
|
||||
setInputValue('');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View className="relative w-full">
|
||||
<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}
|
||||
className={`absolute right-0 top-1/2 -translate-y-1/2 `} // 使用绝对定位将按钮放在输入框内右侧
|
||||
>
|
||||
<VoiceSvg />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
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, // 添加一点右边距
|
||||
},
|
||||
});
|
||||
@ -1,28 +1,29 @@
|
||||
'use client';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
||||
import {
|
||||
Platform,
|
||||
StyleSheet,
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import { fetchApi } from '@/lib/server-api-util';
|
||||
import { Message } from '@/types/ask';
|
||||
import { RecordingPresets, useAudioRecorder } from 'expo-audio';
|
||||
interface Props {
|
||||
setIsHello: (isHello: boolean) => void,
|
||||
setInputValue: (inputValue: string) => void,
|
||||
inputValue: string,
|
||||
createNewConversation: (user_text: string) => void,
|
||||
conversationId: string | null,
|
||||
getConversation: ({ user_text, session_id }: { user_text: string, session_id: string }) => void
|
||||
setUserMessages: Dispatch<SetStateAction<Message[]>>;
|
||||
setConversationId: (conversationId: string) => void,
|
||||
}
|
||||
export default function AudioRecordPlay(props: Props) {
|
||||
const { setIsHello, setInputValue, inputValue, createNewConversation, conversationId, getConversation } = props;
|
||||
const { setIsHello, conversationId, setUserMessages, setConversationId } = props;
|
||||
const audioRecorder = useAudioRecorder(RecordingPresets.HIGH_QUALITY);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [isVoiceStart, setIsVoiceStart] = useState(false);
|
||||
const [elapsedTime, setElapsedTime] = useState(0);
|
||||
|
||||
// 用户询问
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [timerInterval, setTimerInterval] = useState<NodeJS.Timeout | number>(0);
|
||||
|
||||
const formatTime = (ms: number): string => {
|
||||
@ -68,15 +69,39 @@ export default function AudioRecordPlay(props: Props) {
|
||||
// }
|
||||
// })();
|
||||
// }, []);
|
||||
// 使用 useCallback 缓存回调函数
|
||||
const handleChangeText = useCallback((text: string) => {
|
||||
setInputValue(text);
|
||||
// 获取对话信息
|
||||
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 });
|
||||
}, []);
|
||||
|
||||
const getConversation = useCallback(async ({ session_id, user_text }: { session_id: string, user_text: string }) => {
|
||||
if (!session_id) return;
|
||||
const response = await fetchApi<Message>(`/chat`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
session_id,
|
||||
user_text
|
||||
})
|
||||
});
|
||||
setUserMessages((prev: Message[]) => [...prev, response]);
|
||||
}, []);
|
||||
|
||||
// 使用 useCallback 缓存 handleSubmit
|
||||
const handleSubmit = useCallback(() => {
|
||||
const text = inputValue.trim();
|
||||
const handleSubmit = () => {
|
||||
const text = inputValue;
|
||||
if (text) {
|
||||
setUserMessages(pre => ([...pre, {
|
||||
content: {
|
||||
text: text
|
||||
},
|
||||
role: 'User',
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
]));
|
||||
if (!conversationId) {
|
||||
createNewConversation(text);
|
||||
setIsHello(false);
|
||||
@ -88,15 +113,7 @@ export default function AudioRecordPlay(props: Props) {
|
||||
}
|
||||
setInputValue('');
|
||||
}
|
||||
}, [conversationId]);
|
||||
|
||||
// 使用 useCallback 缓存 handleKeyPress
|
||||
const handleKeyPress = useCallback((e: any) => {
|
||||
if (Platform.OS === 'web' && e.nativeEvent.key !== 'Enter') {
|
||||
return;
|
||||
}
|
||||
handleSubmit();
|
||||
}, [handleSubmit]);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user