diff --git a/app/food/[id].tsx b/app/food/[id].tsx index 726b3da..46586eb 100644 --- a/app/food/[id].tsx +++ b/app/food/[id].tsx @@ -1,43 +1,32 @@ -"use client"; - -import { IconSymbol } from "@/components/ui/IconSymbol"; -import { - getFoodById, - getIngredients, - getNutrients, -} from "@/services/data/foods"; -import { supabase } from "@/services/supabase"; -import { Foods } from "@/types"; -import { Ingredient, Nutrient } from "@/types/index"; -import { Feather } from "@expo/vector-icons"; -import { useQuery } from "@tanstack/react-query"; -import { Image } from "expo-image"; -import { router, useLocalSearchParams } from "expo-router"; -import { useState } from "react"; -import { - KeyboardAvoidingView, - Platform, - ScrollView, - Text, - TouchableOpacity, - View, -} from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; +"use client" +import { getFoodById, getIngredients, getNutrients } from "@/services/data/foods" +import { supabase } from "@/services/supabase" +import type { Foods } from "@/types" +import type { Ingredient, Nutrient } from "@/types/index" +import { Feather, MaterialCommunityIcons, Ionicons } from "@expo/vector-icons" +import { useQuery } from "@tanstack/react-query" +import { Image } from "expo-image" +import { router, useLocalSearchParams } from "expo-router" +import { useRef } from "react" +import { ScrollView, Text, TouchableOpacity, View, ActivityIndicator, Animated, Dimensions } from "react-native" +import { LinearGradient } from "expo-linear-gradient" interface Step { - id: string; - food_id: string; - title: string; - step_order: number; - description: string; - created_at: string; + id: string + food_id: string + title: string + step_order: number + description: string + created_at: string } -export default function FoodDetailScreen() { - const { id } = useLocalSearchParams(); - const [activeTab, setActiveTab] = useState("Ingredients"); +const { width } = Dimensions.get("window") - const foodId = typeof id === "string" ? id : ""; +export default function FoodDetailScreen() { + const { id } = useLocalSearchParams() + const scrollY = useRef(new Animated.Value(0)).current + + const foodId = typeof id === "string" ? id : "" const { data: foodData, @@ -46,13 +35,13 @@ export default function FoodDetailScreen() { } = useQuery({ queryKey: ["food-detail", foodId], queryFn: async () => { - const { data, error } = await getFoodById(foodId); - if (error) throw error; - if (!data) throw new Error("Food not found"); - return data; + const { data, error } = await getFoodById(foodId) + if (error) throw error + if (!data) throw new Error("Food not found") + return data }, enabled: !!foodId, - }); + }) const { data: nutrients, @@ -61,12 +50,12 @@ export default function FoodDetailScreen() { } = useQuery({ queryKey: ["food-nutrients", foodId], queryFn: async () => { - const { data, error } = await getNutrients(foodId); - if (error) throw error; - return data; + const { data, error } = await getNutrients(foodId) + if (error) throw error + return data }, enabled: !!foodId && !!foodData, - }); + }) const { data: ingredients, @@ -75,12 +64,12 @@ export default function FoodDetailScreen() { } = useQuery({ queryKey: ["food-ingredients", foodId], queryFn: async () => { - const { data, error } = await getIngredients(foodId); - if (error) throw error; - return data ?? []; + const { data, error } = await getIngredients(foodId) + if (error) throw error + return data ?? [] }, enabled: !!foodId && !!foodData, - }); + }) const { data: steps, @@ -99,321 +88,314 @@ export default function FoodDetailScreen() { step_order, description, created_at - ` + `, ) .eq("food_id", foodId) - .order("step_order", { ascending: true }); - if (error) throw error; - return data ?? []; + .order("step_order", { ascending: true }) + if (error) throw error + return data ?? [] }, enabled: !!foodId && !!foodData, - }); + }) + + // Calculate header opacity based on scroll position + const headerOpacity = scrollY.interpolate({ + inputRange: [0, 100, 150], + outputRange: [0, 0.5, 1], + extrapolate: "clamp", + }) + + // Calculate image scale based on scroll position + const imageScale = scrollY.interpolate({ + inputRange: [-100, 0, 100], + outputRange: [1.2, 1, 0.8], + extrapolate: "clamp", + }) if (isLoading || stepsLoading || nutrientsLoading || ingredientsLoading) { return ( - - - Loading... - - - ); + + + Loading recipe details... + + ) } if (error || !foodData || ingredientsError || stepsError || nutrientsError) { return ( - - - Error loading food details - router.push("/home")} - > - - Go back to home page - - - - - ); + + + Oops! Something went wrong + + We couldn't load the recipe details. Please try again later. + + router.push("/home")}> + Go back to home + + + ) } const startCookingSession = () => { - // Corrected router push to use the actual foodId - router.push(`/cooking/${foodId}`); - }; + router.push(`/cooking/${foodId}`) + } + + // Recipe info cards data + const recipeInfoCards = [ + { + id: "skill_level", + title: "Skill Level", + icon: , + value: foodData.skill_level || "Easy", + color: "#4CAF50", + }, + { + id: "cooking_time", + title: "Cooking Time", + icon: , + value: `${foodData.time_to_cook_minutes || 0} min`, + color: "#bb0718", + }, + { + id: "ingredients", + title: "Ingredients", + icon: , + value: `${foodData.ingredient_count ?? ingredients?.length ?? 0}`, + color: "#2196F3", + }, + { + id: "calories", + title: "Calories", + icon: , + value: `${foodData.calories || 0} kcal`, + color: "#F44336", + }, + ] return ( - - + {/* Animated header background */} + + + {/* Header with back and share buttons */} + + router.back()} + > + + + + + + + + - - {/* Header with back and share buttons */} - - router.back()} - > - - - - - - - - {/* Food Image */} - - - {foodData.image_url ? ( - - ) : ( - - Image not available - - )} - - - - {/* Food Title and Description */} - - - {foodData.name} - - - {foodData.description} - - - {/* Info Tabs */} - - setActiveTab("Skills")} - > - Skills - - {foodData.skill_level} - - - setActiveTab("Time")} - > - Time - - {foodData.time_to_cook_minutes} - - - setActiveTab("Ingredients")} - > - Ingredients - - {/* Use ingredient_count from foodData or length of the fetched ingredients array */} - {foodData.ingredient_count ?? ingredients?.length ?? 0} - - - setActiveTab("Calories")} - > - Calories - - {foodData.calories} - - - - - {/* Ingredients Section */} - - - Ingredients - - - {(ingredients ?? []).map( - // Use the 'ingredients' state variable - ( - ingredient: Ingredient, - index: number // Added type for ingredient - ) => ( - - - {ingredient.emoji} - - - {ingredient.name} - - - ) - )} - {/* You might want to show a loading/empty state for ingredients here too */} - {/*!ingredientsLoading && ingredients?.length === 0 && ( - No ingredients listed. - )*/} + {/* Food Image with gradient overlay */} + + + + + {foodData.name} + + + {foodData.skill_level || "Easy"} + + + {foodData.time_to_cook_minutes || 0} min + + - {/* Nutrition Section - Improved UI */} - - - Nutrition Facts - - {/* Conditionally render nutrients or show placeholder/loading */} - {nutrients ? ( - + + {/* Description */} + {foodData.description && ( + {foodData.description} + )} + + {/* Recipe Info Cards - Horizontal Scrollable */} + + + {recipeInfoCards.map((card) => ( + - - - {nutrients.fat_g ?? 0} - - - g - - - - Fat - - - - - - {nutrients.fiber_g ?? 0} - - - g - - - - Fiber - - - - - - {nutrients.protein_g ?? 0} - - - g - - - - Protein - - - - - - {nutrients.carbs_g ?? 0} - - - g - - - - Carbs + {card.icon} + {card.title} + + {card.value} + ))} + + + + {/* Ingredients Section */} + + Ingredients + + {ingredients && ingredients.length > 0 ? ( + + {ingredients.map((ingredient, index) => ( + + + {ingredient.emoji || "🍴"} + + {ingredient.name} + + ))} + ) : ( - - Nutrition facts not available. - + + + No ingredients listed + )} + - {/* Steps Preview */} - - - Cooking Steps - - - {steps && steps.length > 0 ? ( - steps.slice(0, 2).map( - ( - step: Step, - index: number // Added type for step - ) => ( - - - - {step.step_order ?? index + 1}{" "} - {/* Use step_order or fallback to index */} - - - - {step.description || step.title}{" "} - {/* Display description or title */} - - - ) - ) - ) : ( - - No cooking steps listed - - )} - {steps && steps.length > 2 && ( - - ...and {steps.length - 2} more steps - - )} + {/* Nutrition Section */} + {nutrients && ( + + Nutrition Facts + + + + + + + - - + )} - {/* Cook Button */} + {/* Steps Preview */} + + Cooking Steps + + {steps && steps.length > 0 ? ( + + {steps.slice(0, 3).map((step, index) => ( + + + {step.step_order ?? index + 1} + + + {step.title && {step.title}} + + {step.description || "No description available"} + + + + ))} + {steps.length > 3 && ( + + View all {steps.length} steps + + )} + + ) : ( + + + No cooking steps available + + )} + + + + + + {/* Cook Button */} + - - Let's Cook! - - + Let's Cook! + - - - ); + + + ) +} + +// Helper component for nutrition facts +function NutrientCircle({ + value, + label, + color, + bgColor, +}: { value: number; label: string; color: string; bgColor: string }) { + return ( + + + + {value}g + + + {label} + + ) }