feat: Implement Food Camera Screen and Floating Food Overlay
- Added FoodCameraScreen for capturing food images with camera functionality. - Integrated image picker for selecting images from the gallery. - Created FloatingFoodOverlay for quick access to food library and scanning options. - Updated NutritionRadarCard to utilize FloatingFoodOverlay for adding food. - Enhanced ExploreScreen layout and styles for better user experience. - Removed unused SafeAreaView from ExploreScreen. - Updated profile edit screen to remove unnecessary state variables. - Updated avatar image source in profile edit screen. - Added ExpoCamera dependency for camera functionalities.
This commit is contained in:
192
components/FloatingFoodOverlay.tsx
Normal file
192
components/FloatingFoodOverlay.tsx
Normal file
@@ -0,0 +1,192 @@
|
||||
import { ROUTES } from '@/constants/Routes';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { useRouter } from 'expo-router';
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
interface FloatingFoodOverlayProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
mealType?: string;
|
||||
}
|
||||
|
||||
export function FloatingFoodOverlay({ visible, onClose, mealType = 'dinner' }: FloatingFoodOverlayProps) {
|
||||
const router = useRouter();
|
||||
|
||||
const handleFoodLibrary = () => {
|
||||
onClose();
|
||||
router.push(`${ROUTES.FOOD_LIBRARY}?mealType=${mealType}`);
|
||||
};
|
||||
|
||||
const handlePhotoRecognition = () => {
|
||||
onClose();
|
||||
router.push(`/food-camera?mealType=${mealType}`);
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
id: 'scan',
|
||||
title: '扫描',
|
||||
icon: '📷',
|
||||
backgroundColor: '#4FC3F7',
|
||||
onPress: handlePhotoRecognition,
|
||||
},
|
||||
{
|
||||
id: 'food-library',
|
||||
title: '食物库',
|
||||
icon: '🍎',
|
||||
backgroundColor: '#FF9500',
|
||||
onPress: handleFoodLibrary,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
|
||||
transparent={true}
|
||||
animationType="fade"
|
||||
onRequestClose={onClose}
|
||||
|
||||
>
|
||||
<View style={styles.overlay}>
|
||||
<TouchableOpacity
|
||||
style={styles.backdrop}
|
||||
activeOpacity={1}
|
||||
onPress={onClose}
|
||||
/>
|
||||
|
||||
<View style={styles.container}>
|
||||
<BlurView intensity={80} tint="light" style={styles.blurContainer}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.title}>日常记录</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.menuGrid}>
|
||||
{menuItems.map((item) => (
|
||||
<TouchableOpacity
|
||||
key={item.id}
|
||||
style={styles.menuItem}
|
||||
onPress={item.onPress}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={[styles.iconContainer, { backgroundColor: item.backgroundColor }]}>
|
||||
<Text style={styles.iconText}>{item.icon}</Text>
|
||||
</View>
|
||||
<Text style={styles.menuText}>{item.title}</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</BlurView>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.closeButton}
|
||||
onPress={onClose}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.closeButtonInner}>
|
||||
<Ionicons name="close" size={24} color="#666" />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
backdrop: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
blurContainer: {
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||
minWidth: 340,
|
||||
paddingVertical: 20,
|
||||
paddingHorizontal: 16,
|
||||
minHeight: 100
|
||||
},
|
||||
header: {
|
||||
paddingBottom: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: '#333',
|
||||
},
|
||||
menuGrid: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
gap: 32,
|
||||
},
|
||||
menuItem: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
},
|
||||
iconContainer: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 24,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.15,
|
||||
shadowRadius: 4,
|
||||
elevation: 4,
|
||||
},
|
||||
iconText: {
|
||||
fontSize: 22,
|
||||
},
|
||||
menuText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '500',
|
||||
color: '#333',
|
||||
textAlign: 'center',
|
||||
},
|
||||
closeButton: {
|
||||
marginTop: 20,
|
||||
},
|
||||
closeButtonInner: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user