fitaiProto/apps/mobile/src/app/(tabs)/index.tsx
echo db0d2cf215 scan food
with mock db, real db[openfoodfacts] to be implemented
2025-12-01 19:49:10 +01:00

334 lines
9.4 KiB
TypeScript

import { View, Text, StyleSheet, ScrollView, RefreshControl, Image } from "react-native";
import { useUser } from "@clerk/clerk-expo";
import { LinearGradient } from "expo-linear-gradient";
import { useState, useCallback, useEffect } from "react";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { theme } from "../../styles/theme";
import { ActivityWidget } from "../../components/ActivityWidget";
import { QuickActionGrid } from "../../components/QuickActionGrid";
import { TrackMealModal } from "../../components/TrackMealModal";
import { AddWaterModal } from "../../components/AddWaterModal";
import { HydrationWidget } from "../../components/HydrationWidget";
import { ScanFoodModal } from "../../components/ScanFoodModal";
import { Ionicons } from "@expo/vector-icons";
export default function HomeScreen() {
const { user } = useUser();
const [refreshing, setRefreshing] = useState(false);
const [trackMealModalVisible, setTrackMealModalVisible] = useState(false);
const [addWaterModalVisible, setAddWaterModalVisible] = useState(false);
const [scanFoodModalVisible, setScanFoodModalVisible] = useState(false);
const [calories, setCalories] = useState(0);
const [waterIntake, setWaterIntake] = useState(0);
const onRefresh = useCallback(() => {
setRefreshing(true);
setTimeout(() => {
setRefreshing(false);
}, 2000);
}, []);
const getGreeting = () => {
const hour = new Date().getHours();
if (hour < 12) return "Good Morning";
if (hour < 18) return "Good Afternoon";
return "Good Evening";
};
const handleSaveMeal = (meal: { type: string; name: string; calories: number }) => {
setCalories(prev => prev + meal.calories);
setTrackMealModalVisible(false);
};
const handleAddWater = (amount: number) => {
setWaterIntake(prev => prev + amount);
setAddWaterModalVisible(false);
};
const handleResetCalories = () => {
setCalories(0);
};
const handleResetWater = () => {
setWaterIntake(0);
};
const handleAddScannedFood = (scannedCalories: number) => {
setCalories(prev => prev + scannedCalories);
setScanFoodModalVisible(false);
};
const resetAllCounters = async () => {
setCalories(0);
setWaterIntake(0);
const today = new Date().toDateString();
await AsyncStorage.setItem('lastResetDate', today);
};
// Check for midnight reset
useEffect(() => {
const checkAndResetIfNeeded = async () => {
const lastResetDate = await AsyncStorage.getItem('lastResetDate');
const today = new Date().toDateString();
if (lastResetDate !== today) {
await resetAllCounters();
}
};
checkAndResetIfNeeded();
// Calculate time until midnight
const now = new Date();
const midnight = new Date();
midnight.setHours(24, 0, 0, 0);
const timeUntilMidnight = midnight.getTime() - now.getTime();
// Set timer for midnight reset
const midnightTimer = setTimeout(async () => {
await resetAllCounters();
// Set up daily interval after first midnight
const dailyInterval = setInterval(async () => {
await resetAllCounters();
}, 24 * 60 * 60 * 1000); // 24 hours
return () => clearInterval(dailyInterval);
}, timeUntilMidnight);
return () => clearTimeout(midnightTimer);
}, []);
return (
<View style={styles.container}>
<ScrollView
contentContainerStyle={styles.scrollContent}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={theme.colors.primary} />
}
>
{/* Header Section */}
<View style={styles.header}>
<View>
<Text style={styles.greeting}>{getGreeting()},</Text>
<Text style={styles.name}>{user?.firstName || "Athlete"}</Text>
</View>
<View style={styles.avatarContainer}>
{user?.imageUrl ? (
<Image source={{ uri: user.imageUrl }} style={styles.avatar} />
) : (
<View style={styles.placeholderAvatar}>
<Ionicons name="person" size={24} color="#fff" />
</View>
)}
</View>
</View>
{/* Activity Widget */}
<ActivityWidget
steps={8432}
calories={calories}
duration={45}
/>
{/* Hydration Widget */}
<HydrationWidget
current={waterIntake}
goal={2500}
/>
{/* Quick Actions */}
<QuickActionGrid
onTrackMealPress={() => setTrackMealModalVisible(true)}
onAddWaterPress={() => setAddWaterModalVisible(true)}
onScanFoodPress={() => setScanFoodModalVisible(true)}
/>
<TrackMealModal
visible={trackMealModalVisible}
onClose={() => setTrackMealModalVisible(false)}
onSave={handleSaveMeal}
onResetData={handleResetCalories}
/>
<AddWaterModal
visible={addWaterModalVisible}
onClose={() => setAddWaterModalVisible(false)}
onAdd={handleAddWater}
onResetData={handleResetWater}
/>
<ScanFoodModal
visible={scanFoodModalVisible}
onClose={() => setScanFoodModalVisible(false)}
onAddFood={handleAddScannedFood}
/>
{/* Recent Activity Section */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Recent Activity</Text>
<Text style={styles.seeAll}>See All</Text>
</View>
<View style={styles.activityCard}>
<LinearGradient
colors={['rgba(255, 255, 255, 0.8)', 'rgba(255, 255, 255, 0.5)']}
style={[styles.recentItem, theme.shadows.subtle]}
>
<View style={styles.recentIconContainer}>
<LinearGradient
colors={theme.gradients.primary}
style={styles.recentIcon}
>
<Ionicons name="barbell" size={20} color="#fff" />
</LinearGradient>
</View>
<View style={styles.recentInfo}>
<Text style={styles.recentTitle}>Upper Body Power</Text>
<Text style={styles.recentSubtitle}>Today, 10:00 AM</Text>
</View>
<Text style={styles.recentValue}>45m</Text>
</LinearGradient>
<LinearGradient
colors={['rgba(255, 255, 255, 0.8)', 'rgba(255, 255, 255, 0.5)']}
style={[styles.recentItem, theme.shadows.subtle]}
>
<View style={styles.recentIconContainer}>
<LinearGradient
colors={theme.gradients.success}
style={styles.recentIcon}
>
<Ionicons name="bicycle" size={20} color="#fff" />
</LinearGradient>
</View>
<View style={styles.recentInfo}>
<Text style={styles.recentTitle}>Morning Cardio</Text>
<Text style={styles.recentSubtitle}>Yesterday, 7:30 AM</Text>
</View>
<Text style={styles.recentValue}>30m</Text>
</LinearGradient>
</View>
</View>
{/* Bottom Spacer for Tab Bar */}
<View style={{ height: 100 }} />
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
scrollContent: {
paddingTop: 60,
},
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 24,
marginBottom: 32,
},
greeting: {
fontSize: 16,
color: theme.colors.gray600,
fontWeight: "500",
marginBottom: 4,
},
name: {
fontSize: 32,
fontWeight: "800",
color: theme.colors.gray900,
letterSpacing: -0.5,
},
avatarContainer: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 5,
},
avatar: {
width: 56,
height: 56,
borderRadius: 20,
borderWidth: 2,
borderColor: "#fff",
},
placeholderAvatar: {
width: 56,
height: 56,
borderRadius: 20,
backgroundColor: theme.colors.primary,
justifyContent: "center",
alignItems: "center",
borderWidth: 2,
borderColor: "#fff",
},
section: {
paddingHorizontal: 20,
marginBottom: 24,
},
sectionHeader: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: "700",
color: theme.colors.gray900,
},
seeAll: {
fontSize: 14,
color: theme.colors.primary,
fontWeight: "600",
},
activityCard: {
gap: 12,
},
recentItem: {
flexDirection: "row",
alignItems: "center",
padding: 16,
borderRadius: 20,
backgroundColor: "#fff",
borderWidth: 1,
borderColor: "rgba(255, 255, 255, 0.6)",
},
recentIconContainer: {
marginRight: 16,
},
recentIcon: {
width: 48,
height: 48,
borderRadius: 16,
justifyContent: "center",
alignItems: "center",
},
recentInfo: {
flex: 1,
},
recentTitle: {
fontSize: 16,
fontWeight: "600",
color: theme.colors.gray900,
marginBottom: 4,
},
recentSubtitle: {
fontSize: 12,
color: theme.colors.gray500,
},
recentValue: {
fontSize: 14,
fontWeight: "600",
color: theme.colors.gray900,
},
});