feat: 添加相机和相册权限请求功能
- 在 AI 体态评估页面中集成相机和相册权限请求逻辑 - 更新 app.json 和 Info.plist,添加相应的权限说明 - 修改布局以支持照片上传功能,用户可上传正面、侧面和背面照片 - 更新 package.json 和 package-lock.json,添加 expo-image-picker 依赖
This commit is contained in:
5
app.json
5
app.json
@@ -12,7 +12,10 @@
|
|||||||
"supportsTablet": true,
|
"supportsTablet": true,
|
||||||
"bundleIdentifier": "com.anonymous.digitalpilates",
|
"bundleIdentifier": "com.anonymous.digitalpilates",
|
||||||
"infoPlist": {
|
"infoPlist": {
|
||||||
"ITSAppUsesNonExemptEncryption": false
|
"ITSAppUsesNonExemptEncryption": false,
|
||||||
|
"NSCameraUsageDescription": "应用需要使用相机以拍摄您的体态照片用于AI测评。",
|
||||||
|
"NSPhotoLibraryUsageDescription": "应用需要访问相册以选择您的体态照片用于AI测评。",
|
||||||
|
"NSPhotoLibraryAddUsageDescription": "应用需要写入相册以保存拍摄的体态照片(可选)。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ import React from 'react';
|
|||||||
import { Text, TouchableOpacity, View } from 'react-native';
|
import { Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
|
||||||
import { IconSymbol } from '@/components/ui/IconSymbol';
|
import { IconSymbol } from '@/components/ui/IconSymbol';
|
||||||
|
import { Colors } from '@/constants/Colors';
|
||||||
import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar';
|
import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar';
|
||||||
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
export default function TabLayout() {
|
export default function TabLayout() {
|
||||||
|
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
||||||
|
const colorTokens = Colors[theme];
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -19,9 +23,9 @@ export default function TabLayout() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarActiveTintColor: '#192126',
|
tabBarActiveTintColor: colorTokens.tabIconSelected,
|
||||||
tabBarButton: (props) => {
|
tabBarButton: (props) => {
|
||||||
const { children, onPress } = props;
|
const { onPress } = props;
|
||||||
|
|
||||||
const handlePress = (event: any) => {
|
const handlePress = (event: any) => {
|
||||||
if (process.env.EXPO_OS === 'ios') {
|
if (process.env.EXPO_OS === 'ios') {
|
||||||
@@ -30,9 +34,28 @@ export default function TabLayout() {
|
|||||||
onPress && onPress(event);
|
onPress && onPress(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 基于 routeName 设置图标与标题,避免 tabBarIcon 的包装导致文字裁剪
|
||||||
|
const getIconAndTitle = () => {
|
||||||
|
switch (routeName) {
|
||||||
|
case 'index':
|
||||||
|
return { icon: 'house.fill', title: '首页' } as const;
|
||||||
|
case 'explore':
|
||||||
|
return { icon: 'paperplane.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.onPrimary;
|
||||||
|
const inactiveContentColor = colorTokens.tabIconDefault;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handlePress}
|
onPress={handlePress}
|
||||||
|
accessibilityRole="button"
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -41,12 +64,32 @@ export default function TabLayout() {
|
|||||||
marginHorizontal: 6,
|
marginHorizontal: 6,
|
||||||
marginVertical: 10,
|
marginVertical: 10,
|
||||||
borderRadius: 25,
|
borderRadius: 25,
|
||||||
backgroundColor: isSelected ? '#BBF246' : 'transparent',
|
backgroundColor: isSelected ? colorTokens.tabBarActiveBackground : 'transparent',
|
||||||
paddingHorizontal: isSelected ? 16 : 8,
|
paddingHorizontal: isSelected ? 16 : 10,
|
||||||
paddingVertical: 8,
|
paddingVertical: 8,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
<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>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -55,7 +98,7 @@ export default function TabLayout() {
|
|||||||
bottom: TAB_BAR_BOTTOM_OFFSET,
|
bottom: TAB_BAR_BOTTOM_OFFSET,
|
||||||
height: TAB_BAR_HEIGHT,
|
height: TAB_BAR_HEIGHT,
|
||||||
borderRadius: 34,
|
borderRadius: 34,
|
||||||
backgroundColor: '#192126',
|
backgroundColor: colorTokens.tabBarBackground,
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOffset: { width: 0, height: 2 },
|
shadowOffset: { width: 0, height: 2 },
|
||||||
shadowOpacity: 0.2,
|
shadowOpacity: 0.2,
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import { BlurView } from 'expo-blur';
|
||||||
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import React from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
Image,
|
Image,
|
||||||
ImageBackground,
|
Linking,
|
||||||
|
Platform,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
@@ -14,49 +18,157 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|||||||
|
|
||||||
import { Colors } from '@/constants/Colors';
|
import { Colors } from '@/constants/Colors';
|
||||||
|
|
||||||
type Exercise = {
|
type PoseView = 'front' | 'side' | 'back';
|
||||||
id: string;
|
|
||||||
title: string;
|
type UploadState = {
|
||||||
duration: string;
|
front?: string | null;
|
||||||
imageUri: string;
|
side?: string | null;
|
||||||
|
back?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const EXERCISES: Exercise[] = [
|
type Sample = { uri: string; correct: boolean };
|
||||||
{
|
|
||||||
id: '1',
|
const SAMPLES: Record<PoseView, Sample[]> = {
|
||||||
title: 'Jumping Jacks',
|
front: [
|
||||||
duration: '00:30',
|
{ uri: 'https://images.unsplash.com/photo-1594737625785-c6683fc87c73?w=400&q=80&auto=format', correct: true },
|
||||||
imageUri:
|
{ uri: 'https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?w=400&q=80&auto=format', correct: false },
|
||||||
'https://images.unsplash.com/photo-1546483875-ad9014c88eba?q=80&w=400&auto=format&fit=crop',
|
{ uri: 'https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?w=400&q=80&auto=format', correct: false },
|
||||||
},
|
],
|
||||||
{
|
side: [
|
||||||
id: '2',
|
{ uri: 'https://images.unsplash.com/photo-1554463529-e27854014799?w=400&q=80&auto=format', correct: true },
|
||||||
title: 'Squats',
|
{ uri: 'https://images.unsplash.com/photo-1596357395104-5bcae0b1a5eb?w=400&q=80&auto=format', correct: false },
|
||||||
duration: '00:45',
|
{ uri: 'https://images.unsplash.com/photo-1526506118085-60ce8714f8c5?w=400&q=80&auto=format', correct: false },
|
||||||
imageUri:
|
],
|
||||||
'https://images.unsplash.com/photo-1583454110551-21f2fa2f36f0?q=80&w=400&auto=format&fit=crop',
|
back: [
|
||||||
},
|
{ uri: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?w=400&q=80&auto=format', correct: true },
|
||||||
{
|
{ uri: 'https://images.unsplash.com/photo-1571721797421-f4c9f2b13107?w=400&q=80&auto=format', correct: false },
|
||||||
id: '3',
|
{ uri: 'https://images.unsplash.com/photo-1518611012118-696072aa579a?w=400&q=80&auto=format', correct: false },
|
||||||
title: 'Backward Lunge',
|
],
|
||||||
duration: '00:40',
|
};
|
||||||
imageUri:
|
|
||||||
'https://images.unsplash.com/photo-1597074866923-5c3bfa3b6c46?q=80&w=400&auto=format&fit=crop',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
title: 'High Knees',
|
|
||||||
duration: '00:30',
|
|
||||||
imageUri:
|
|
||||||
'https://images.unsplash.com/photo-1596357395104-5bcae0b1a5eb?q=80&w=400&auto=format&fit=crop',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function AIPostureAssessmentScreen() {
|
export default function AIPostureAssessmentScreen() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
const theme = Colors.dark;
|
||||||
|
|
||||||
const theme = Colors.dark; // 该页面采用深色视觉
|
const [uploadState, setUploadState] = useState<UploadState>({});
|
||||||
|
const canStart = useMemo(
|
||||||
|
() => Boolean(uploadState.front && uploadState.side && uploadState.back),
|
||||||
|
[uploadState]
|
||||||
|
);
|
||||||
|
|
||||||
|
const [cameraPerm, setCameraPerm] = useState<ImagePicker.PermissionStatus | null>(null);
|
||||||
|
const [libraryPerm, setLibraryPerm] = useState<ImagePicker.PermissionStatus | null>(null);
|
||||||
|
const [libraryAccess, setLibraryAccess] = useState<'all' | 'limited' | 'none' | null>(null);
|
||||||
|
const [cameraCanAsk, setCameraCanAsk] = useState<boolean | null>(null);
|
||||||
|
const [libraryCanAsk, setLibraryCanAsk] = useState<boolean | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const cam = await ImagePicker.getCameraPermissionsAsync();
|
||||||
|
const lib = await ImagePicker.getMediaLibraryPermissionsAsync();
|
||||||
|
setCameraPerm(cam.status);
|
||||||
|
setLibraryPerm(lib.status);
|
||||||
|
setLibraryAccess(
|
||||||
|
(lib as any).accessPrivileges ?? (lib.status === 'granted' ? 'all' : 'none')
|
||||||
|
);
|
||||||
|
setCameraCanAsk(cam.canAskAgain);
|
||||||
|
setLibraryCanAsk(lib.canAskAgain);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function requestAllPermissions() {
|
||||||
|
try {
|
||||||
|
const cam = await ImagePicker.requestCameraPermissionsAsync();
|
||||||
|
const lib = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
||||||
|
setCameraPerm(cam.status);
|
||||||
|
setLibraryPerm(lib.status);
|
||||||
|
setLibraryAccess(
|
||||||
|
(lib as any).accessPrivileges ?? (lib.status === 'granted' ? 'all' : 'none')
|
||||||
|
);
|
||||||
|
setCameraCanAsk(cam.canAskAgain);
|
||||||
|
setLibraryCanAsk(lib.canAskAgain);
|
||||||
|
const libGranted = lib.status === 'granted' || (lib as any).accessPrivileges === 'limited';
|
||||||
|
if (cam.status !== 'granted' || !libGranted) {
|
||||||
|
Alert.alert(
|
||||||
|
'权限未完全授予',
|
||||||
|
'请在系统设置中授予相机与相册权限以完成上传',
|
||||||
|
[
|
||||||
|
{ text: '取消', style: 'cancel' },
|
||||||
|
{ text: '去设置', onPress: () => Linking.openSettings() },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestPermissionAndPick(source: 'camera' | 'library', key: PoseView) {
|
||||||
|
try {
|
||||||
|
if (source === 'camera') {
|
||||||
|
const resp = await ImagePicker.requestCameraPermissionsAsync();
|
||||||
|
setCameraPerm(resp.status);
|
||||||
|
setCameraCanAsk(resp.canAskAgain);
|
||||||
|
if (resp.status !== 'granted') {
|
||||||
|
Alert.alert(
|
||||||
|
'权限不足',
|
||||||
|
'需要相机权限以拍摄照片',
|
||||||
|
resp.canAskAgain
|
||||||
|
? [{ text: '好的' }]
|
||||||
|
: [
|
||||||
|
{ text: '取消', style: 'cancel' },
|
||||||
|
{ text: '去设置', onPress: () => Linking.openSettings() },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await ImagePicker.launchCameraAsync({
|
||||||
|
allowsEditing: true,
|
||||||
|
quality: 0.8,
|
||||||
|
aspect: [3, 4],
|
||||||
|
});
|
||||||
|
if (!result.canceled) {
|
||||||
|
setUploadState((s) => ({ ...s, [key]: result.assets[0]?.uri ?? null }));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const resp = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
||||||
|
setLibraryPerm(resp.status);
|
||||||
|
setLibraryAccess(
|
||||||
|
(resp as any).accessPrivileges ?? (resp.status === 'granted' ? 'all' : 'none')
|
||||||
|
);
|
||||||
|
setLibraryCanAsk(resp.canAskAgain);
|
||||||
|
const libGranted = resp.status === 'granted' || (resp as any).accessPrivileges === 'limited';
|
||||||
|
if (!libGranted) {
|
||||||
|
Alert.alert(
|
||||||
|
'权限不足',
|
||||||
|
'需要相册权限以选择照片',
|
||||||
|
resp.canAskAgain
|
||||||
|
? [{ text: '好的' }]
|
||||||
|
: [
|
||||||
|
{ text: '取消', style: 'cancel' },
|
||||||
|
{ text: '去设置', onPress: () => Linking.openSettings() },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await ImagePicker.launchImageLibraryAsync({
|
||||||
|
allowsEditing: true,
|
||||||
|
quality: 0.8,
|
||||||
|
aspect: [3, 4],
|
||||||
|
});
|
||||||
|
if (!result.canceled) {
|
||||||
|
setUploadState((s) => ({ ...s, [key]: result.assets[0]?.uri ?? null }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Alert.alert('发生错误', '选择图片失败,请重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStart() {
|
||||||
|
if (!canStart) return;
|
||||||
|
// TODO: 调用后端或进入分析页面
|
||||||
|
Alert.alert('开始测评', '已收集三视角照片,准备开始AI体态分析');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.screen, { backgroundColor: theme.background }]}>
|
<View style={[styles.screen, { backgroundColor: theme.background }]}>
|
||||||
@@ -69,7 +181,7 @@ export default function AIPostureAssessmentScreen() {
|
|||||||
>
|
>
|
||||||
<Ionicons name="chevron-back" size={24} color="#ECEDEE" />
|
<Ionicons name="chevron-back" size={24} color="#ECEDEE" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={styles.headerTitle}>AI体态评估</Text>
|
<Text style={styles.headerTitle}>AI体态测评</Text>
|
||||||
<View style={{ width: 32 }} />
|
<View style={{ width: 32 }} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -77,104 +189,142 @@ export default function AIPostureAssessmentScreen() {
|
|||||||
contentContainerStyle={{ paddingBottom: insets.bottom + 120 }}
|
contentContainerStyle={{ paddingBottom: insets.bottom + 120 }}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
>
|
>
|
||||||
{/* Hero */}
|
{/* Permissions Banner (iOS 优先提示) */}
|
||||||
<View style={styles.heroContainer}>
|
{Platform.OS === 'ios' && (
|
||||||
<ImageBackground
|
(cameraPerm !== 'granted' || !(libraryPerm === 'granted' || libraryAccess === 'limited')) && (
|
||||||
source={{
|
<BlurView intensity={18} tint="dark" style={styles.permBanner}>
|
||||||
uri:
|
<Text style={styles.permTitle}>需要相机与相册权限</Text>
|
||||||
'https://images.unsplash.com/photo-1594737625785-c6683fc87c73?q=80&w=1200&auto=format&fit=crop',
|
<Text style={styles.permDesc}>
|
||||||
}}
|
授权后可拍摄或选择三视角全身照片用于AI体态测评。
|
||||||
style={styles.heroImage}
|
</Text>
|
||||||
imageStyle={{ borderRadius: 28 }}
|
<View style={styles.permActions}>
|
||||||
>
|
{((cameraCanAsk ?? true) || (libraryCanAsk ?? true)) ? (
|
||||||
<View style={styles.heroOverlay} />
|
<TouchableOpacity style={styles.permPrimary} onPress={requestAllPermissions}>
|
||||||
</ImageBackground>
|
<Text style={styles.permPrimaryText}>一键授权</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
{/* Floating stats */}
|
) : (
|
||||||
<View style={styles.statsFloating}>
|
<TouchableOpacity style={styles.permPrimary} onPress={() => Linking.openSettings()}>
|
||||||
<StatCard
|
<Text style={styles.permPrimaryText}>去设置开启</Text>
|
||||||
icon={<Ionicons name="time-outline" size={18} color="#192126" />}
|
</TouchableOpacity>
|
||||||
label="Time"
|
)}
|
||||||
value="20 min"
|
<TouchableOpacity style={styles.permSecondary} onPress={() => requestPermissionAndPick('library', 'front')}>
|
||||||
/>
|
<Text style={styles.permSecondaryText}>稍后再说</Text>
|
||||||
<View style={styles.divider} />
|
</TouchableOpacity>
|
||||||
<StatCard
|
|
||||||
icon={<Ionicons name="flame-outline" size={18} color="#192126" />}
|
|
||||||
label="Burn"
|
|
||||||
value="95 kcal"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
|
</BlurView>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Title & description */}
|
{/* Intro */}
|
||||||
<View style={styles.contentSection}>
|
<View style={styles.introBox}>
|
||||||
<Text style={styles.title}>Lower Body Training</Text>
|
<Text style={styles.title}>上传标准姿势照片</Text>
|
||||||
<Text style={styles.description}>
|
<Text style={styles.description}>
|
||||||
The lower abdomen and hips are the most difficult areas of the body to reduce when we are on
|
请依次上传正面、侧面与背面全身照。保持光线均匀、背景简洁,身体立正自然放松。
|
||||||
a diet. Even so, in this area, especially the legs as a whole, you can reduce weight even if
|
|
||||||
you don't use tools.
|
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Rounds header */}
|
{/* Upload sections */}
|
||||||
<View style={styles.roundsHeader}>
|
<UploadTile
|
||||||
<Text style={styles.roundsTitle}>Rounds</Text>
|
label="正面"
|
||||||
<Text style={styles.roundsCount}>1/8</Text>
|
value={uploadState.front}
|
||||||
</View>
|
onPickCamera={() => requestPermissionAndPick('camera', 'front')}
|
||||||
|
onPickLibrary={() => requestPermissionAndPick('library', 'front')}
|
||||||
|
samples={SAMPLES.front}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Exercise list */}
|
<UploadTile
|
||||||
<View style={{ gap: 14, marginHorizontal: 20 }}>
|
label="侧面"
|
||||||
{EXERCISES.map((item) => (
|
value={uploadState.side}
|
||||||
<ExerciseItem key={item.id} exercise={item} />)
|
onPickCamera={() => requestPermissionAndPick('camera', 'side')}
|
||||||
)}
|
onPickLibrary={() => requestPermissionAndPick('library', 'side')}
|
||||||
</View>
|
samples={SAMPLES.side}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UploadTile
|
||||||
|
label="背面"
|
||||||
|
value={uploadState.back}
|
||||||
|
onPickCamera={() => requestPermissionAndPick('camera', 'back')}
|
||||||
|
onPickLibrary={() => requestPermissionAndPick('library', 'back')}
|
||||||
|
samples={SAMPLES.back}
|
||||||
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
{/* Bottom CTA */}
|
{/* Bottom CTA */}
|
||||||
<View style={[styles.bottomCtaWrap, { paddingBottom: insets.bottom + 10 }]}>
|
<View style={[styles.bottomCtaWrap, { paddingBottom: insets.bottom + 10 }]}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.9}
|
disabled={!canStart}
|
||||||
onPress={() => { }}
|
activeOpacity={1}
|
||||||
style={[styles.bottomCta, { backgroundColor: theme.primary }]}
|
onPress={handleStart}
|
||||||
|
style={[
|
||||||
|
styles.bottomCta,
|
||||||
|
{ backgroundColor: canStart ? theme.primary : theme.neutral300 },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Text style={styles.bottomCtaText}>Lets Workout</Text>
|
<Text style={[styles.bottomCtaText, { color: canStart ? theme.onPrimary : theme.textMuted }]}>
|
||||||
|
{canStart ? '开始测评' : '请先完成三视角上传'}
|
||||||
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatCard({
|
function UploadTile({
|
||||||
icon,
|
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
|
onPickCamera,
|
||||||
|
onPickLibrary,
|
||||||
|
samples,
|
||||||
}: {
|
}: {
|
||||||
icon: React.ReactNode;
|
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value?: string | null;
|
||||||
|
onPickCamera: () => void;
|
||||||
|
onPickLibrary: () => void;
|
||||||
|
samples: Sample[];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.statCard}>
|
<View style={styles.section}>
|
||||||
<View style={styles.statIconWrap}>{icon}</View>
|
<View style={styles.sectionHeader}>
|
||||||
<View style={{ marginLeft: 8 }}>
|
<Text style={styles.sectionTitle}>{label}</Text>
|
||||||
<Text style={styles.statLabel}>{label}</Text>
|
{value ? (
|
||||||
<Text style={styles.statValue}>{value}</Text>
|
<Text style={styles.retakeHint}>可长按替换</Text>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.retakeHint}>需上传此视角</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExerciseItem({ exercise }: { exercise: Exercise }) {
|
<TouchableOpacity
|
||||||
return (
|
activeOpacity={0.95}
|
||||||
<View style={styles.exerciseItem}>
|
onLongPress={onPickLibrary}
|
||||||
<Image source={{ uri: exercise.imageUri }} style={styles.exerciseThumb} />
|
onPress={onPickCamera}
|
||||||
<View style={{ flex: 1, marginHorizontal: 12 }}>
|
style={styles.uploader}
|
||||||
<Text style={styles.exerciseTitle}>{exercise.title}</Text>
|
>
|
||||||
<Text style={styles.exerciseDuration}>{exercise.duration}</Text>
|
{value ? (
|
||||||
|
<Image source={{ uri: value }} style={styles.preview} />
|
||||||
|
) : (
|
||||||
|
<View style={styles.placeholder}>
|
||||||
|
<View style={styles.plusBadge}>
|
||||||
|
<Ionicons name="camera" size={16} color="#192126" />
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity style={styles.exercisePlayButton}>
|
<Text style={styles.placeholderTitle}>拍摄或选择照片</Text>
|
||||||
<Ionicons name="play" size={18} color="#BBF246" />
|
<Text style={styles.placeholderDesc}>点击拍摄,长按从相册选择</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<BlurView intensity={18} tint="dark" style={styles.sampleBox}>
|
||||||
|
<Text style={styles.sampleTitle}>示例</Text>
|
||||||
|
<View style={styles.sampleRow}>
|
||||||
|
{samples.map((s, idx) => (
|
||||||
|
<View key={idx} style={styles.sampleItem}>
|
||||||
|
<Image source={{ uri: s.uri }} style={styles.sampleImg} />
|
||||||
|
<View style={[styles.sampleTag, { backgroundColor: s.correct ? '#2BCC7F' : '#E24D4D' }]}>
|
||||||
|
<Text style={styles.sampleTagText}>{s.correct ? '正确示范' : '错误示范'}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</BlurView>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -183,6 +333,57 @@ const styles = StyleSheet.create({
|
|||||||
screen: {
|
screen: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
permBanner: {
|
||||||
|
marginTop: 12,
|
||||||
|
marginHorizontal: 16,
|
||||||
|
padding: 14,
|
||||||
|
borderRadius: 16,
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.04)'
|
||||||
|
},
|
||||||
|
permTitle: {
|
||||||
|
color: '#ECEDEE',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
permDesc: {
|
||||||
|
color: 'rgba(255,255,255,0.75)',
|
||||||
|
marginTop: 6,
|
||||||
|
fontSize: 13,
|
||||||
|
},
|
||||||
|
permActions: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 10,
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
permPrimary: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: '#BBF246',
|
||||||
|
},
|
||||||
|
permPrimaryText: {
|
||||||
|
color: '#192126',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '800',
|
||||||
|
},
|
||||||
|
permSecondary: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'rgba(255,255,255,0.18)',
|
||||||
|
},
|
||||||
|
permSecondaryText: {
|
||||||
|
color: 'rgba(255,255,255,0.85)',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -202,73 +403,13 @@ const styles = StyleSheet.create({
|
|||||||
color: '#ECEDEE',
|
color: '#ECEDEE',
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
heroContainer: {
|
introBox: {
|
||||||
marginTop: 14,
|
marginTop: 12,
|
||||||
marginHorizontal: 16,
|
|
||||||
},
|
|
||||||
heroImage: {
|
|
||||||
height: 260,
|
|
||||||
borderRadius: 28,
|
|
||||||
overflow: 'hidden',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
},
|
|
||||||
heroOverlay: {
|
|
||||||
...StyleSheet.absoluteFillObject,
|
|
||||||
backgroundColor: 'rgba(0,0,0,0.18)',
|
|
||||||
borderRadius: 28,
|
|
||||||
},
|
|
||||||
statsFloating: {
|
|
||||||
position: 'absolute',
|
|
||||||
left: 22,
|
|
||||||
right: 22,
|
|
||||||
bottom: -26,
|
|
||||||
height: 72,
|
|
||||||
borderRadius: 20,
|
|
||||||
backgroundColor: '#1E262C',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-around',
|
|
||||||
paddingHorizontal: 14,
|
|
||||||
shadowColor: '#000',
|
|
||||||
shadowOpacity: 0.3,
|
|
||||||
shadowRadius: 10,
|
|
||||||
shadowOffset: { width: 0, height: 6 },
|
|
||||||
elevation: 6,
|
|
||||||
},
|
|
||||||
divider: {
|
|
||||||
width: 1,
|
|
||||||
height: 36,
|
|
||||||
backgroundColor: 'rgba(255,255,255,0.08)',
|
|
||||||
},
|
|
||||||
statCard: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
statIconWrap: {
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
borderRadius: 12,
|
|
||||||
backgroundColor: '#BBF246',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
statLabel: {
|
|
||||||
color: 'rgba(255,255,255,0.75)',
|
|
||||||
fontSize: 12,
|
|
||||||
marginBottom: 2,
|
|
||||||
},
|
|
||||||
statValue: {
|
|
||||||
color: '#ECEDEE',
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: '700',
|
|
||||||
},
|
|
||||||
contentSection: {
|
|
||||||
marginTop: 46,
|
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: 20,
|
||||||
gap: 12,
|
gap: 10,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 28,
|
fontSize: 26,
|
||||||
color: '#ECEDEE',
|
color: '#ECEDEE',
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
@@ -277,53 +418,98 @@ const styles = StyleSheet.create({
|
|||||||
lineHeight: 22,
|
lineHeight: 22,
|
||||||
color: 'rgba(255,255,255,0.75)',
|
color: 'rgba(255,255,255,0.75)',
|
||||||
},
|
},
|
||||||
roundsHeader: {
|
section: {
|
||||||
marginTop: 18,
|
marginTop: 16,
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: 16,
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
sectionHeader: {
|
||||||
|
paddingHorizontal: 4,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
roundsTitle: {
|
sectionTitle: {
|
||||||
color: '#ECEDEE',
|
color: '#ECEDEE',
|
||||||
fontSize: 22,
|
fontSize: 18,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
roundsCount: {
|
retakeHint: {
|
||||||
color: 'rgba(255,255,255,0.6)',
|
color: 'rgba(255,255,255,0.55)',
|
||||||
fontSize: 16,
|
|
||||||
},
|
|
||||||
exerciseItem: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: '#1E262C',
|
|
||||||
padding: 12,
|
|
||||||
borderRadius: 18,
|
|
||||||
},
|
|
||||||
exerciseThumb: {
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
borderRadius: 12,
|
|
||||||
},
|
|
||||||
exerciseTitle: {
|
|
||||||
color: '#ECEDEE',
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
exerciseDuration: {
|
|
||||||
color: 'rgba(255,255,255,0.6)',
|
|
||||||
marginTop: 4,
|
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
},
|
},
|
||||||
exercisePlayButton: {
|
uploader: {
|
||||||
|
height: 220,
|
||||||
|
borderRadius: 18,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderStyle: 'dashed',
|
||||||
|
borderColor: 'rgba(255,255,255,0.18)',
|
||||||
|
backgroundColor: '#1E262C',
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
plusBadge: {
|
||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
borderRadius: 18,
|
borderRadius: 18,
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: 'rgba(255,255,255,0.2)',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
backgroundColor: '#192126',
|
backgroundColor: '#BBF246',
|
||||||
|
},
|
||||||
|
placeholderTitle: {
|
||||||
|
color: '#ECEDEE',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
placeholderDesc: {
|
||||||
|
color: 'rgba(255,255,255,0.65)',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
sampleBox: {
|
||||||
|
marginTop: 8,
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 12,
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.04)',
|
||||||
|
},
|
||||||
|
sampleTitle: {
|
||||||
|
color: 'rgba(255,255,255,0.8)',
|
||||||
|
fontSize: 14,
|
||||||
|
marginBottom: 8,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
sampleRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
sampleItem: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
sampleImg: {
|
||||||
|
width: '100%',
|
||||||
|
height: 90,
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: '#111',
|
||||||
|
},
|
||||||
|
sampleTag: {
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 4,
|
||||||
|
borderRadius: 8,
|
||||||
|
marginTop: 6,
|
||||||
|
},
|
||||||
|
sampleTagText: {
|
||||||
|
color: '#192126',
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: '700',
|
||||||
},
|
},
|
||||||
bottomCtaWrap: {
|
bottomCtaWrap: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@@ -338,10 +524,8 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
bottomCtaText: {
|
bottomCtaText: {
|
||||||
color: '#192126',
|
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,103 @@
|
|||||||
/**
|
/**
|
||||||
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
|
* 应用全局配色规范(来自设计规范图)。
|
||||||
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
|
* 说明:保持原有导出结构不变,同时扩展更完整的语义令牌与原子调色板。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const primaryColor = '#BBF246'; // 应用主题色
|
// 原子调色板(与设计图一致)
|
||||||
|
export const palette = {
|
||||||
|
// Primary
|
||||||
|
primary: '#BBF246',
|
||||||
|
ink: '#192126',
|
||||||
|
|
||||||
|
// Secondary / Neutrals
|
||||||
|
neutral100: '#888F92',
|
||||||
|
neutral200: '#5E6468',
|
||||||
|
neutral300: '#384046',
|
||||||
|
|
||||||
|
// Accents
|
||||||
|
purple: '#A48AED',
|
||||||
|
red: '#ED4747',
|
||||||
|
orange: '#FCC46F',
|
||||||
|
blue: '#95CCE3',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const primaryColor = palette.primary; // 应用主题色
|
||||||
const tintColorLight = primaryColor;
|
const tintColorLight = primaryColor;
|
||||||
const tintColorDark = '#fff';
|
const tintColorDark = '#FFFFFF';
|
||||||
|
|
||||||
export const Colors = {
|
export const Colors = {
|
||||||
light: {
|
light: {
|
||||||
|
// 基础文本/背景
|
||||||
text: '#11181C',
|
text: '#11181C',
|
||||||
background: '#fff',
|
textSecondary: palette.neutral300,
|
||||||
|
textMuted: palette.neutral200,
|
||||||
|
background: '#FFFFFF',
|
||||||
|
surface: '#FFFFFF',
|
||||||
|
card: '#FFFFFF',
|
||||||
|
|
||||||
|
// 品牌与可交互主色
|
||||||
tint: tintColorLight,
|
tint: tintColorLight,
|
||||||
primary: primaryColor,
|
primary: primaryColor,
|
||||||
|
onPrimary: palette.ink, // 与主色搭配的前景色(按钮文字/图标)
|
||||||
|
|
||||||
|
// 中性色与辅助
|
||||||
|
neutral100: palette.neutral100,
|
||||||
|
neutral200: palette.neutral200,
|
||||||
|
neutral300: palette.neutral300,
|
||||||
|
|
||||||
|
// 状态/反馈色
|
||||||
|
success: palette.primary,
|
||||||
|
warning: palette.orange,
|
||||||
|
danger: palette.red,
|
||||||
|
info: palette.blue,
|
||||||
|
accentPurple: palette.purple,
|
||||||
|
|
||||||
|
// 结构色
|
||||||
|
border: palette.neutral100 + '33', // 20% 透明度
|
||||||
|
separator: palette.neutral100 + '33',
|
||||||
icon: '#687076',
|
icon: '#687076',
|
||||||
|
|
||||||
|
// Tab 相关(保持兼容)
|
||||||
tabIconDefault: '#687076',
|
tabIconDefault: '#687076',
|
||||||
tabIconSelected: '#192126', // tab 激活时的文字/图标颜色(深色,在亮色背景上显示)
|
tabIconSelected: palette.ink, // tab 激活时的文字/图标颜色(深色,在亮色背景上显示)
|
||||||
tabBarBackground: '#192126', // tab 栏背景色
|
tabBarBackground: palette.ink, // tab 栏背景色
|
||||||
tabBarActiveBackground: primaryColor, // tab 激活时的背景色
|
tabBarActiveBackground: primaryColor, // tab 激活时的背景色
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
|
// 基础文本/背景
|
||||||
text: '#ECEDEE',
|
text: '#ECEDEE',
|
||||||
|
textSecondary: palette.neutral100,
|
||||||
|
textMuted: '#9BA1A6',
|
||||||
background: '#151718',
|
background: '#151718',
|
||||||
|
surface: '#1A1D1E',
|
||||||
|
card: '#1A1D1E',
|
||||||
|
|
||||||
|
// 品牌与可交互主色
|
||||||
tint: tintColorDark,
|
tint: tintColorDark,
|
||||||
primary: primaryColor,
|
primary: primaryColor,
|
||||||
|
onPrimary: palette.ink,
|
||||||
|
|
||||||
|
// 中性色与辅助
|
||||||
|
neutral100: palette.neutral100,
|
||||||
|
neutral200: palette.neutral200,
|
||||||
|
neutral300: palette.neutral300,
|
||||||
|
|
||||||
|
// 状态/反馈色
|
||||||
|
success: palette.primary,
|
||||||
|
warning: palette.orange,
|
||||||
|
danger: palette.red,
|
||||||
|
info: palette.blue,
|
||||||
|
accentPurple: palette.purple,
|
||||||
|
|
||||||
|
// 结构色
|
||||||
|
border: '#2A2F32',
|
||||||
|
separator: '#2A2F32',
|
||||||
icon: '#9BA1A6',
|
icon: '#9BA1A6',
|
||||||
|
|
||||||
|
// Tab 相关(保持兼容)
|
||||||
tabIconDefault: '#9BA1A6',
|
tabIconDefault: '#9BA1A6',
|
||||||
tabIconSelected: '#192126', // 在亮色背景上使用深色文字
|
tabIconSelected: palette.ink, // 在亮色背景上使用深色文字
|
||||||
tabBarBackground: '#192126',
|
tabBarBackground: palette.ink,
|
||||||
tabBarActiveBackground: primaryColor,
|
tabBarActiveBackground: primaryColor,
|
||||||
},
|
},
|
||||||
};
|
} as const;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ PODS:
|
|||||||
- DoubleConversion (1.1.6)
|
- DoubleConversion (1.1.6)
|
||||||
- EXConstants (17.1.7):
|
- EXConstants (17.1.7):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
|
- EXImageLoader (5.1.0):
|
||||||
|
- ExpoModulesCore
|
||||||
|
- React-Core
|
||||||
- Expo (53.0.20):
|
- Expo (53.0.20):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
@@ -49,6 +52,8 @@ PODS:
|
|||||||
- SDWebImageAVIFCoder (~> 0.11.0)
|
- SDWebImageAVIFCoder (~> 0.11.0)
|
||||||
- SDWebImageSVGCoder (~> 1.7.0)
|
- SDWebImageSVGCoder (~> 1.7.0)
|
||||||
- SDWebImageWebPCoder (~> 0.14.6)
|
- SDWebImageWebPCoder (~> 0.14.6)
|
||||||
|
- ExpoImagePicker (16.1.4):
|
||||||
|
- ExpoModulesCore
|
||||||
- ExpoKeepAwake (14.1.4):
|
- ExpoKeepAwake (14.1.4):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoLinearGradient (14.1.5):
|
- ExpoLinearGradient (14.1.5):
|
||||||
@@ -2100,6 +2105,7 @@ DEPENDENCIES:
|
|||||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||||
- EXConstants (from `../node_modules/expo-constants/ios`)
|
- EXConstants (from `../node_modules/expo-constants/ios`)
|
||||||
|
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
|
||||||
- Expo (from `../node_modules/expo`)
|
- Expo (from `../node_modules/expo`)
|
||||||
- ExpoAsset (from `../node_modules/expo-asset/ios`)
|
- ExpoAsset (from `../node_modules/expo-asset/ios`)
|
||||||
- ExpoBlur (from `../node_modules/expo-blur/ios`)
|
- ExpoBlur (from `../node_modules/expo-blur/ios`)
|
||||||
@@ -2108,6 +2114,7 @@ DEPENDENCIES:
|
|||||||
- ExpoHaptics (from `../node_modules/expo-haptics/ios`)
|
- ExpoHaptics (from `../node_modules/expo-haptics/ios`)
|
||||||
- ExpoHead (from `../node_modules/expo-router/ios`)
|
- ExpoHead (from `../node_modules/expo-router/ios`)
|
||||||
- ExpoImage (from `../node_modules/expo-image/ios`)
|
- ExpoImage (from `../node_modules/expo-image/ios`)
|
||||||
|
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
|
||||||
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
||||||
- ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`)
|
- ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`)
|
||||||
- ExpoLinking (from `../node_modules/expo-linking/ios`)
|
- ExpoLinking (from `../node_modules/expo-linking/ios`)
|
||||||
@@ -2212,6 +2219,8 @@ EXTERNAL SOURCES:
|
|||||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||||
EXConstants:
|
EXConstants:
|
||||||
:path: "../node_modules/expo-constants/ios"
|
:path: "../node_modules/expo-constants/ios"
|
||||||
|
EXImageLoader:
|
||||||
|
:path: "../node_modules/expo-image-loader/ios"
|
||||||
Expo:
|
Expo:
|
||||||
:path: "../node_modules/expo"
|
:path: "../node_modules/expo"
|
||||||
ExpoAsset:
|
ExpoAsset:
|
||||||
@@ -2228,6 +2237,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-router/ios"
|
:path: "../node_modules/expo-router/ios"
|
||||||
ExpoImage:
|
ExpoImage:
|
||||||
:path: "../node_modules/expo-image/ios"
|
:path: "../node_modules/expo-image/ios"
|
||||||
|
ExpoImagePicker:
|
||||||
|
:path: "../node_modules/expo-image-picker/ios"
|
||||||
ExpoKeepAwake:
|
ExpoKeepAwake:
|
||||||
:path: "../node_modules/expo-keep-awake/ios"
|
:path: "../node_modules/expo-keep-awake/ios"
|
||||||
ExpoLinearGradient:
|
ExpoLinearGradient:
|
||||||
@@ -2400,6 +2411,7 @@ SPEC CHECKSUMS:
|
|||||||
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
|
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
|
||||||
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
|
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
|
||||||
EXConstants: 98bcf0f22b820f9b28f9fee55ff2daededadd2f8
|
EXConstants: 98bcf0f22b820f9b28f9fee55ff2daededadd2f8
|
||||||
|
EXImageLoader: 4d3d3284141f1a45006cc4d0844061c182daf7ee
|
||||||
Expo: a40d525c930dd1c8a158e082756ee071955baccb
|
Expo: a40d525c930dd1c8a158e082756ee071955baccb
|
||||||
ExpoAsset: ef06e880126c375f580d4923fdd1cdf4ee6ee7d6
|
ExpoAsset: ef06e880126c375f580d4923fdd1cdf4ee6ee7d6
|
||||||
ExpoBlur: 3c8885b9bf9eef4309041ec87adec48b5f1986a9
|
ExpoBlur: 3c8885b9bf9eef4309041ec87adec48b5f1986a9
|
||||||
@@ -2408,6 +2420,7 @@ SPEC CHECKSUMS:
|
|||||||
ExpoHaptics: 0ff6e0d83cd891178a306e548da1450249d54500
|
ExpoHaptics: 0ff6e0d83cd891178a306e548da1450249d54500
|
||||||
ExpoHead: a7b66cbaeeb51f4a85338d335a0f5467e29a2c90
|
ExpoHead: a7b66cbaeeb51f4a85338d335a0f5467e29a2c90
|
||||||
ExpoImage: e4102c93d1dbe99ff54b075452d1bc9d6ec21b7c
|
ExpoImage: e4102c93d1dbe99ff54b075452d1bc9d6ec21b7c
|
||||||
|
ExpoImagePicker: 0963da31800c906e01c03e25d7c849f16ebf02a2
|
||||||
ExpoKeepAwake: bf0811570c8da182bfb879169437d4de298376e7
|
ExpoKeepAwake: bf0811570c8da182bfb879169437d4de298376e7
|
||||||
ExpoLinearGradient: 7734c8059972fcf691fb4330bcdf3390960a152d
|
ExpoLinearGradient: 7734c8059972fcf691fb4330bcdf3390960a152d
|
||||||
ExpoLinking: d5c183998ca6ada66ff45e407e0f965b398a8902
|
ExpoLinking: d5c183998ca6ada66ff45e407e0f965b398a8902
|
||||||
|
|||||||
@@ -8,28 +8,28 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
27B482E4BA415859EA8B0372 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8732A3660B00244505674555 /* ExpoModulesProvider.swift */; };
|
2C9C524987451393B76B9C7E /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 7EC44F9488C227087AA8DF97 /* PrivacyInfo.xcprivacy */; };
|
||||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||||
7F5A026795C1BF28BEBBFA4E /* libPods-digitalpilates.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAB65677BEFC9C3E52449E8 /* libPods-digitalpilates.a */; };
|
6B6021A2D1EB466803BE19D7 /* libPods-digitalpilates.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F1A078ADDB1BCB06E0DBEFDA /* libPods-digitalpilates.a */; };
|
||||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||||
CBE4D0B57478826DADA47BC9 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = C59D7448330DC33ACFEECB09 /* PrivacyInfo.xcprivacy */; };
|
DC3BFC72D3A68C7493D5B44A /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D1B5F0EC906D7A2F599549 /* ExpoModulesProvider.swift */; };
|
||||||
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
|
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
031BAB3982A98E78D1493449 /* Pods-digitalpilates.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.debug.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.debug.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
13B07F961A680F5B00A75B9A /* digitalpilates.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = digitalpilates.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
13B07F961A680F5B00A75B9A /* digitalpilates.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = digitalpilates.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = digitalpilates/Images.xcassets; sourceTree = "<group>"; };
|
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = digitalpilates/Images.xcassets; sourceTree = "<group>"; };
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = digitalpilates/Info.plist; sourceTree = "<group>"; };
|
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = digitalpilates/Info.plist; sourceTree = "<group>"; };
|
||||||
7398BB3C47424238F7BEF8F9 /* Pods-digitalpilates.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.release.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.release.xcconfig"; sourceTree = "<group>"; };
|
4D6B8E20DD8E5677F8B2EAA1 /* Pods-digitalpilates.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.debug.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
8732A3660B00244505674555 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-digitalpilates/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
|
7EC44F9488C227087AA8DF97 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = digitalpilates/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||||
|
83D1B5F0EC906D7A2F599549 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-digitalpilates/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
|
||||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = digitalpilates/SplashScreen.storyboard; sourceTree = "<group>"; };
|
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = digitalpilates/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||||
C59D7448330DC33ACFEECB09 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = digitalpilates/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
EA6A757B2DE1747F7B3664B4 /* Pods-digitalpilates.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.release.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||||
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = digitalpilates/AppDelegate.swift; sourceTree = "<group>"; };
|
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = digitalpilates/AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
F11748442D0722820044C1D9 /* digitalpilates-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "digitalpilates-Bridging-Header.h"; path = "digitalpilates/digitalpilates-Bridging-Header.h"; sourceTree = "<group>"; };
|
F11748442D0722820044C1D9 /* digitalpilates-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "digitalpilates-Bridging-Header.h"; path = "digitalpilates/digitalpilates-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
FDAB65677BEFC9C3E52449E8 /* libPods-digitalpilates.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-digitalpilates.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
F1A078ADDB1BCB06E0DBEFDA /* libPods-digitalpilates.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-digitalpilates.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
7F5A026795C1BF28BEBBFA4E /* libPods-digitalpilates.a in Frameworks */,
|
6B6021A2D1EB466803BE19D7 /* libPods-digitalpilates.a in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
|
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
|
||||||
C59D7448330DC33ACFEECB09 /* PrivacyInfo.xcprivacy */,
|
7EC44F9488C227087AA8DF97 /* PrivacyInfo.xcprivacy */,
|
||||||
);
|
);
|
||||||
name = digitalpilates;
|
name = digitalpilates;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -62,17 +62,19 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||||
FDAB65677BEFC9C3E52449E8 /* libPods-digitalpilates.a */,
|
F1A078ADDB1BCB06E0DBEFDA /* libPods-digitalpilates.a */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
7B79F82457416491183F46D6 /* digitalpilates */ = {
|
3EE8D66219D64F4A63E8298D /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
8732A3660B00244505674555 /* ExpoModulesProvider.swift */,
|
4D6B8E20DD8E5677F8B2EAA1 /* Pods-digitalpilates.debug.xcconfig */,
|
||||||
|
EA6A757B2DE1747F7B3664B4 /* Pods-digitalpilates.release.xcconfig */,
|
||||||
);
|
);
|
||||||
name = digitalpilates;
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||||
@@ -89,8 +91,8 @@
|
|||||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
8D14961AE80832AA51F9FFA3 /* Pods */,
|
3EE8D66219D64F4A63E8298D /* Pods */,
|
||||||
F7E6B913F90880BFADBE192E /* ExpoModulesProviders */,
|
F899CC3CCA86CFEC0C4F53F7 /* ExpoModulesProviders */,
|
||||||
);
|
);
|
||||||
indentWidth = 2;
|
indentWidth = 2;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -105,15 +107,6 @@
|
|||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
8D14961AE80832AA51F9FFA3 /* Pods */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
031BAB3982A98E78D1493449 /* Pods-digitalpilates.debug.xcconfig */,
|
|
||||||
7398BB3C47424238F7BEF8F9 /* Pods-digitalpilates.release.xcconfig */,
|
|
||||||
);
|
|
||||||
path = Pods;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
BB2F792B24A3F905000567C9 /* Supporting */ = {
|
BB2F792B24A3F905000567C9 /* Supporting */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -123,10 +116,18 @@
|
|||||||
path = digitalpilates/Supporting;
|
path = digitalpilates/Supporting;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
F7E6B913F90880BFADBE192E /* ExpoModulesProviders */ = {
|
DFAD2B7142CEC38E9ED66053 /* digitalpilates */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7B79F82457416491183F46D6 /* digitalpilates */,
|
83D1B5F0EC906D7A2F599549 /* ExpoModulesProvider.swift */,
|
||||||
|
);
|
||||||
|
name = digitalpilates;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
F899CC3CCA86CFEC0C4F53F7 /* ExpoModulesProviders */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DFAD2B7142CEC38E9ED66053 /* digitalpilates */,
|
||||||
);
|
);
|
||||||
name = ExpoModulesProviders;
|
name = ExpoModulesProviders;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -139,13 +140,13 @@
|
|||||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "digitalpilates" */;
|
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "digitalpilates" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
|
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
|
||||||
E959B4C3D4071EECDD504F2E /* [Expo] Configure project */,
|
60F566376E07CDAA8138E40B /* [Expo] Configure project */,
|
||||||
13B07F871A680F5B00A75B9A /* Sources */,
|
13B07F871A680F5B00A75B9A /* Sources */,
|
||||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
||||||
679509E8E2F87FD68CDC2915 /* [CP] Embed Pods Frameworks */,
|
761236F3114550442BC2DA44 /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -195,7 +196,7 @@
|
|||||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
|
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
|
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
|
||||||
CBE4D0B57478826DADA47BC9 /* PrivacyInfo.xcprivacy in Resources */,
|
2C9C524987451393B76B9C7E /* PrivacyInfo.xcprivacy in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -239,7 +240,26 @@
|
|||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
679509E8E2F87FD68CDC2915 /* [CP] Embed Pods Frameworks */ = {
|
60F566376E07CDAA8138E40B /* [Expo] Configure project */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "[Expo] Configure project";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-digitalpilates/expo-configure-project.sh\"\n";
|
||||||
|
};
|
||||||
|
761236F3114550442BC2DA44 /* [CP] Embed Pods Frameworks */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
@@ -295,25 +315,6 @@
|
|||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-digitalpilates/Pods-digitalpilates-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-digitalpilates/Pods-digitalpilates-resources.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
E959B4C3D4071EECDD504F2E /* [Expo] Configure project */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[Expo] Configure project";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-digitalpilates/expo-configure-project.sh\"\n";
|
|
||||||
};
|
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@@ -322,7 +323,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */,
|
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */,
|
||||||
27B482E4BA415859EA8B0372 /* ExpoModulesProvider.swift in Sources */,
|
DC3BFC72D3A68C7493D5B44A /* ExpoModulesProvider.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -331,13 +332,12 @@
|
|||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 031BAB3982A98E78D1493449 /* Pods-digitalpilates.debug.xcconfig */;
|
baseConfigurationReference = 4D6B8E20DD8E5677F8B2EAA1 /* Pods-digitalpilates.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = digitalpilates/digitalpilates.entitlements;
|
CODE_SIGN_ENTITLEMENTS = digitalpilates/digitalpilates.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = 756WVXJ6MT;
|
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -368,13 +368,12 @@
|
|||||||
};
|
};
|
||||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7398BB3C47424238F7BEF8F9 /* Pods-digitalpilates.release.xcconfig */;
|
baseConfigurationReference = EA6A757B2DE1747F7B3664B4 /* Pods-digitalpilates.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = digitalpilates/digitalpilates.entitlements;
|
CODE_SIGN_ENTITLEMENTS = digitalpilates/digitalpilates.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = 756WVXJ6MT;
|
|
||||||
INFOPLIST_FILE = digitalpilates/Info.plist;
|
INFOPLIST_FILE = digitalpilates/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
@@ -28,12 +28,14 @@
|
|||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>digitalpilates</string>
|
<string>digitalpilates</string>
|
||||||
<string>digital-pilates</string>
|
<string>com.anonymous.digitalpilates</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>12.0</string>
|
<string>12.0</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
@@ -45,10 +47,16 @@
|
|||||||
<key>NSAllowsLocalNetworking</key>
|
<key>NSAllowsLocalNetworking</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>应用需要使用相机以拍摄您的体态照片用于AI测评。</string>
|
||||||
<key>NSHealthShareUsageDescription</key>
|
<key>NSHealthShareUsageDescription</key>
|
||||||
<string>应用需要访问您的健康数据(步数与能量消耗)以展示运动统计。</string>
|
<string>应用需要访问您的健康数据(步数与能量消耗)以展示运动统计。</string>
|
||||||
<key>NSHealthUpdateUsageDescription</key>
|
<key>NSHealthUpdateUsageDescription</key>
|
||||||
<string>Allow $(PRODUCT_NAME) to update health info</string>
|
<string>Allow $(PRODUCT_NAME) to update health info</string>
|
||||||
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
|
<string>应用需要写入相册以保存拍摄的体态照片(可选)。</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>应用需要访问相册以选择您的体态照片用于AI测评。</string>
|
||||||
<key>NSUserActivityTypes</key>
|
<key>NSUserActivityTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
||||||
@@ -79,8 +87,5 @@
|
|||||||
<string>Automatic</string>
|
<string>Automatic</string>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
<!-- Export compliance: App does not use non-exempt encryption -->
|
</dict>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
</plist>
|
||||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -20,6 +20,7 @@
|
|||||||
"expo-font": "~13.3.2",
|
"expo-font": "~13.3.2",
|
||||||
"expo-haptics": "~14.1.4",
|
"expo-haptics": "~14.1.4",
|
||||||
"expo-image": "~2.4.0",
|
"expo-image": "~2.4.0",
|
||||||
|
"expo-image-picker": "~16.1.4",
|
||||||
"expo-linear-gradient": "^14.1.5",
|
"expo-linear-gradient": "^14.1.5",
|
||||||
"expo-linking": "~7.1.7",
|
"expo-linking": "~7.1.7",
|
||||||
"expo-router": "~5.1.4",
|
"expo-router": "~5.1.4",
|
||||||
@@ -6301,6 +6302,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-image-loader": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://mirrors.tencent.com/npm/expo-image-loader/-/expo-image-loader-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-sEBx3zDQIODWbB5JwzE7ZL5FJD+DK3LVLWBVJy6VzsqIA6nDEnSFnsnWyCfCTSvbGigMATs1lgkC2nz3Jpve1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/expo-image-picker": {
|
||||||
|
"version": "16.1.4",
|
||||||
|
"resolved": "https://mirrors.tencent.com/npm/expo-image-picker/-/expo-image-picker-16.1.4.tgz",
|
||||||
|
"integrity": "sha512-bTmmxtw1AohUT+HxEBn2vYwdeOrj1CLpMXKjvi9FKSoSbpcarT4xxI0z7YyGwDGHbrJqyyic3I9TTdP2J2b4YA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"expo-image-loader": "~5.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-keep-awake": {
|
"node_modules/expo-keep-awake": {
|
||||||
"version": "14.1.4",
|
"version": "14.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz",
|
||||||
|
|||||||
@@ -40,7 +40,8 @@
|
|||||||
"react-native-safe-area-context": "5.4.0",
|
"react-native-safe-area-context": "5.4.0",
|
||||||
"react-native-screens": "~4.11.1",
|
"react-native-screens": "~4.11.1",
|
||||||
"react-native-web": "~0.20.0",
|
"react-native-web": "~0.20.0",
|
||||||
"react-native-webview": "13.13.5"
|
"react-native-webview": "13.13.5",
|
||||||
|
"expo-image-picker": "~16.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user