feat: 新增动画资源与庆祝效果,优化布局与标签页配置

This commit is contained in:
richarjiang
2025-09-03 15:03:26 +08:00
parent 8b6ef378d0
commit 951c02f644
14 changed files with 373 additions and 267 deletions

View File

@@ -3,12 +3,7 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
- **Start development server**: `npm start`
- **Run on Android**: `npm run android`
- **Run on iOS**: `npm run ios`
- **Run on Web**: `npm run web`
- **Lint**: `npm run lint`
- **Reset project**: `npm run reset-project`
## Architecture
- **Framework**: React Native (Expo) with TypeScript using Expo Router for file-based navigation

View File

@@ -1,7 +1,8 @@
import * as Haptics from 'expo-haptics';
import { Tabs, usePathname } from 'expo-router';
import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
import { Text, TouchableOpacity, View, ViewStyle } from 'react-native';
import type { BottomTabNavigationOptions } from '@react-navigation/bottom-tabs';
import { IconSymbol } from '@/components/ui/IconSymbol';
import { Colors } from '@/constants/Colors';
@@ -9,270 +10,138 @@ import { ROUTES } from '@/constants/Routes';
import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar';
import { useColorScheme } from '@/hooks/useColorScheme';
// Tab configuration
type TabConfig = {
icon: string;
title: string;
};
const TAB_CONFIGS: Record<string, TabConfig> = {
statistics: { icon: 'chart.pie.fill', title: '健康' },
explore: { icon: 'magnifyingglass.circle.fill', title: '发现' },
goals: { icon: 'flag.fill', title: '习惯' },
personal: { icon: 'person.fill', title: '个人' },
};
export default function TabLayout() {
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
const colorTokens = Colors[theme];
const pathname = usePathname();
// Helper function to determine if a tab is selected
const isTabSelected = (routeName: string): boolean => {
const routeMap: Record<string, string> = {
explore: ROUTES.TAB_EXPLORE,
goals: ROUTES.TAB_GOALS,
statistics: ROUTES.TAB_STATISTICS,
};
return routeMap[routeName] === pathname || pathname.includes(routeName);
};
// Custom tab button component
const createTabButton = (routeName: string) => (props: any) => {
const { onPress } = props;
const tabConfig = TAB_CONFIGS[routeName];
if (!tabConfig) return null;
const isSelected = isTabSelected(routeName);
const handlePress = (event: any) => {
if (process.env.EXPO_OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
onPress?.(event);
};
return (
<TouchableOpacity
onPress={handlePress}
accessibilityRole="button"
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
marginHorizontal: 6,
marginVertical: 10,
borderRadius: 25,
backgroundColor: isSelected ? colorTokens.tabBarActiveBackground : 'transparent',
paddingHorizontal: isSelected ? 16 : 10,
paddingVertical: 8,
}}
>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol
size={22}
name={tabConfig.icon as any}
color={isSelected ? colorTokens.tabIconSelected : colorTokens.tabIconDefault}
/>
{isSelected && (
<Text
style={{
color: colorTokens.tabIconSelected,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
}}
numberOfLines={0 as any}
>
{tabConfig.title}
</Text>
)}
</View>
</TouchableOpacity>
);
};
// Common screen options
const getScreenOptions = (routeName: string): BottomTabNavigationOptions => ({
headerShown: false,
tabBarActiveTintColor: colorTokens.tabIconSelected,
tabBarButton: createTabButton(routeName),
tabBarStyle: {
position: 'absolute',
bottom: TAB_BAR_BOTTOM_OFFSET,
height: TAB_BAR_HEIGHT,
borderRadius: 34,
backgroundColor: colorTokens.tabBarBackground,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 10,
elevation: 5,
paddingHorizontal: 10,
paddingTop: 0,
paddingBottom: 0,
marginHorizontal: 20,
left: 20,
right: 20,
alignSelf: 'center',
} as ViewStyle,
tabBarItemStyle: {
backgroundColor: 'transparent',
height: TAB_BAR_HEIGHT,
marginTop: 0,
marginBottom: 0,
paddingTop: 0,
paddingBottom: 0,
},
tabBarShowLabel: false,
});
return (
<Tabs
initialRouteName="statistics"
screenOptions={({ route }) => {
const routeName = route.name;
const isSelected = (routeName === 'explore' && pathname === ROUTES.TAB_EXPLORE) ||
(routeName === 'coach' && pathname === ROUTES.TAB_COACH) ||
(routeName === 'goals' && pathname === ROUTES.TAB_GOALS) ||
(routeName === 'statistics' && pathname === ROUTES.TAB_STATISTICS) ||
pathname.includes(routeName);
screenOptions={({ route }) => getScreenOptions(route.name)}
>
return {
headerShown: false,
tabBarActiveTintColor: colorTokens.tabIconSelected,
tabBarButton: (props) => {
const { onPress } = props;
const handlePress = (event: any) => {
if (process.env.EXPO_OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
onPress && onPress(event);
};
// 基于 routeName 设置图标与标题,避免 tabBarIcon 的包装导致文字裁剪
const getIconAndTitle = () => {
switch (routeName) {
case 'explore':
return { icon: 'magnifyingglass.circle.fill', title: '发现' } as const;
case 'coach':
return { icon: 'message.fill', title: 'AI' } as const;
case 'goals':
return { icon: 'flag.fill', title: '习惯' } as const;
case 'statistics':
return { icon: 'chart.pie.fill', title: '健康' } as const;
case 'personal':
return { icon: 'person.fill', title: '个人' } as const;
default:
return { icon: 'circle', title: '' } as const;
}
};
const { icon, title } = getIconAndTitle();
const activeContentColor = colorTokens.tabIconSelected; // 使用专门为Tab定义的选中颜色
const inactiveContentColor = colorTokens.tabIconDefault;
return (
<TouchableOpacity
onPress={handlePress}
accessibilityRole="button"
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
marginHorizontal: 6,
marginVertical: 10,
borderRadius: 25,
backgroundColor: isSelected ? colorTokens.tabBarActiveBackground : 'transparent',
paddingHorizontal: isSelected ? 16 : 10,
paddingVertical: 8,
}}
>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol
size={22}
name={icon as any}
color={isSelected ? activeContentColor : inactiveContentColor}
/>
{isSelected && !!title && (
<Text
style={{
color: activeContentColor,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
}}
// 选中态下不限制行数,避免大屏布局下被裁剪成省略号
numberOfLines={0 as any}
>
{title}
</Text>
)}
</View>
</TouchableOpacity>
);
},
tabBarStyle: {
position: 'absolute',
bottom: TAB_BAR_BOTTOM_OFFSET,
height: TAB_BAR_HEIGHT,
borderRadius: 34,
backgroundColor: colorTokens.tabBarBackground,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 10,
elevation: 5,
paddingHorizontal: 10,
paddingTop: 0,
paddingBottom: 0,
marginHorizontal: 20,
width: '90%',
alignSelf: 'center',
},
tabBarItemStyle: {
backgroundColor: 'transparent',
height: TAB_BAR_HEIGHT,
marginTop: 0,
marginBottom: 0,
paddingTop: 0,
paddingBottom: 0,
},
tabBarShowLabel: false,
};
}}>
<Tabs.Screen
name="statistics"
options={{
title: '统计',
tabBarIcon: ({ color }) => {
const isStatisticsSelected = pathname === '/statistics';
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol size={22} name="chart.pie.fill" color={color} />
{isStatisticsSelected && (
<Text
numberOfLines={1}
style={{
color: color,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
textAlign: 'center',
flexShrink: 0,
}}>
</Text>
)}
</View>
);
},
}}
/>
<Tabs.Screen
name="explore"
options={{
title: '发现',
href: null,
tabBarIcon: ({ color }) => {
const isHomeSelected = pathname === '/' || pathname === '/index';
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol size={22} name="magnifyingglass.circle.fill" color={color} />
{isHomeSelected && (
<Text
numberOfLines={1}
style={{
color: color,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
textAlign: 'center',
flexShrink: 0,
}}>
</Text>
)}
</View>
);
},
}}
/>
<Tabs.Screen
name="coach"
options={{
title: 'Seal',
tabBarIcon: ({ color }) => {
const isCoachSelected = pathname === '/coach';
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol size={22} name="person.3.fill" color={color} />
{isCoachSelected && (
<Text
numberOfLines={1}
style={{
color: color,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
textAlign: 'center',
flexShrink: 0,
}}>
Seal
</Text>
)}
</View>
);
},
}}
/>
<Tabs.Screen
name="goals"
options={{
title: '目标',
tabBarIcon: ({ color }) => {
const isGoalsSelected = pathname === '/goals';
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol size={22} name="flag.fill" color={color} />
{isGoalsSelected && (
<Text
numberOfLines={1}
style={{
color: color,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
textAlign: 'center',
flexShrink: 0,
}}>
</Text>
)}
</View>
);
},
}}
/>
<Tabs.Screen
name="personal"
options={{
title: '个人',
tabBarIcon: ({ color }) => {
const isPersonalSelected = pathname === '/personal';
return (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<IconSymbol size={22} name="person.fill" color={color} />
{isPersonalSelected && (
<Text
numberOfLines={1}
style={{
color: color,
fontSize: 12,
fontWeight: '600',
marginLeft: 6,
textAlign: 'center',
flexShrink: 0,
}}>
</Text>
)}
</View>
);
},
}}
/>
<Tabs.Screen name="statistics" options={{ title: '健康' }} />
<Tabs.Screen name="explore" options={{ title: '发现', href: null }} />
<Tabs.Screen name="coach" options={{ title: 'AI', href: null }} />
<Tabs.Screen name="goals" options={{ title: '习惯' }} />
<Tabs.Screen name="personal" options={{ title: '个人' }} />
</Tabs>
);
}

