import { View, Text, StyleSheet, ScrollView, RefreshControl, Image, Animated, TouchableOpacity, } from "react-native"; import { useUser } from "@clerk/clerk-expo"; import { useState, useCallback, useEffect, useRef, useMemo } from "react"; import { useFocusEffect } from "@react-navigation/native"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { Ionicons } from "@expo/vector-icons"; import { useTheme } from "../../contexts/ThemeContext"; import { useStatistics } from "../../contexts/StatisticsContext"; import { useFitnessGoals } from "../../contexts/FitnessGoalsContext"; import { MinimalCard } from "../../components/MinimalCard"; import { SectionHeader } from "../../components/SectionHeader"; import { IconContainer } from "../../components/IconContainer"; import { ProgressBar } from "../../components/ProgressBar"; import { TrackMealModal } from "../../components/TrackMealModal"; import { AddWaterModal } from "../../components/AddWaterModal"; import { ScanFoodModal } from "../../components/ScanFoodModal"; import { ActivityRing } from "../../components/ActivityRing"; import { checkInsToActivities, completedGoalsToActivities, newGoalsToActivities, combineActivities, getRecentActivities, formatActivityTime, formatDuration, getActivityIcon, getActivityEmoji, ActivityType, } from "../../utils/activityFeed"; const CALORIE_GOAL = 2000; const WATER_GOAL = 2000; const WORKOUT_GOAL = 3; export default function HomeScreen() { const { user } = useUser(); const { colors, typography } = useTheme(); const { refetchStatistics, forceRefresh, statistics, loading } = useStatistics(); const { goals, refetchGoals } = useFitnessGoals(); 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 caloriesBounce = useRef(new Animated.Value(1)).current; const waterBounce = useRef(new Animated.Value(1)).current; useFocusEffect( useCallback(() => { refetchStatistics(); refetchGoals(); }, [refetchStatistics, refetchGoals]), ); const onRefresh = useCallback(async () => { setRefreshing(true); await Promise.all([forceRefresh(), refetchGoals()]); setRefreshing(false); }, [forceRefresh, refetchGoals]); const getGreeting = () => { const hour = new Date().getHours(); if (hour < 12) return "Good Morning"; if (hour < 18) return "Good Afternoon"; return "Good Evening"; }; const getMotivationalMessage = () => { const messages = [ "Let's crush it today! 💪", "Ready to level up? 🔥", "You've got this! ⚡", "Time to shine! ✨", "Let's make it happen! 🚀", ]; return messages[Math.floor(Math.random() * messages.length)]; }; const handleSaveMeal = (meal: { type: string; name: string; calories: number; }) => { setCalories((prev) => prev + meal.calories); setTrackMealModalVisible(false); Animated.sequence([ Animated.timing(caloriesBounce, { toValue: 1.15, duration: 150, useNativeDriver: true, }), Animated.timing(caloriesBounce, { toValue: 1, duration: 150, useNativeDriver: true, }), ]).start(); }; const handleAddWater = (amount: number) => { setWaterIntake((prev) => prev + amount); setAddWaterModalVisible(false); Animated.sequence([ Animated.timing(waterBounce, { toValue: 1.15, duration: 150, useNativeDriver: true, }), Animated.timing(waterBounce, { toValue: 1, duration: 150, useNativeDriver: true, }), ]).start(); }; 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); await AsyncStorage.removeItem(`calories_${today}`); await AsyncStorage.removeItem(`water_${today}`); }; useEffect(() => { const loadPersistedData = async () => { const today = new Date().toDateString(); const storedCalories = await AsyncStorage.getItem(`calories_${today}`); const storedWater = await AsyncStorage.getItem(`water_${today}`); if (storedCalories) setCalories(parseInt(storedCalories, 10)); if (storedWater) setWaterIntake(parseInt(storedWater, 10)); }; loadPersistedData(); }, []); useEffect(() => { const persistCalories = async () => { const today = new Date().toDateString(); await AsyncStorage.setItem(`calories_${today}`, calories.toString()); }; persistCalories(); }, [calories]); useEffect(() => { const persistWater = async () => { const today = new Date().toDateString(); await AsyncStorage.setItem(`water_${today}`, waterIntake.toString()); }; persistWater(); }, [waterIntake]); useEffect(() => { const checkAndResetIfNeeded = async () => { const lastResetDate = await AsyncStorage.getItem("lastResetDate"); const today = new Date().toDateString(); if (lastResetDate !== today) { await resetAllCounters(); } }; checkAndResetIfNeeded(); }, []); const checkInsThisWeek = statistics?.attendance.checkInsThisWeek || 0; const currentStreak = statistics?.attendance.currentStreak || 0; const workoutsThisWeek = checkInsThisWeek; const recentActivities = useMemo(() => { if (!statistics || !goals) return []; const checkInActivities = checkInsToActivities(statistics.attendance).slice( 0, 2, ); const completedGoalActivities = completedGoalsToActivities(goals).slice( 0, 2, ); const newGoalActivities = newGoalsToActivities(goals, 7); const allActivities = combineActivities( checkInActivities, completedGoalActivities, newGoalActivities, ); return getRecentActivities(allActivities, 5); }, [statistics, goals]); return ( } > {/* Hero Header */} {getGreeting()} 👋 {user?.firstName || "Champion"} {getMotivationalMessage()} {user?.imageUrl ? ( ) : ( )} {/* Activity Rings - Hero Section */} {calories} } /> {waterIntake} } /> {workoutsThisWeek} } /> {/* Streak Banner */} {currentStreak >= 1 && ( 🔥 {currentStreak} Day Streak! Keep it going )} {/* Quick Actions */} console.log("Log workout")} activeOpacity={0.85} style={[ styles.quickActionCard, { backgroundColor: colors.primary }, ]} > Workout Log your session setTrackMealModalVisible(true)} activeOpacity={0.85} style={[ styles.quickActionCard, { backgroundColor: colors.success }, ]} > Meal Track calories setAddWaterModalVisible(true)} activeOpacity={0.85} style={[styles.quickActionCard, { backgroundColor: colors.info }]} > Hydrate Add water setScanFoodModalVisible(true)} activeOpacity={0.85} style={[ styles.quickActionCard, { backgroundColor: colors.accent }, ]} > Scan Quick add {/* Today's Progress */} 🔥 Calories {calories >= CALORIE_GOAL ? "Goal reached! 🎉" : `${CALORIE_GOAL - calories} remaining`} {calories}/{CALORIE_GOAL} 💧 Hydration {waterIntake >= WATER_GOAL ? "Fully hydrated! 🌊" : `${WATER_GOAL - waterIntake}ml remaining`} {waterIntake}/{WATER_GOAL} {/* Recent Activity */} {recentActivities.length === 0 ? ( 🏃 No Activity Yet Start by checking in at the gym or completing a goal! ) : ( {recentActivities.map((activity) => { const iconName = getActivityIcon(activity.type); const emoji = getActivityEmoji(activity.type); const timeStr = formatActivityTime(activity.timestamp); let iconColor = colors.primary; if (activity.type === ActivityType.GOAL_COMPLETED) iconColor = colors.success; else if (activity.type === ActivityType.GOAL_CREATED) iconColor = colors.accent; return ( {emoji} {activity.title} {timeStr} {activity.duration && ( {formatDuration(activity.duration)} )} ); })} )} setTrackMealModalVisible(false)} onSave={handleSaveMeal} onResetData={handleResetCalories} /> setAddWaterModalVisible(false)} onAdd={handleAddWater} onResetData={handleResetWater} /> setScanFoodModalVisible(false)} onAddFood={handleAddScannedFood} /> ); } const styles = StyleSheet.create({ container: { flex: 1 }, scrollContent: { paddingTop: 60, paddingBottom: 20 }, section: { paddingHorizontal: 20, marginBottom: 28 }, // Hero Header heroHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingHorizontal: 20, marginBottom: 24, }, heroTextContainer: { flex: 1, marginRight: 16 }, heroAvatar: { width: 64, height: 64, borderRadius: 20, borderWidth: 3, borderColor: "#0066FF", }, heroPlaceholderAvatar: { width: 64, height: 64, borderRadius: 20, justifyContent: "center", alignItems: "center", borderWidth: 3, borderColor: "#0066FF", }, // Activity Rings ringsCard: { padding: 24 }, ringsContainer: { flexDirection: "row", justifyContent: "space-around", alignItems: "center", }, streakBanner: { flexDirection: "row", alignItems: "center", marginTop: 24, padding: 16, borderRadius: 14, }, // Quick Actions quickActionsGrid: { flexDirection: "row", flexWrap: "wrap", gap: 12 }, quickActionCard: { width: "47%", padding: 20, borderRadius: 20, }, quickActionIcon: { width: 52, height: 52, borderRadius: 14, justifyContent: "center", alignItems: "center", }, // Progress Cards progressCard: { padding: 20 }, progressHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", }, progressLabelRow: { flexDirection: "row", alignItems: "center" }, progressIcon: { width: 48, height: 48, borderRadius: 14, justifyContent: "center", alignItems: "center", }, // Activity activityList: { gap: 12 }, activityItem: { flexDirection: "row", alignItems: "center", padding: 16 }, activityIconContainer: { width: 48, height: 48, borderRadius: 14, justifyContent: "center", alignItems: "center", }, activityInfo: { flex: 1, marginLeft: 14 }, // Empty emptyCard: { alignItems: "center", padding: 40 }, });