Compare commits
3 Commits
2f4fd12c67
...
546bca3472
| Author | SHA1 | Date | |
|---|---|---|---|
| 546bca3472 | |||
| 6e8113e109 | |||
| fab73d196f |
@ -20,6 +20,8 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View
|
View
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
||||||
|
import { runOnJS } 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() {
|
||||||
@ -46,6 +48,25 @@ export default function AskScreen() {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const updateState = (value: string) => {
|
||||||
|
console.log('Received in JS:', value);
|
||||||
|
// 更新 React 状态或路由
|
||||||
|
router.replace(value);
|
||||||
|
};
|
||||||
|
// 右滑
|
||||||
|
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) {
|
||||||
scrollToEnd();
|
scrollToEnd();
|
||||||
@ -236,86 +257,88 @@ export default function AskScreen() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { paddingTop: insets.top }]}>
|
<GestureDetector gesture={gesture}>
|
||||||
{/* 导航栏 */}
|
<View style={[styles.container, { paddingTop: insets.top }]}>
|
||||||
<View style={[styles.navbar, isHello && styles.hiddenNavbar]}>
|
{/* 导航栏 */}
|
||||||
<TouchableOpacity
|
<View style={[styles.navbar, isHello && styles.hiddenNavbar]}>
|
||||||
style={styles.backButton}
|
<TouchableOpacity
|
||||||
onPress={() => {
|
style={styles.backButton}
|
||||||
try {
|
onPress={() => {
|
||||||
if (TextInput.State?.currentlyFocusedInput) {
|
try {
|
||||||
const input = TextInput.State.currentlyFocusedInput();
|
if (TextInput.State?.currentlyFocusedInput) {
|
||||||
if (input) input.blur();
|
const input = TextInput.State.currentlyFocusedInput();
|
||||||
|
if (input) input.blur();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('失去焦点失败:', error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
Keyboard.dismiss();
|
||||||
console.log('失去焦点失败:', error);
|
router.push('/memo-list');
|
||||||
}
|
}}
|
||||||
Keyboard.dismiss();
|
>
|
||||||
router.push('/memo-list');
|
<ReturnArrow />
|
||||||
}}
|
</TouchableOpacity>
|
||||||
>
|
<ThemedText style={styles.title} onPress={() => { router.push('/owner') }}>MemoWake</ThemedText>
|
||||||
<ReturnArrow />
|
<View style={styles.placeholder} />
|
||||||
</TouchableOpacity>
|
|
||||||
<ThemedText style={styles.title} onPress={() => { router.push('/owner') }}>MemoWake</ThemedText>
|
|
||||||
<View style={styles.placeholder} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.contentContainer}>
|
|
||||||
{/* 欢迎页面 */}
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.absoluteView,
|
|
||||||
{
|
|
||||||
opacity: fadeAnim,
|
|
||||||
pointerEvents: isHello ? 'auto' : 'none',
|
|
||||||
zIndex: 1
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<AskHello setUserMessages={setUserMessages} setConversationId={setConversationId} setIsHello={setIsHello} />
|
|
||||||
</Animated.View>
|
|
||||||
|
|
||||||
{/* 聊天页面 */}
|
|
||||||
<Animated.View
|
|
||||||
style={[
|
|
||||||
styles.absoluteView,
|
|
||||||
{
|
|
||||||
opacity: fadeAnimChat,
|
|
||||||
pointerEvents: isHello ? 'none' : 'auto',
|
|
||||||
zIndex: 0
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Chat
|
|
||||||
ref={chatListRef}
|
|
||||||
userMessages={userMessages}
|
|
||||||
sessionId={sessionId}
|
|
||||||
setSelectedImages={setSelectedImages}
|
|
||||||
selectedImages={selectedImages}
|
|
||||||
style={styles.chatContainer}
|
|
||||||
contentContainerStyle={styles.chatContentContainer}
|
|
||||||
showsVerticalScrollIndicator={false}
|
|
||||||
onContentSizeChange={() => scrollToEnd()}
|
|
||||||
/>
|
|
||||||
</Animated.View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 输入框区域 */}
|
|
||||||
<KeyboardAvoidingView
|
|
||||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
|
||||||
keyboardVerticalOffset={0} >
|
|
||||||
<View style={styles.inputContainer} key={conversationId}>
|
|
||||||
<SendMessage
|
|
||||||
setIsHello={setIsHello}
|
|
||||||
conversationId={conversationId}
|
|
||||||
setConversationId={setConversationId}
|
|
||||||
setUserMessages={setUserMessages}
|
|
||||||
selectedImages={selectedImages}
|
|
||||||
setSelectedImages={setSelectedImages}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
</KeyboardAvoidingView>
|
|
||||||
</View >
|
<View style={styles.contentContainer}>
|
||||||
|
{/* 欢迎页面 */}
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.absoluteView,
|
||||||
|
{
|
||||||
|
opacity: fadeAnim,
|
||||||
|
pointerEvents: isHello ? 'auto' : 'none',
|
||||||
|
zIndex: 1
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<AskHello setUserMessages={setUserMessages} setConversationId={setConversationId} setIsHello={setIsHello} />
|
||||||
|
</Animated.View>
|
||||||
|
|
||||||
|
{/* 聊天页面 */}
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.absoluteView,
|
||||||
|
{
|
||||||
|
opacity: fadeAnimChat,
|
||||||
|
pointerEvents: isHello ? 'none' : 'auto',
|
||||||
|
zIndex: 0
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Chat
|
||||||
|
ref={chatListRef}
|
||||||
|
userMessages={userMessages}
|
||||||
|
sessionId={sessionId}
|
||||||
|
setSelectedImages={setSelectedImages}
|
||||||
|
selectedImages={selectedImages}
|
||||||
|
style={styles.chatContainer}
|
||||||
|
contentContainerStyle={styles.chatContentContainer}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
onContentSizeChange={() => scrollToEnd()}
|
||||||
|
/>
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 输入框区域 */}
|
||||||
|
<KeyboardAvoidingView
|
||||||
|
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||||
|
keyboardVerticalOffset={0} >
|
||||||
|
<View style={styles.inputContainer} key={conversationId}>
|
||||||
|
<SendMessage
|
||||||
|
setIsHello={setIsHello}
|
||||||
|
conversationId={conversationId}
|
||||||
|
setConversationId={setConversationId}
|
||||||
|
setUserMessages={setUserMessages}
|
||||||
|
selectedImages={selectedImages}
|
||||||
|
setSelectedImages={setSelectedImages}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
</View >
|
||||||
|
</GestureDetector>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { checkNotificationPermission, getLocationPermission, getPermissions, req
|
|||||||
import { ThemedText } from '@/components/ThemedText';
|
import { ThemedText } from '@/components/ThemedText';
|
||||||
import { useAuth } from '@/contexts/auth-context';
|
import { useAuth } from '@/contexts/auth-context';
|
||||||
import { fetchApi } from '@/lib/server-api-util';
|
import { fetchApi } from '@/lib/server-api-util';
|
||||||
import { Address } from '@/types/user';
|
import { Address, User, UserInfoDetails } from '@/types/user';
|
||||||
import * as Location from 'expo-location';
|
import * as Location from 'expo-location';
|
||||||
import { useFocusEffect, useRouter } from 'expo-router';
|
import { useFocusEffect, useRouter } from 'expo-router';
|
||||||
import * as SecureStore from 'expo-secure-store';
|
import * as SecureStore from 'expo-secure-store';
|
||||||
@ -20,8 +20,14 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Linking, Platform, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { Linking, Platform, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
|
||||||
const Setting = (props: { userInfo: any }) => {
|
const Setting = (props: { userInfo: UserInfoDetails }) => {
|
||||||
const { userInfo } = props;
|
const [userInfo, setUserInfo] = useState<User | null>(null);
|
||||||
|
|
||||||
|
const getUserInfo = async () => {
|
||||||
|
const res = await fetchApi<User>("/iam/user-info");
|
||||||
|
setUserInfo(res);
|
||||||
|
}
|
||||||
|
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// 判断当前语言环境
|
// 判断当前语言环境
|
||||||
@ -200,6 +206,7 @@ const Setting = (props: { userInfo: any }) => {
|
|||||||
// 获取语言环境
|
// 获取语言环境
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getLanguage();
|
getLanguage();
|
||||||
|
getUserInfo()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -220,7 +227,7 @@ const Setting = (props: { userInfo: any }) => {
|
|||||||
<ScrollView style={styles.modalContent} showsVerticalScrollIndicator={false}>
|
<ScrollView style={styles.modalContent} showsVerticalScrollIndicator={false}>
|
||||||
{/* 用户信息 */}
|
{/* 用户信息 */}
|
||||||
<UserInfo
|
<UserInfo
|
||||||
userInfo={userInfo}
|
userInfo={userInfo || {} as User}
|
||||||
setCurrentLocation={setCurrentLocation}
|
setCurrentLocation={setCurrentLocation}
|
||||||
getCurrentLocation={getCurrentLocation}
|
getCurrentLocation={getCurrentLocation}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export default function Look(props: Props) {
|
|||||||
const { fileData, setFileData, isLoading, handleUser, avatar } = props;
|
const { fileData, setFileData, isLoading, handleUser, avatar } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-textPrimary justify-between p-[2rem]">
|
<View className="flex-1 bg-textPrimary justify-between p-[2rem]">
|
||||||
<View className="flex-1 justify-center items-center">
|
<View className="flex-1 justify-center items-center">
|
||||||
@ -32,11 +33,11 @@ export default function Look(props: Props) {
|
|||||||
{t('auth.userMessage.avatorText2', { ns: 'login' })}
|
{t('auth.userMessage.avatorText2', { ns: 'login' })}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
{
|
{
|
||||||
fileData[0]?.previewUrl
|
fileData[0]?.preview || fileData[0]?.previewUrl
|
||||||
?
|
?
|
||||||
<Image
|
<Image
|
||||||
className='rounded-full w-[10rem] h-[10rem]'
|
className='rounded-full w-[10rem] h-[10rem]'
|
||||||
source={{ uri: fileData[0].previewUrl }}
|
source={{ uri: fileData[0].preview || fileData[0].previewUrl }}
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
avatar
|
avatar
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import * as MediaLibrary from 'expo-media-library';
|
import * as MediaLibrary from 'expo-media-library';
|
||||||
|
|
||||||
export type ExtendedAsset = MediaLibrary.Asset & {
|
export type ExtendedAsset = MediaLibrary.Asset & {
|
||||||
size?: number;
|
size?: number;
|
||||||
exif?: Record<string, any> | null;
|
exif?: Record<string, any> | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 上传任务类型
|
// 上传任务类型
|
||||||
@ -18,92 +18,93 @@ export type UploadTask = {
|
|||||||
|
|
||||||
// 文件元数据信息
|
// 文件元数据信息
|
||||||
interface FileSize {
|
interface FileSize {
|
||||||
value: number;
|
value: number;
|
||||||
unit: string;
|
unit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FileMetadata {
|
interface FileMetadata {
|
||||||
originalName: string;
|
originalName: string;
|
||||||
type: string;
|
type: string;
|
||||||
isCompressed: string;
|
isCompressed: string;
|
||||||
fileType: string;
|
fileType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 后端返回的文件信息
|
// 后端返回的文件信息
|
||||||
interface FileInfo {
|
interface FileInfo {
|
||||||
file_id: number;
|
file_id: number;
|
||||||
name: string;
|
name: string;
|
||||||
size: FileSize;
|
size: FileSize;
|
||||||
content_type: string; // 这里与 ConfirmUpload 的 content_type 定义不同,需要注意
|
content_type: string; // 这里与 ConfirmUpload 的 content_type 定义不同,需要注意
|
||||||
upload_time: string;
|
upload_time: string;
|
||||||
storage_medium: string;
|
storage_medium: string;
|
||||||
file_path: string; // 这里与 ConfirmUpload 的 file_path 定义不同
|
file_path: string; // 这里与 ConfirmUpload 的 file_path 定义不同
|
||||||
uploader_id: number;
|
uploader_id: number;
|
||||||
upload_status: string;
|
upload_status: string;
|
||||||
deletion_status: string;
|
deletion_status: string;
|
||||||
metadata: FileMetadata;
|
metadata: FileMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传队列项 - 作为唯一的类型定义
|
// 上传队列项 - 作为唯一的类型定义
|
||||||
// 定义 EXIF 数据类型
|
// 定义 EXIF 数据类型
|
||||||
export type ExifData = {
|
export type ExifData = {
|
||||||
GPSLatitude?: number | undefined;
|
GPSLatitude?: number | undefined;
|
||||||
GPSLongitude?: number | undefined;
|
GPSLongitude?: number | undefined;
|
||||||
GPSAltitude?: number | undefined;
|
GPSAltitude?: number | undefined;
|
||||||
DateTimeOriginal?: string | undefined;
|
DateTimeOriginal?: string | undefined;
|
||||||
Make?: string | undefined;
|
Make?: string | undefined;
|
||||||
Model?: string | undefined;
|
Model?: string | undefined;
|
||||||
ExposureTime?: number | undefined;
|
ExposureTime?: number | undefined;
|
||||||
FNumber?: number | undefined;
|
FNumber?: number | undefined;
|
||||||
ISOSpeedRatings?: number | undefined;
|
ISOSpeedRatings?: number | undefined;
|
||||||
FocalLength?: number | undefined;
|
FocalLength?: number | undefined;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 默认的 EXIF 数据结构
|
// 默认的 EXIF 数据结构
|
||||||
export const defaultExifData: ExifData = {
|
export const defaultExifData: ExifData = {
|
||||||
GPSLatitude: undefined,
|
GPSLatitude: undefined,
|
||||||
GPSLongitude: undefined,
|
GPSLongitude: undefined,
|
||||||
GPSAltitude: undefined,
|
GPSAltitude: undefined,
|
||||||
DateTimeOriginal: undefined,
|
DateTimeOriginal: undefined,
|
||||||
Make: undefined,
|
Make: undefined,
|
||||||
Model: undefined,
|
Model: undefined,
|
||||||
ExposureTime: undefined,
|
ExposureTime: undefined,
|
||||||
FNumber: undefined,
|
FNumber: undefined,
|
||||||
ISOSpeedRatings: undefined,
|
ISOSpeedRatings: undefined,
|
||||||
FocalLength: undefined,
|
FocalLength: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 压缩图片可配置参数
|
// 压缩图片可配置参数
|
||||||
export interface ImagesuploaderProps {
|
export interface ImagesuploaderProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
style?: import('react-native').StyleProp<import('react-native').ViewStyle>;
|
style?: import('react-native').StyleProp<import('react-native').ViewStyle>;
|
||||||
onPickImage?: (file: File, exifData: ExifData) => void;
|
onPickImage?: (file: File, exifData: ExifData) => void;
|
||||||
compressQuality?: number;
|
compressQuality?: number;
|
||||||
maxWidth?: number;
|
maxWidth?: number;
|
||||||
maxHeight?: number;
|
maxHeight?: number;
|
||||||
preserveExif?: boolean;
|
preserveExif?: boolean;
|
||||||
uploadOriginal?: boolean;
|
uploadOriginal?: boolean;
|
||||||
onUploadComplete?: (result: FileUploadItem[]) => void;
|
onUploadComplete?: (result: FileUploadItem[]) => void;
|
||||||
onProgress?: (progress: any) => void; // TODO: Define a proper type for progress
|
onProgress?: (progress: any) => void; // TODO: Define a proper type for progress
|
||||||
multipleChoice?: boolean;
|
multipleChoice?: boolean;
|
||||||
fileType?: any[]; // TODO: Use MediaType from expo-image-picker
|
fileType?: any[]; // TODO: Use MediaType from expo-image-picker
|
||||||
showPreview?: boolean;
|
showPreview?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileUploadItem {
|
export interface FileUploadItem {
|
||||||
id: string;
|
id: string;
|
||||||
uri: string; // 用于本地展示的资源URI
|
uri: string; // 用于本地展示的资源URI
|
||||||
name: string;
|
name: string;
|
||||||
progress: number;
|
progress: number;
|
||||||
status: 'pending' | 'uploading' | 'success' | 'error'; // 统一状态
|
status: 'pending' | 'uploading' | 'success' | 'error'; // 统一状态
|
||||||
error?: string | null;
|
error?: string | null;
|
||||||
previewUrl: string; // 预览URL
|
previewUrl: string; // 预览URL
|
||||||
file?: File;
|
preview: string; // 预览URL
|
||||||
type: 'image' | 'video';
|
file?: File;
|
||||||
thumbnail?: string; // 缩略图URL
|
type: 'image' | 'video';
|
||||||
thumbnailFile?: File; // 缩略图文件对象
|
thumbnail?: string; // 缩略图URL
|
||||||
originalFile?: FileInfo // 上传后返回的文件信息
|
thumbnailFile?: File; // 缩略图文件对象
|
||||||
|
originalFile?: FileInfo // 上传后返回的文件信息
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确认上传返回
|
// 确认上传返回
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user