feat: ask交互
This commit is contained in:
parent
479eecdc95
commit
b261fbf970
@ -5,8 +5,8 @@ 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 { default as React, default as React, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
FlatList,
|
||||
@ -116,7 +116,6 @@ export default function AskScreen() {
|
||||
Animated.timing(fadeAnimChat, {
|
||||
toValue: 1,
|
||||
duration: 300,
|
||||
duration: 300,
|
||||
useNativeDriver: true,
|
||||
})
|
||||
]).start(() => {
|
||||
@ -131,31 +130,17 @@ export default function AskScreen() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!isHello) {
|
||||
const timer = setTimeout(() => {
|
||||
scrollToEnd(false);
|
||||
}, 300);
|
||||
return () => clearTimeout(timer);
|
||||
// 不再自动关闭键盘,让用户手动控制
|
||||
// 这里可以添加其他需要在隐藏hello界面时执行的逻辑
|
||||
scrollToEnd(false);
|
||||
}
|
||||
}, [isHello]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (!isHello) {
|
||||
try {
|
||||
if (TextInput.State?.currentlyFocusedInput) {
|
||||
const input = TextInput.State.currentlyFocusedInput();
|
||||
if (input) input.blur();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('失去焦点失败:', error);
|
||||
}
|
||||
|
||||
scrollToEnd(false);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [isHello]);
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
setIsHello(true);
|
||||
}, [])
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { paddingTop: insets.top }]}>
|
||||
@ -299,7 +284,7 @@ const styles = StyleSheet.create({
|
||||
padding: 16,
|
||||
paddingBottom: 24,
|
||||
backgroundColor: 'white',
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#f0f0f0',
|
||||
// borderTopWidth: 1,
|
||||
// borderTopColor: '#f0f0f0',
|
||||
},
|
||||
});
|
||||
3
assets/icons/svg/sun.svg
Normal file
3
assets/icons/svg/sun.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 3V1.5M9 15V16.5M4.81066 4.81066L3.75 3.75M13.296 13.296L14.3567 14.3567M3 9H1.5M15 9H16.5M13.2964 4.81066L14.357 3.75M4.81103 13.296L3.75037 14.3567M9 12.75C6.92893 12.75 5.25 11.0711 5.25 9C5.25 6.92893 6.92893 5.25 9 5.25C11.0711 5.25 12.75 6.92893 12.75 9C12.75 11.0711 11.0711 12.75 9 12.75Z" stroke="#FFB645" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 497 B |
3
assets/icons/svg/video.svg
Normal file
3
assets/icons/svg/video.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 0.609375C3.35685 0.609375 0 3.96623 0 8.10938C0 12.2525 3.35685 15.6094 7.5 15.6094C11.6431 15.6094 15 12.2525 15 8.10938C15 3.96623 11.6431 0.609375 7.5 0.609375ZM9.91935 5.69002C10.4546 5.69002 10.8871 6.12248 10.8871 6.65776C10.8871 7.19304 10.4546 7.6255 9.91935 7.6255C9.38407 7.6255 8.95161 7.19304 8.95161 6.65776C8.95161 6.12248 9.38407 5.69002 9.91935 5.69002ZM5.08065 5.69002C5.61593 5.69002 6.04839 6.12248 6.04839 6.65776C6.04839 7.19304 5.61593 7.6255 5.08065 7.6255C4.54536 7.6255 4.1129 7.19304 4.1129 6.65776C4.1129 6.12248 4.54536 5.69002 5.08065 5.69002ZM10.9718 10.8372C10.1099 11.8715 8.84577 12.4642 7.5 12.4642C6.15423 12.4642 4.89012 11.8715 4.02823 10.8372C3.61694 10.3443 4.36089 9.72732 4.77218 10.2172C5.4496 11.0307 6.44153 11.4934 7.5 11.4934C8.55847 11.4934 9.5504 11.0277 10.2278 10.2172C10.6331 9.72732 11.38 10.3443 10.9718 10.8372Z" fill="#E2793F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1001 B |
@ -5,7 +5,8 @@ import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
FlatList,
|
||||
FlatListProps,
|
||||
SafeAreaView
|
||||
SafeAreaView,
|
||||
View
|
||||
} from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import MessageItem from './aiChat';
|
||||
@ -27,7 +28,11 @@ function ChatComponent(
|
||||
const { t } = useTranslation();
|
||||
const keyExtractor = useCallback((item: Message) => `${item.role}-${item.timestamp}`, []);
|
||||
|
||||
const contentContainerStyle = useMemo(() => ({ padding: 16, flexGrow: 1 }), []);
|
||||
const contentContainerStyle = useMemo(() => ({
|
||||
padding: 16,
|
||||
flexGrow: 1,
|
||||
paddingTop: 0,
|
||||
}), []);
|
||||
|
||||
const [modalDetailsVisible, setModalDetailsVisible] = useState<boolean>(false);
|
||||
|
||||
@ -37,7 +42,14 @@ function ChatComponent(
|
||||
ref={ref}
|
||||
data={userMessages}
|
||||
keyExtractor={keyExtractor}
|
||||
renderItem={({ item }) => MessageItem({ t, setSelectedImages, selectedImages, insets, item, sessionId, modalVisible, setModalVisible, setModalDetailsVisible, modalDetailsVisible })}
|
||||
renderItem={({ item, index }) => {
|
||||
const itemStyle = index === 0 ? { marginTop: 16 } : {};
|
||||
return (
|
||||
<View style={itemStyle}>
|
||||
{MessageItem({ t, setSelectedImages, selectedImages, insets, item, sessionId, modalVisible, setModalVisible, setModalDetailsVisible, modalDetailsVisible })}
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
contentContainerStyle={contentContainerStyle}
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
'use client';
|
||||
import SendSvg from '@/assets/icons/svg/send.svg';
|
||||
import React, { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react';
|
||||
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,
|
||||
@ -11,6 +15,7 @@ import {
|
||||
|
||||
import { fetchApi } from '@/lib/server-api-util';
|
||||
import { Message } from '@/types/ask';
|
||||
import { ThemedText } from '../ThemedText';
|
||||
|
||||
interface Props {
|
||||
setIsHello: Dispatch<SetStateAction<boolean>>,
|
||||
@ -52,9 +57,36 @@ export default function SendMessage(props: Props) {
|
||||
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 = () => {
|
||||
const text = inputValue;
|
||||
const handleSubmit = useCallback(() => {
|
||||
const text = inputValue.trim();
|
||||
// 用户输入信息之后进行后续操作
|
||||
if (text) {
|
||||
// 将用户输入信息添加到消息列表中
|
||||
@ -85,38 +117,25 @@ export default function SendMessage(props: Props) {
|
||||
}
|
||||
// 将输入框清空
|
||||
setInputValue('');
|
||||
// 关闭键盘
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
const keyboardWillShowListener = Keyboard.addListener(
|
||||
'keyboardWillShow',
|
||||
() => {
|
||||
if (!conversationId) {
|
||||
console.log('Keyboard will show');
|
||||
setIsHello(false);
|
||||
setUserMessages([{
|
||||
content: {
|
||||
text: "快来寻找你的记忆吧。。。"
|
||||
},
|
||||
role: 'Assistant',
|
||||
timestamp: new Date().toISOString()
|
||||
}])
|
||||
} else {
|
||||
|
||||
}
|
||||
// 只有在键盘可见时才关闭键盘
|
||||
if (isKeyboardVisible.current) {
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
keyboardWillShowListener.remove();
|
||||
};
|
||||
}, [conversationId]);
|
||||
}
|
||||
}, [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..."
|
||||
@ -132,7 +151,7 @@ export default function SendMessage(props: Props) {
|
||||
<TouchableOpacity
|
||||
style={styles.voiceButton}
|
||||
onPress={handleSubmit}
|
||||
className={`absolute right-0 top-1/2 -translate-y-1/2 `} // 使用绝对定位将按钮放在输入框内右侧
|
||||
className={`absolute right-0 bottom-0`} // 使用绝对定位将按钮放在输入框内右侧
|
||||
>
|
||||
<View style={{ transform: [{ rotate: '330deg' }] }}>
|
||||
<SendSvg color={'white'} width={24} height={24} />
|
||||
@ -144,6 +163,17 @@ export default function SendMessage(props: Props) {
|
||||
}
|
||||
|
||||
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',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user