import { HeaderBar } from '@/components/ui/HeaderBar'; import { Colors } from '@/constants/Colors'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useI18n } from '@/hooks/useI18n'; import { Ionicons } from '@expo/vector-icons'; import { CameraType, CameraView, useCameraPermissions } from 'expo-camera'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; import React, { useMemo, useRef, useState } from 'react'; import { ActivityIndicator, Alert, Dimensions, Modal, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack'; export default function FoodCameraScreen() { const { t } = useI18n(); const insets = useSafeAreaInsets(); const router = useRouter(); const params = useLocalSearchParams<{ mealType?: string }>(); const cameraRef = useRef(null); const { ensureLoggedIn } = useAuthGuard(); const scheme = (useColorScheme() ?? 'light') as keyof typeof Colors; const colors = Colors[scheme]; const [currentMealType, setCurrentMealType] = useState( (params.mealType as MealType) || 'dinner' ); const [facing, setFacing] = useState('back'); const [permission, requestPermission] = useCameraPermissions(); const [showInstructionModal, setShowInstructionModal] = useState(false); const [isCapturing, setIsCapturing] = useState(false); // 餐次选择选项 const mealOptions = [ { key: 'breakfast' as const, label: t('nutritionRecords.mealTypes.breakfast'), icon: '☀️' }, { key: 'lunch' as const, label: t('nutritionRecords.mealTypes.lunch'), icon: '🌤️' }, { key: 'dinner' as const, label: t('nutritionRecords.mealTypes.dinner'), icon: '🌙' }, { key: 'snack' as const, label: t('nutritionRecords.mealTypes.snack'), icon: '🍎' }, ]; // 计算固定的相机高度 const cameraHeight = useMemo(() => { const { height: screenHeight } = Dimensions.get('window'); // 计算固定占用的高度 const headerHeight = insets.top + 40; // HeaderBar 高度 const topMetaHeight = 12 + 28 + 26 + 16 + 6; // topMeta 区域 const shotsRowHeight = 12 + 88; // MealType 区域 const bottomBarHeight = 12 + 86 + 10 + Math.max(insets.bottom, 20); // bottomBar 区域 const margins = 12 + 12; // cameraCard 的上下边距 // 可用于相机的高度 const availableHeight = screenHeight - headerHeight - topMetaHeight - shotsRowHeight - bottomBarHeight - margins; // 确保最小高度为 300,最大不超过屏幕的 55% return Math.max(300, Math.min(availableHeight, screenHeight * 0.55)); }, [insets.top, insets.bottom]); if (!permission) { return ( router.back()} transparent={true} /> ); } if (!permission.granted) { return ( router.back()} transparent /> {t('foodCamera.permission.title')} {t('foodCamera.permission.description')} {t('foodCamera.permission.button')} ); } // 切换相机前后摄像头 function toggleCameraFacing() { setFacing(current => (current === 'back' ? 'front' : 'back')); } // 拍摄照片 const takePicture = async () => { if (cameraRef.current && !isCapturing) { setIsCapturing(true); try { const photo = await cameraRef.current.takePictureAsync({ quality: 0.8, base64: false, }); if (photo) { console.log('照片拍摄成功:', photo.uri); const isLoggedIn = await ensureLoggedIn(); if (isLoggedIn) { router.replace(`/food/food-recognition?imageUri=${encodeURIComponent(photo.uri)}&mealType=${currentMealType}`); } } } catch (error) { console.error('拍照失败:', error); Alert.alert(t('foodCamera.alerts.captureFailed.title'), t('foodCamera.alerts.captureFailed.message')); } finally { setIsCapturing(false); } } }; // 从相册选择照片 const pickImageFromGallery = async () => { try { const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsEditing: true, aspect: [4, 3], quality: 0.8, }); if (!result.canceled && result.assets[0]) { const imageUri = result.assets[0].uri; console.log('从相册选择的照片:', imageUri); const isLoggedIn = await ensureLoggedIn(); if (isLoggedIn) { router.push(`/food/food-recognition?imageUri=${encodeURIComponent(imageUri)}&mealType=${currentMealType}`); } } } catch (error) { console.error('选择照片失败:', error); Alert.alert(t('foodCamera.alerts.pickFailed.title'), t('foodCamera.alerts.pickFailed.message')); } }; // 餐次选择 const handleMealTypeChange = (mealType: MealType) => { setCurrentMealType(mealType); }; return ( router.back()} transparent={true} right={ setShowInstructionModal(true)} activeOpacity={0.7} > {isLiquidGlassAvailable() ? ( ) : ( )} } /> {/* Top Meta Info */} {t('foodCamera.hint')} {t('nutritionRecords.listTitle')} {t('foodCamera.guide.description')} {/* Camera Card */} {/* Viewfinder Overlay */} {/* Meal Type Selector (Replacing Shots Row) */} {mealOptions.map((option) => { const active = currentMealType === option.key; return ( handleMealTypeChange(option.key)} activeOpacity={0.7} style={[styles.shotCard, active && styles.shotCardActive]} > {option.icon} {option.label} ); })} {/* Bottom Actions */} {/* Album Button */} {isLiquidGlassAvailable() ? ( {t('foodCamera.buttons.album')} ) : ( {t('foodCamera.buttons.album')} )} {/* Capture Button */} {isLiquidGlassAvailable() ? ( {isCapturing ? ( ) : ( )} ) : ( {isCapturing ? ( ) : ( )} )} {/* Flip Button */} {isLiquidGlassAvailable() ? ( {t('foodCamera.buttons.capture')} ) : ( {t('foodCamera.buttons.capture')} )} {/* Instruction Modal */} setShowInstructionModal(false)} > {t('foodCamera.guide.title')} {/* Good Example */} {t('foodCamera.guide.good')} {/* Bad Example */} {t('foodCamera.guide.bad')} {t('foodCamera.guide.description')} setShowInstructionModal(false)} > {t('foodCamera.guide.button')} ); } const styles = StyleSheet.create({ container: { flex: 1, }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, topMeta: { paddingHorizontal: 20, paddingTop: 12, gap: 6, }, metaBadge: { alignSelf: 'flex-start', backgroundColor: '#e0f2fe', paddingHorizontal: 10, paddingVertical: 6, borderRadius: 14, }, metaBadgeText: { color: '#0369a1', fontWeight: '700', fontSize: 12, }, metaTitle: { fontSize: 22, fontWeight: '700', color: '#0f172a', }, metaSubtitle: { fontSize: 14, color: '#475569', }, cameraCard: { marginHorizontal: 20, marginTop: 12, borderRadius: 24, overflow: 'hidden', shadowColor: '#0f172a', shadowOpacity: 0.12, shadowRadius: 18, shadowOffset: { width: 0, height: 10 }, }, cameraFrame: { borderRadius: 24, overflow: 'hidden', backgroundColor: '#0b172a', position: 'relative', }, cameraView: { flex: 1, }, cameraOverlay: { position: 'absolute', left: 0, right: 0, bottom: 0, height: 80, }, viewfinderOverlay: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', alignItems: 'center', margin: 20, }, corner: { position: 'absolute', width: 30, height: 30, borderColor: 'rgba(255, 255, 255, 0.6)', borderWidth: 4, borderRadius: 2, }, topLeft: { top: 0, left: 0, borderRightWidth: 0, borderBottomWidth: 0, }, topRight: { top: 0, right: 0, borderLeftWidth: 0, borderBottomWidth: 0, }, bottomLeft: { bottom: 0, left: 0, borderRightWidth: 0, borderTopWidth: 0, }, bottomRight: { bottom: 0, right: 0, borderLeftWidth: 0, borderTopWidth: 0, }, shotsRow: { flexDirection: 'row', paddingHorizontal: 20, paddingTop: 12, gap: 8, justifyContent: 'space-between', }, shotCard: { flex: 1, borderRadius: 14, backgroundColor: '#f8fafc', paddingVertical: 12, gap: 6, borderWidth: 1, borderColor: '#e2e8f0', alignItems: 'center', justifyContent: 'center', }, shotCardActive: { borderColor: '#38bdf8', backgroundColor: '#ecfeff', }, mealTypeIcon: { fontSize: 20, }, shotLabel: { fontSize: 12, color: '#475569', fontWeight: '600', }, shotLabelActive: { color: '#0ea5e9', }, bottomBar: { paddingHorizontal: 20, paddingTop: 12, gap: 10, }, bottomActions: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, captureBtn: { width: 72, height: 72, borderRadius: 36, justifyContent: 'center', alignItems: 'center', overflow: 'hidden', shadowColor: '#0ea5e9', shadowOpacity: 0.25, shadowRadius: 12, shadowOffset: { width: 0, height: 6 }, }, fallbackCaptureBtn: { backgroundColor: 'rgba(255, 255, 255, 0.95)', borderWidth: 2, borderColor: 'rgba(14, 165, 233, 0.2)', }, captureOuterRing: { width: 60, height: 60, borderRadius: 30, backgroundColor: 'rgba(255, 255, 255, 0.15)', justifyContent: 'center', alignItems: 'center', }, captureInner: { width: 48, height: 48, borderRadius: 24, backgroundColor: '#fff', shadowColor: '#0ea5e9', shadowOpacity: 0.4, shadowRadius: 6, shadowOffset: { width: 0, height: 2 }, }, secondaryBtn: { flexDirection: 'row', alignItems: 'center', gap: 6, paddingHorizontal: 16, paddingVertical: 12, borderRadius: 16, overflow: 'hidden', shadowColor: '#0f172a', shadowOpacity: 0.08, shadowRadius: 8, shadowOffset: { width: 0, height: 4 }, minWidth: 88, justifyContent: 'center', }, fallbackSecondaryBtn: { backgroundColor: 'rgba(255, 255, 255, 0.9)', borderWidth: 1, borderColor: 'rgba(15, 23, 42, 0.1)', }, secondaryBtnText: { color: '#0f172a', fontWeight: '600', fontSize: 14, }, infoButton: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center', overflow: 'hidden', }, fallbackInfoButton: { backgroundColor: 'rgba(255, 255, 255, 0.9)', borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.3)', }, permissionCard: { marginHorizontal: 24, borderRadius: 18, padding: 24, backgroundColor: '#fff', shadowColor: '#0f172a', shadowOpacity: 0.08, shadowRadius: 12, shadowOffset: { width: 0, height: 10 }, alignItems: 'center', gap: 10, }, permissionTitle: { fontSize: 18, fontWeight: '700', color: '#0f172a', marginBottom: 4, }, permissionTip: { fontSize: 14, color: '#475569', textAlign: 'center', lineHeight: 20, marginBottom: 16, }, permissionBtn: { borderRadius: 14, paddingHorizontal: 24, paddingVertical: 14, width: '100%', alignItems: 'center', }, permissionBtnText: { color: '#fff', fontWeight: '700', fontSize: 16, }, modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.7)', justifyContent: 'center', paddingHorizontal: 20, }, instructionModal: { backgroundColor: '#FFF', borderRadius: 24, padding: 24, alignItems: 'center', }, instructionTitle: { fontSize: 20, fontWeight: '700', color: '#0f172a', marginBottom: 24, }, exampleContainer: { flexDirection: 'row', gap: 16, marginBottom: 24, }, exampleItem: { flex: 1, alignItems: 'center', gap: 8, }, exampleImagePlaceholder: { width: '100%', aspectRatio: 1, backgroundColor: '#F1F5F9', borderRadius: 16, overflow: 'hidden', position: 'relative', }, exampleImage: { width: '100%', height: '100%', }, exampleText: { fontSize: 13, color: '#64748b', fontWeight: '500', }, checkmarkContainer: { position: 'absolute', top: 8, right: 8, width: 24, height: 24, borderRadius: 12, backgroundColor: '#22c55e', justifyContent: 'center', alignItems: 'center', zIndex: 10, }, crossContainer: { position: 'absolute', top: 8, right: 8, width: 24, height: 24, borderRadius: 12, backgroundColor: '#ef4444', justifyContent: 'center', alignItems: 'center', zIndex: 10, }, instructionDescription: { fontSize: 15, textAlign: 'center', color: '#334155', marginBottom: 24, lineHeight: 22, }, knowButton: { backgroundColor: '#0f172a', borderRadius: 16, paddingVertical: 14, paddingHorizontal: 32, width: '100%', }, knowButtonText: { color: '#FFF', fontSize: 16, fontWeight: '600', textAlign: 'center', }, });