feat: 兼容

This commit is contained in:
jinyaqiu 2025-07-17 19:29:06 +08:00
parent 39b768f2cc
commit 436b44a107
4 changed files with 59 additions and 51 deletions

View File

@ -1,3 +1,4 @@
import IP from '@/assets/icons/svg/ip.svg';
import * as MediaLibrary from 'expo-media-library'; import * as MediaLibrary from 'expo-media-library';
import { useRouter } from 'expo-router'; import { useRouter } from 'expo-router';
import * as SecureStore from 'expo-secure-store'; import * as SecureStore from 'expo-secure-store';
@ -96,7 +97,7 @@ export default function HomeScreen() {
isLoggedIn ? <MemoList /> : isLoggedIn ? <MemoList /> :
<View className="flex-1 bg-bgPrimary px-[1rem] h-screen overflow-auto py-[2rem] " style={{ paddingTop: insets.top + 48 }}> <View className="flex-1 bg-bgPrimary px-[1rem] h-screen overflow-auto py-[2rem] " style={{ paddingTop: insets.top + 48 }}>
{/* 标题区域 */} {/* 标题区域 */}
<View className="items-center mb-10 w-full px-5"> <View className="mb-10 w-full px-5">
<Text className="text-white text-3xl font-bold mb-3 text-left"> <Text className="text-white text-3xl font-bold mb-3 text-left">
{t('auth.welcomeAwaken.awaken', { ns: 'login' })} {t('auth.welcomeAwaken.awaken', { ns: 'login' })}
{"\n"} {"\n"}
@ -110,9 +111,9 @@ export default function HomeScreen() {
</View> </View>
{/* Memo 形象区域 */} {/* Memo 形象区域 */}
{/* <View className="items-center"> <View className="items-center">
<IP /> <IP />
</View> */} </View>
{/* 介绍文本 */} {/* 介绍文本 */}
<Text className="text-white text-base text-center mb-[1rem] leading-6 opacity-90 px-10 -mt-[4rem]"> <Text className="text-white text-base text-center mb-[1rem] leading-6 opacity-90 px-10 -mt-[4rem]">

View File

@ -0,0 +1,45 @@
import { useVideoPlayer, VideoView } from 'expo-video';
import {
Pressable,
StyleProp,
ViewStyle
} from 'react-native';
const VideoPlayer = ({
videoUrl,
style,
onPress
}: {
videoUrl: string;
style?: StyleProp<ViewStyle>;
onPress?: () => void;
}) => {
const player = useVideoPlayer(videoUrl, (player) => {
player.loop = true;
player.play();
});
return (
<Pressable
style={[{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}, style]}
onPress={onPress}
>
<VideoView
style={{
width: '100%',
height: '100%',
backgroundColor: '#000', // 添加背景色
}}
player={player}
allowsFullscreen
allowsPictureInPicture
/>
</Pressable>
);
};
export default VideoPlayer

View File

@ -5,24 +5,22 @@ import ReturnArrow from "@/assets/icons/svg/returnArrow.svg";
import YesSvg from "@/assets/icons/svg/yes.svg"; import YesSvg from "@/assets/icons/svg/yes.svg";
import { Message, Video } from "@/types/ask"; import { Message, Video } from "@/types/ask";
import { MaterialItem } from "@/types/personal-info"; import { MaterialItem } from "@/types/personal-info";
import { useVideoPlayer, VideoView } from 'expo-video'; import { TFunction } from "i18next";
import React from 'react'; import React from 'react';
import { useTranslation } from "react-i18next";
import { import {
FlatList, FlatList,
Image, Image,
Modal, Modal,
Pressable, Pressable,
StyleProp,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,
View, View
ViewStyle
} from 'react-native'; } from 'react-native';
import { ThemedText } from "../ThemedText"; import { ThemedText } from "../ThemedText";
import TypewriterText from "./typewriterText"; import TypewriterText from "./typewriterText";
import { mergeArrays } from "./utils"; import { mergeArrays } from "./utils";
import VideoPlayer from "./VideoPlayer";
interface RenderMessageProps { interface RenderMessageProps {
insets: { top: number }; insets: { top: number };
@ -34,51 +32,14 @@ interface RenderMessageProps {
modalDetailsVisible: boolean; modalDetailsVisible: boolean;
setSelectedImages: React.Dispatch<React.SetStateAction<string[]>>; setSelectedImages: React.Dispatch<React.SetStateAction<string[]>>;
selectedImages: string[]; selectedImages: string[];
t: TFunction;
} }
const renderMessage = ({ insets, item, sessionId, setModalVisible, modalVisible, setModalDetailsVisible, modalDetailsVisible, setSelectedImages, selectedImages }: RenderMessageProps) => { const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible, setModalDetailsVisible, modalDetailsVisible, setSelectedImages, selectedImages }: RenderMessageProps) => {
const isUser = item.role === 'User'; const isUser = item.role === 'User';
const isVideo = (data: Video | MaterialItem): data is Video => { const isVideo = (data: Video | MaterialItem): data is Video => {
return 'video' in data; return 'video' in data;
}; };
const { t } = useTranslation();
// 创建一个新的 VideoPlayer 组件
const VideoPlayer = ({
videoUrl,
style,
onPress
}: {
videoUrl: string;
style?: StyleProp<ViewStyle>;
onPress?: () => void;
}) => {
const player = useVideoPlayer(videoUrl, (player) => {
player.loop = true;
player.play();
});
return (
<Pressable
style={[{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}, style]}
onPress={onPress}
>
<VideoView
style={{
width: '100%',
height: '100%',
backgroundColor: '#000', // 添加背景色
}}
player={player}
allowsFullscreen
allowsPictureInPicture
/>
</Pressable>
);
};
return ( return (
<View className={`flex-row items-start gap-2 w-full ${isUser ? 'justify-end' : 'justify-start'}`}> <View className={`flex-row items-start gap-2 w-full ${isUser ? 'justify-end' : 'justify-start'}`}>
@ -304,7 +265,7 @@ const renderMessage = ({ insets, item, sessionId, setModalVisible, modalVisible,
); );
}; };
export default renderMessage; export default MessageItem;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
imageGridContainer: { imageGridContainer: {

View File

@ -1,12 +1,13 @@
import { Message, Video } from '@/types/ask'; import { Message, Video } from '@/types/ask';
import { MaterialItem } from '@/types/personal-info'; import { MaterialItem } from '@/types/personal-info';
import React, { Dispatch, memo, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { Dispatch, memo, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { import {
FlatList, FlatList,
SafeAreaView SafeAreaView
} from 'react-native'; } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import renderMessage from "./aiChat"; import MessageItem from './aiChat';
interface ChatProps { interface ChatProps {
userMessages: Message[]; userMessages: Message[];
@ -19,7 +20,7 @@ function ChatComponent({ userMessages, sessionId, setSelectedImages, selectedIma
const flatListRef = useRef<FlatList>(null); const flatListRef = useRef<FlatList>(null);
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const [modalVisible, setModalVisible] = React.useState({ visible: false, data: {} as Video | MaterialItem }); const [modalVisible, setModalVisible] = React.useState({ visible: false, data: {} as Video | MaterialItem });
const { t } = useTranslation();
// 使用 useCallback 缓存 keyExtractor 函数 // 使用 useCallback 缓存 keyExtractor 函数
const keyExtractor = useCallback((item: Message) => `${item.role}-${item.timestamp}`, []); const keyExtractor = useCallback((item: Message) => `${item.role}-${item.timestamp}`, []);
@ -51,7 +52,7 @@ function ChatComponent({ userMessages, sessionId, setSelectedImages, selectedIma
updateCellsBatchingPeriod={50} updateCellsBatchingPeriod={50}
initialNumToRender={10} initialNumToRender={10}
windowSize={11} windowSize={11}
renderItem={({ item }) => renderMessage({ setSelectedImages, selectedImages, insets, item, sessionId, modalVisible, setModalVisible, setModalDetailsVisible, modalDetailsVisible })} renderItem={({ item }) => MessageItem({ t, setSelectedImages, selectedImages, insets, item, sessionId, modalVisible, setModalVisible, setModalDetailsVisible, modalDetailsVisible })}
/> />
</SafeAreaView> </SafeAreaView>
); );