View File

@@ -1,3 +1,4 @@
import CelebrationAnimation, { CelebrationAnimationRef } from '@/components/CelebrationAnimation';
import GoalTemplateModal from '@/components/GoalTemplateModal';
import { GoalsPageGuide } from '@/components/GoalsPageGuide';
import { GuideTestButton } from '@/components/GuideTestButton';
@@ -20,8 +21,9 @@ import { GoalNotificationHelpers } from '@/utils/notificationHelpers';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { useFocusEffect } from '@react-navigation/native';
import dayjs from 'dayjs';
import * as Haptics from 'expo-haptics';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Alert, FlatList, Image, RefreshControl, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
export default function GoalsScreen() {
@@ -58,6 +60,9 @@ export default function GoalsScreen() {
const [showGuide, setShowGuide] = useState(false); // 控制引导显示
const [selectedTemplateData, setSelectedTemplateData] = useState<Partial<CreateGoalRequest> | undefined>();
// 庆祝动画引用
const celebrationAnimationRef = useRef<CelebrationAnimationRef>(null);
// 页面聚焦时重新加载数据
useFocusEffect(
useCallback(() => {
@@ -288,10 +293,24 @@ export default function GoalsScreen() {
}
};
// 处理任务完成
const handleTaskCompleted = (completedTask: TaskListItem) => {
// 触发震动反馈
if (process.env.EXPO_OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
}
// 播放庆祝动画
celebrationAnimationRef.current?.play();
console.log(`任务 "${completedTask.title}" 已完成,播放庆祝动画`);
};
// 渲染任务项
const renderTaskItem = ({ item }: { item: TaskListItem }) => (
<TaskCard
task={item}
onTaskCompleted={handleTaskCompleted}
/>
);
@@ -542,6 +561,12 @@ export default function GoalsScreen() {
}
}
},
{
text: '测试庆祝动画',
onPress: () => {
celebrationAnimationRef.current?.play();
}
},
]
);
}}
@@ -549,6 +574,9 @@ export default function GoalsScreen() {
<Text style={styles.testButtonText}></Text>
</TouchableOpacity>
)}
{/* 庆祝动画组件 */}
<CelebrationAnimation ref={celebrationAnimationRef} />
</View>
</SafeAreaView>
);

