feat: ts报错
This commit is contained in:
parent
a5f6c71d05
commit
aa03a6798a
@ -48,11 +48,6 @@ export default function AskScreen() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateState = (value: string) => {
|
||||
console.log('Received in JS:', value);
|
||||
// 更新 React 状态或路由
|
||||
router.replace(value);
|
||||
};
|
||||
// 右滑
|
||||
const gesture = Gesture.Pan()
|
||||
.onEnd((event) => {
|
||||
@ -61,7 +56,7 @@ export default function AskScreen() {
|
||||
|
||||
if (translationX > threshold) {
|
||||
// 从左向右滑动,跳转页面
|
||||
runOnJS(router.replace)("memo-list");
|
||||
runOnJS(router.replace)("/memo-list");
|
||||
}
|
||||
})
|
||||
.minPointers(1)
|
||||
|
||||
@ -143,7 +143,7 @@ export default function Rights() {
|
||||
}
|
||||
setConfirmLoading(true);
|
||||
const history = await fetchPurchaseHistory()
|
||||
const historyIds = history?.filter((item: any) => isOrderExpired(item?.expirationDateIos))?.map((i) => { return i?.id })
|
||||
const historyIds = history?.filter((item: any) => isOrderExpired(item?.expirationDateIos))?.map((i: any) => { return i?.id })
|
||||
if (historyIds?.includes(payType)) {
|
||||
setConfirmLoading(false);
|
||||
setTimeout(() => {
|
||||
|
||||
@ -1,221 +0,0 @@
|
||||
'use client';
|
||||
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
||||
import {
|
||||
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,
|
||||
conversationId: string | null,
|
||||
setUserMessages: Dispatch<SetStateAction<Message[]>>;
|
||||
setConversationId: (conversationId: string) => void,
|
||||
}
|
||||
export default function AudioRecordPlay(props: 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 => {
|
||||
const totalSeconds = ms / 1000;
|
||||
const minutes = Math.floor(totalSeconds / 60);
|
||||
const seconds = Math.floor(totalSeconds % 60);
|
||||
const milliseconds = Math.floor(ms % 1000);
|
||||
|
||||
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}`;
|
||||
};
|
||||
// 开始录音
|
||||
const record = async () => {
|
||||
await audioRecorder.prepareToRecordAsync();
|
||||
const startTime = Date.now();
|
||||
|
||||
// 每 10ms 更新一次时间
|
||||
const interval = setInterval(() => {
|
||||
const elapsed = Date.now() - startTime;
|
||||
setElapsedTime(elapsed);
|
||||
}, 10);
|
||||
|
||||
setTimerInterval(interval);
|
||||
setIsVoiceStart(true)
|
||||
audioRecorder.record();
|
||||
setIsRecording(true);
|
||||
};
|
||||
|
||||
const stopRecording = async () => {
|
||||
// The recording will be available on `audioRecorder.uri`.
|
||||
|
||||
|
||||
if (timerInterval) clearInterval(timerInterval);
|
||||
setTimerInterval(0);
|
||||
await audioRecorder.stop();
|
||||
setIsRecording(false);
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// (async () => {
|
||||
// const status = await AudioModule.requestRecordingPermissionsAsync();
|
||||
// if (!status.granted) {
|
||||
// Alert.alert('Permission to access microphone was denied');
|
||||
// }
|
||||
// })();
|
||||
// }, []);
|
||||
// 获取对话信息
|
||||
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 = () => {
|
||||
const text = inputValue;
|
||||
if (text) {
|
||||
setUserMessages(pre => ([...pre, {
|
||||
content: {
|
||||
text: text
|
||||
},
|
||||
role: 'User',
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
]));
|
||||
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">
|
||||
{/* <TouchableOpacity
|
||||
onPress={() => console.log('Left icon pressed')}
|
||||
className={`absolute left-2 top-1/2 -translate-y-1/2 p-2 bg-white rounded-full ${isVoiceStart ? "opacity-100" : "opacity-0"}`} // 使用绝对定位将按钮放在输入框内右侧
|
||||
>
|
||||
<VoiceDeleteSvg />
|
||||
</TouchableOpacity> */}
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Ask MeMo Anything..."
|
||||
placeholderTextColor="#999"
|
||||
className={isVoiceStart ? 'bg-bgPrimary border-none pl-12' : ''}
|
||||
value={isVoiceStart ? `· · · · · · · · · · · · · · ${formatTime(elapsedTime)}` : inputValue}
|
||||
onChangeText={(text: string) => {
|
||||
setInputValue(text);
|
||||
}}
|
||||
onSubmitEditing={handleSubmit}
|
||||
editable={!isVoiceStart}
|
||||
// 调起的键盘类型
|
||||
returnKeyType="send"
|
||||
/>
|
||||
{/* <TouchableOpacity
|
||||
style={styles.voiceButton}
|
||||
className={`absolute right-0 top-1/2 -translate-y-1/2 ${isVoiceStart ? 'bg-white px-8' : 'bg-bgPrimary'}`} // 使用绝对定位将按钮放在输入框内右侧
|
||||
onPress={isVoiceStart ? stopRecording : record}
|
||||
>
|
||||
{isVoiceStart ? <VoiceSendSvg /> : <VoiceSvg />}
|
||||
</TouchableOpacity> */}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 20,
|
||||
textAlign: 'center',
|
||||
},
|
||||
recordButton: {
|
||||
padding: 15,
|
||||
borderRadius: 8,
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
startButton: {
|
||||
backgroundColor: '#ff6b6b',
|
||||
},
|
||||
stopButton: {
|
||||
backgroundColor: '#4CAF50',
|
||||
},
|
||||
buttonText: {
|
||||
color: 'white',
|
||||
fontSize: 16,
|
||||
},
|
||||
listTitle: {
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 10,
|
||||
},
|
||||
emptyText: {
|
||||
fontStyle: 'italic',
|
||||
color: '#888',
|
||||
marginBottom: 10,
|
||||
},
|
||||
recordingItem: {
|
||||
padding: 10,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#eee',
|
||||
},
|
||||
uriText: {
|
||||
fontSize: 12,
|
||||
color: '#777',
|
||||
},
|
||||
leftIcon: {
|
||||
padding: 10,
|
||||
paddingLeft: 15,
|
||||
},
|
||||
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,14 +1,14 @@
|
||||
import { requestLocationPermission, requestMediaLibraryPermission } from '@/components/owner/utils';
|
||||
import { addMaterial, confirmUpload, getUploadUrl } from '@/lib/background-uploader/api';
|
||||
import { ConfirmUpload, ExifData, FileUploadItem, ImagesuploaderProps, UploadResult, UploadTask, defaultExifData } from '@/lib/background-uploader/types';
|
||||
import { uploadFileWithProgress } from '@/lib/background-uploader/uploader';
|
||||
import { compressImage } from '@/lib/image-process/imageCompress';
|
||||
import { PermissionService } from '@/lib/PermissionService';
|
||||
import { createVideoThumbnailFile } from '@/lib/video-process/videoThumbnail';
|
||||
import * as ImagePicker from 'expo-image-picker';
|
||||
import { requestLocationPermission, requestMediaLibraryPermission } from '@/components/owner/utils';
|
||||
import { PermissionService } from '@/lib/PermissionService';
|
||||
import * as MediaLibrary from 'expo-media-library';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Alert, Button, Platform, TouchableOpacity, View } from 'react-native';
|
||||
import { Button, Platform, TouchableOpacity, View } from 'react-native';
|
||||
import UploadPreview from './preview';
|
||||
|
||||
export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
||||
@ -48,6 +48,7 @@ export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
||||
id: fileId,
|
||||
uri: asset.uri,
|
||||
previewUrl: asset.uri, // 使用 asset.uri 作为初始预览
|
||||
preview: asset.uri, // 使用 asset.uri 作为初始预览
|
||||
name: asset.fileName || 'file',
|
||||
progress: 0,
|
||||
status: 'uploading',
|
||||
@ -221,7 +222,7 @@ export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
||||
const batchResults = await Promise.allSettled(
|
||||
batch.map(asset => processSingleAsset(asset))
|
||||
);
|
||||
|
||||
|
||||
// 收集成功的结果
|
||||
for (const result of batchResults) {
|
||||
if (result.status === 'fulfilled' && result.value) {
|
||||
@ -235,7 +236,7 @@ export const ImagesUploader: React.FC<ImagesuploaderProps> = ({
|
||||
for (let i = 0; i < assets.length; i += CONCURRENCY_LIMIT) {
|
||||
batches.push(assets.slice(i, i + CONCURRENCY_LIMIT));
|
||||
}
|
||||
|
||||
|
||||
// 并行处理所有批次,但限制并发数量
|
||||
for (let i = 0; i < batches.length; i += CONCURRENCY_LIMIT) {
|
||||
const batchGroup = batches.slice(i, i + CONCURRENCY_LIMIT);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { fetchApi } from '@/lib/server-api-util';
|
||||
import { ConfirmUpload, UploadUrlResponse } from '@/types/upload';
|
||||
import * as SecureStore from 'expo-secure-store';
|
||||
|
||||
const QUEUE_KEY = 'uploadQueue';
|
||||
@ -34,14 +36,14 @@ export const uploadMediaFile = async (asset: any) => {
|
||||
: `video/${filename.split('.').pop()}`;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', { uri, name: filename, type } as any);
|
||||
formData.append('file', { uri, name: filename, type } as unknown as File);
|
||||
|
||||
await getUploadUrl({
|
||||
...formData,
|
||||
name: filename,
|
||||
type,
|
||||
size: asset.fileSize
|
||||
}, {}).then((res) => {
|
||||
} as unknown as File, {}).then((res) => {
|
||||
confirmUpload(res.file_id).then((confirmRes) => {
|
||||
addMaterial(res.file_id, confirmRes.file_id)
|
||||
}).catch((error) => {
|
||||
|
||||
@ -47,7 +47,7 @@ export const Fireworks: React.FC<FireworksProps> = ({
|
||||
const [particles, setParticles] = useState<Particle[]>([]);
|
||||
const [isPlaying, setIsPlaying] = useState(autoPlay);
|
||||
const particleId = useRef(0);
|
||||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
// 生成随机位置
|
||||
const getRandomPosition = () => {
|
||||
@ -112,7 +112,7 @@ export const Fireworks: React.FC<FireworksProps> = ({
|
||||
]),
|
||||
// 旋转效果
|
||||
Animated.timing(rotation, {
|
||||
toValue: rotation._value + 360,
|
||||
toValue: (rotation as Animated.Value & { _value: number })._value + 360,
|
||||
duration: 2000,
|
||||
easing: Easing.linear,
|
||||
useNativeDriver: true,
|
||||
|
||||
@ -52,7 +52,8 @@ const Login = ({ updateUrlParam, setError, setShowPassword, showPassword }: Logi
|
||||
router.replace('/user-message');
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error.message || t('auth.login.loginError', { ns: 'login' }));
|
||||
const errorMessage = error instanceof Error ? error.message : t('auth.login.loginError', { ns: 'login' });
|
||||
setError(errorMessage);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// welcome.tsx (Web 版本)
|
||||
// 在 Web 端不显示任何内容
|
||||
import { StyleProp, ViewStyle } from "react-native";
|
||||
|
||||
// 占位符 移动端实际引入文件是 welcome.native.tsx 文件
|
||||
export default function WebLottie(props: { source: string }) {
|
||||
export default function WebLottie(props: { source: string, style?: StyleProp<ViewStyle>, loop?: boolean }) {
|
||||
return null;
|
||||
}
|
||||
@ -142,11 +142,6 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: '#4C320C',
|
||||
},
|
||||
closeButton: {
|
||||
fontSize: 28,
|
||||
color: '#4C320C',
|
||||
|
||||
@ -387,7 +387,7 @@ const SettingModal = (props: { modalVisible: boolean, setModalVisible: (visible:
|
||||
|
||||
<PrivacyModal modalVisible={privacyModalVisible} setModalVisible={setPrivacyModalVisible} type={modalType} />
|
||||
<LcensesModal modalVisible={lcensesModalVisible} setModalVisible={setLcensesModalVisible} />
|
||||
<DeleteModal modalVisible={deleteModalVisible} setModalVisible={setDeleteModalVisible} setSettingModalVisible={setModalVisible} />
|
||||
<DeleteModal modalVisible={deleteModalVisible} setModalVisible={setDeleteModalVisible} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -25,7 +25,7 @@ export const reverseGeocode = async (latitude: number, longitude: number): Promi
|
||||
try {
|
||||
const addressResults = await fetchApi<Address[]>(`/area/gecoding?latitude=${latitude}&longitude=${longitude}`);
|
||||
if (Object.keys(addressResults).length === 0) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
console.log('地址1:', addressResults);
|
||||
|
||||
@ -34,7 +34,7 @@ export const reverseGeocode = async (latitude: number, longitude: number): Promi
|
||||
} else {
|
||||
SecureStore.setItemAsync('location', JSON.stringify(addressResults));
|
||||
}
|
||||
return addressResults
|
||||
return addressResults as unknown as Address;
|
||||
} catch (error) {
|
||||
console.log('逆地理编码失败:', error);
|
||||
return undefined;
|
||||
|
||||
@ -4,29 +4,31 @@ interface RawData {
|
||||
location?: {
|
||||
latitude?: string | number;
|
||||
};
|
||||
[key: string]: any; // Allow any additional properties
|
||||
}
|
||||
|
||||
export function transformData(data: RawData): RawData {
|
||||
export function transformData(data: RawData): Omit<RawData, 'exif'> {
|
||||
const result = { ...data };
|
||||
|
||||
if (result.exif) {
|
||||
const newExif: Record<string, any> = {};
|
||||
|
||||
for (const key in result.exif) {
|
||||
const value = result.exif[key];
|
||||
const value: unknown = result.exif[key];
|
||||
|
||||
// 普通对象:{Exif}, {TIFF}, {XMP} 等
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||
const obj = value as Record<string, any>;
|
||||
if (key === '{GPS}') {
|
||||
// 处理 GPS 字段:所有子字段加前缀 "GPS"
|
||||
for (const subKey in value) {
|
||||
for (const subKey in obj) {
|
||||
const newKey = 'GPS' + subKey; // 所有字段都加前缀
|
||||
newExif[newKey] = value[subKey];
|
||||
newExif[newKey] = obj[subKey];
|
||||
}
|
||||
} else {
|
||||
// 其它嵌套对象直接展开字段
|
||||
for (const subKey in value) {
|
||||
newExif[subKey] = value[subKey];
|
||||
for (const subKey in obj) {
|
||||
newExif[subKey] = obj[subKey];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -35,8 +37,12 @@ export function transformData(data: RawData): RawData {
|
||||
}
|
||||
}
|
||||
|
||||
result.exif = newExif;
|
||||
// 合并展开的 exif 数据并移除 exif 属性
|
||||
const { exif, ...rest } = result;
|
||||
return { ...rest, ...newExif };
|
||||
}
|
||||
// 最后将result的exif信息平铺
|
||||
return { ...result, ...result.exif, exif: undefined };
|
||||
|
||||
// 如果没有 exif 数据,直接返回原数据(排除 exif 属性)
|
||||
const { exif, ...rest } = result;
|
||||
return rest;
|
||||
}
|
||||
109
types/works.ts
109
types/works.ts
@ -1,109 +0,0 @@
|
||||
import { MaterialItemWithMetadata } from "@/components/waterfall";
|
||||
|
||||
// 模板列表 list
|
||||
export interface NodeTemplates {
|
||||
id: string;
|
||||
node_template_id: string
|
||||
name: string;
|
||||
description: string;
|
||||
model: string;
|
||||
prompt: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
current_version_id: number;
|
||||
version: number;
|
||||
is_current_version: boolean
|
||||
}
|
||||
|
||||
//模板详情
|
||||
export interface NodeTemplateDetail extends NodeTemplates {
|
||||
material_infos: MaterialItemWithMetadata[]
|
||||
example_material_ids: string[]
|
||||
available_model_names: string[]
|
||||
}
|
||||
|
||||
// 更新模板
|
||||
export interface UpdateNodeTemplate {
|
||||
example_material_ids: string[];
|
||||
}
|
||||
|
||||
export interface Shot {
|
||||
camera_direction: string;
|
||||
camera_movement: string;
|
||||
clip_id: number;
|
||||
composition: string;
|
||||
music_description: string;
|
||||
perspective: string;
|
||||
scene_name: string;
|
||||
scene_sequence: number;
|
||||
shot_description: string;
|
||||
shot_duration: string;
|
||||
shot_name: string;
|
||||
shot_sequence: number;
|
||||
shot_sizes: string;
|
||||
sound_effect: string;
|
||||
transition_in: string;
|
||||
voice_over: string;
|
||||
}
|
||||
// 图片的debug
|
||||
interface CaptionResult {
|
||||
video_shots?: Shot[];
|
||||
caption_result: {
|
||||
common: {
|
||||
background: string;
|
||||
};
|
||||
daily_life: {
|
||||
activity: string;
|
||||
atmosphere: string;
|
||||
extra_elements: string;
|
||||
people_presence: string;
|
||||
scene: string;
|
||||
};
|
||||
person: {
|
||||
person_count: string;
|
||||
person_details: Array<{
|
||||
action: string;
|
||||
age: string;
|
||||
expression: string;
|
||||
gender: string;
|
||||
mood: string;
|
||||
}>;
|
||||
person_identity: string;
|
||||
scene: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export interface PhotosDebug {
|
||||
id: string,
|
||||
state: string,
|
||||
name: string,
|
||||
context: {
|
||||
input: Object,
|
||||
outputs: CaptionResult,
|
||||
metadata: Object,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// prompt需要传参
|
||||
export interface PromptParams {
|
||||
name: string,
|
||||
value_type: string,
|
||||
required: boolean,
|
||||
description: string
|
||||
}
|
||||
|
||||
// input output
|
||||
export interface InputOutput {
|
||||
is_async: boolean,
|
||||
name: string,
|
||||
context: {
|
||||
metadata: {},
|
||||
outputs: { raw_output?: JSON, metadata?: {} },
|
||||
inputs: {
|
||||
material_id: string,
|
||||
prompt: string
|
||||
model_name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user