View File

@@ -33,6 +33,7 @@ import { debounce } from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
AppState,
Image,
SafeAreaView,
ScrollView,
StyleSheet,
@@ -565,10 +566,10 @@ export default function ExploreScreen() {
<View style={styles.container}>
{/* 背景渐变 */}
<LinearGradient
colors={['#F0F9FF', '#E0F2FE']}
colors={['#b9e4f5ff', '#b3f6f8ff', '#e6e5e8ff', '#F3F4F6']}
style={styles.gradientBackground}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
end={{ x: 0, y: 1 }}
/>
{/* 装饰性圆圈 */}
@@ -581,6 +582,23 @@ export default function ExploreScreen() {
contentContainerStyle={{ paddingBottom: bottomPadding }}
showsVerticalScrollIndicator={false}
>
{/* 顶部信息栏 */}
<View style={styles.headerContainer}>
<View style={styles.headerContent}>
{/* 左边logo */}
<Image
source={require('@/assets/images/Sealife.jpeg')}
style={styles.logoImage}
resizeMode="cover"
/>
{/* 右边文字区域 */}
<View style={styles.headerTextContainer}>
<Text style={styles.headerTitle}></Text>
</View>
</View>
</View>
<WeightHistoryCard />
{/* 日期选择器 */}
@@ -749,6 +767,28 @@ const styles = StyleSheet.create({
flex: 1,
paddingHorizontal: 20,
},
headerContainer: {
marginBottom: 20,
},
headerContent: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
logoImage: {
width: 36,
height: 36,
borderRadius: 20,
},
headerTextContainer: {
flex: 1,
marginLeft: 12,
},
headerTitle: {
fontSize: 16,
fontWeight: '500',
color: '#192126',
},
sectionTitle: {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,62 @@
import LottieView from 'lottie-react-native';
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import {
Dimensions,
StyleSheet,
View,
} from 'react-native';
export interface CelebrationAnimationRef {
play: () => void;
}
interface CelebrationAnimationProps {
visible?: boolean;
}
const CelebrationAnimation = forwardRef<CelebrationAnimationRef, CelebrationAnimationProps>(
({ visible = true }, ref) => {
const animationRef = useRef<LottieView>(null);
useImperativeHandle(ref, () => ({
play: () => {
animationRef.current?.play();
},
}));
if (!visible) return null;
return (
<View style={styles.container} pointerEvents="none">
<LottieView
ref={animationRef}
autoPlay={false}
loop={false}
source={require('@/assets/lottie/Confetti.json')}
style={styles.animation}
/>
</View>
);
}
);
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 9999,
alignItems: 'center',
justifyContent: 'center',
},
animation: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
});
CelebrationAnimation.displayName = 'CelebrationAnimation';
export default CelebrationAnimation;

View File

@@ -1,6 +1,7 @@
import { MoodCheckin, getMoodConfig } from '@/services/moodCheckins';
import dayjs from 'dayjs';
import React from 'react';
import LottieView from 'lottie-react-native';
import React, { useEffect, useRef } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
interface MoodCardProps {
@@ -11,10 +12,26 @@ interface MoodCardProps {
export function MoodCard({ moodCheckin, onPress, isLoading = false }: MoodCardProps) {
const moodConfig = moodCheckin ? getMoodConfig(moodCheckin.moodType) : null;
const animationRef = useRef<LottieView>(null);
useEffect(() => {
if (animationRef.current) {
animationRef.current.play();
}
}, []);
return (
<TouchableOpacity onPress={onPress} style={styles.moodCardContent} disabled={isLoading}>
<Text style={styles.cardTitle}></Text>
<View style={styles.moodCardHeader}>
<Text style={styles.cardTitle}></Text>
<LottieView
ref={animationRef}
source={require('@/assets/lottie/mood/mood_demo.json')}
autoPlay
loop
style={styles.lottieAnimation}
/>
</View>
{isLoading ? (
<View style={styles.moodPreview}>
<Text style={styles.moodLoadingText}>...</Text>
@@ -40,11 +57,22 @@ const styles = StyleSheet.create({
width: '100%',
},
moodCardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
cardTitle: {
fontSize: 14,
color: '#192126',
},
lottieAnimation: {
width: 40,
height: 40,
},
moodPreview: {
flexDirection: 'row',
justifyContent: 'space-between',

View File

@@ -10,10 +10,12 @@ import { Alert, Animated, Image, StyleSheet, Text, TouchableOpacity, View } from
interface TaskCardProps {
task: TaskListItem;
onTaskCompleted?: (task: TaskListItem) => void; // 任务完成回调
}
export const TaskCard: React.FC<TaskCardProps> = ({
task,
onTaskCompleted,
}) => {
const theme = useColorScheme() ?? 'light';
const colorTokens = Colors[theme];
@@ -98,6 +100,9 @@ export const TaskCard: React.FC<TaskCardProps> = ({
notes: '通过任务卡片完成'
}
})).unwrap();
// 触发任务完成回调
onTaskCompleted?.(task);
} catch (error) {
Alert.alert('错误', '完成任务失败,请重试');

View File

@@ -2,7 +2,8 @@ import { useWaterDataByDate } from '@/hooks/useWaterData';
import { getQuickWaterAmount } from '@/utils/userPreferences';
import dayjs from 'dayjs';
import * as Haptics from 'expo-haptics';
import React, { useEffect, useMemo, useState } from 'react';
import LottieView from 'lottie-react-native';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
Animated,
StyleSheet,
@@ -31,6 +32,8 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
const currentIntake = waterStats?.totalAmount || 0;
const targetIntake = dailyWaterGoal || 2000;
const animationRef = useRef<LottieView>(null);
// 为每个时间点创建独立的动画值
const animatedValues = useMemo(() =>
Array.from({ length: 24 }, () => new Animated.Value(0))
@@ -119,6 +122,8 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
}
animationRef.current?.play();
// 使用用户配置的快速添加饮水量
const waterAmount = quickWaterAmount;
// 如果有选中日期,则为该日期添加记录;否则为今天添加记录
@@ -156,6 +161,20 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
onPress={handleCardPress}
activeOpacity={0.8}
>
<LottieView
ref={animationRef}
autoPlay={false}
loop={false}
source={require('@/assets/lottie/Confetti.json')}
style={{
width: 150,
height: 150,
position: 'absolute',
left: '15%',
}}
/>
{/* 标题和加号按钮 */}
<View style={styles.header}>
<Text style={styles.title}></Text>
@@ -166,6 +185,7 @@ const WaterIntakeCard: React.FC<WaterIntakeCardProps> = ({
)}
</View>
{/* 柱状图 */}
<View style={styles.chartContainer}>
<View style={styles.chartWrapper}>

View File

@@ -122,6 +122,31 @@ PODS:
- libwebp/sharpyuv (1.5.0)
- libwebp/webp (1.5.0):
- libwebp/sharpyuv
- lottie-ios (4.5.0)
- lottie-react-native (7.3.4):
- DoubleConversion
- glog
- lottie-ios (= 4.5.0)
- RCT-Folly (= 2024.11.18.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsc
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- PurchasesHybridCommon (16.2.2):
- RevenueCat (= 5.34.0)
- QCloudCore (6.5.1):
@@ -1990,6 +2015,7 @@ DEPENDENCIES:
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- lottie-react-native (from `../node_modules/lottie-react-native`)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
@@ -2078,6 +2104,7 @@ SPEC REPOS:
- libavif
- libdav1d
- libwebp
- lottie-ios
- PurchasesHybridCommon
- QCloudCore
- QCloudCOSXML
@@ -2151,6 +2178,8 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
lottie-react-native:
:path: "../node_modules/lottie-react-native"
RCT-Folly:
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTDeprecation:
@@ -2346,6 +2375,8 @@ SPEC CHECKSUMS:
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
lottie-ios: a881093fab623c467d3bce374367755c272bdd59
lottie-react-native: 4969af5ac8f2ed2c562d35b6d3d9fe7d34a1add1
PurchasesHybridCommon: 62f852419aae7041792217593998f7ac3f8b567d
QCloudCore: 6f8c67b96448472d2c6a92b9cfe1bdb5abbb1798
QCloudCOSXML: 92f50a787b4e8d9a7cb6ea8e626775256b4840a7

View File

@@ -284,6 +284,8 @@
"${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
@@ -307,6 +309,8 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;

21
package-lock.json generated
View File

@@ -41,6 +41,7 @@
"expo-task-manager": "^13.1.6",
"expo-web-browser": "~14.2.0",
"lodash": "^4.17.21",
"lottie-react-native": "^7.3.4",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.5",
@@ -9703,6 +9704,26 @@
"loose-envify": "cli.js"
}
},
"node_modules/lottie-react-native": {
"version": "7.3.4",
"resolved": "https://mirrors.tencent.com/npm/lottie-react-native/-/lottie-react-native-7.3.4.tgz",
"integrity": "sha512-XUh7eGFb7ID8JRdU6U4N4cYQeYmjtdQRvd8ZXJ6xrdSsn5gZD0c79ITOREPcwJg4YupBFHgyV1GXdAHQP+KYUQ==",
"license": "Apache-2.0",
"peerDependencies": {
"@lottiefiles/dotlottie-react": "^0.13.5",
"react": "*",
"react-native": ">=0.46",
"react-native-windows": ">=0.63.x"
},
"peerDependenciesMeta": {
"@lottiefiles/dotlottie-react": {
"optional": true
},
"react-native-windows": {
"optional": true
}
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",

View File

@@ -45,6 +45,7 @@
"expo-task-manager": "^13.1.6",
"expo-web-browser": "~14.2.0",
"lodash": "^4.17.21",
"lottie-react-native": "^7.3.4",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.5",