import React, { useState, useEffect } from 'react'; import { View, Text, Image, TouchableOpacity, ScrollView, SafeAreaView, ActivityIndicator, TextInput, KeyboardAvoidingView, Platform, Alert } from 'react-native'; import { Feather, FontAwesome } from '@expo/vector-icons'; import { useLocalSearchParams, router } from 'expo-router'; import { useAuth } from '../context/auth-context'; import { getFoods, getIngredients, getNutrients } from '../services/data/foods'; import { createLike, deleteLike, createSave, deleteSave, getComments, createComment, getLikesCount, getSavesCount, getCommentsCount, checkUserLiked, checkUserSaved } from '../services/data/forum'; import { getProfile } from '../services/data/profile'; import { Food, Ingredient, Nutrient, FoodComment, Profile } from '../types/index'; import { supabase } from '../services/supabase'; export default function PostDetailScreen() { const { id } = useLocalSearchParams(); const foodId = Array.isArray(id) ? id[0] : id; const { isAuthenticated } = useAuth(); const [currentUserId, setCurrentUserId] = useState(null); const [food, setFood] = useState(null); const [foodCreator, setFoodCreator] = useState(null); const [ingredients, setIngredients] = useState([]); const [nutrients, setNutrients] = useState(null); const [comments, setComments] = useState([]); const [loading, setLoading] = useState(true); const [isLiked, setIsLiked] = useState(false); const [isSaved, setIsSaved] = useState(false); const [showReviews, setShowReviews] = useState(true); const [commentText, setCommentText] = useState(''); const [submittingComment, setSubmittingComment] = useState(false); const [stats, setStats] = useState({ likes: 0, saves: 0, comments: 0 }); // Get current user ID from Supabase session useEffect(() => { async function getCurrentUser() { if (isAuthenticated) { const { data } = await supabase.auth.getSession(); const userId = data.session?.user?.id; console.log('Current user ID:', userId); setCurrentUserId(userId || null); } else { setCurrentUserId(null); } } getCurrentUser(); }, [isAuthenticated]); useEffect(() => { if (foodId) { loadFoodDetails(); } }, [foodId]); // Set up real-time subscription for comments useEffect(() => { if (!foodId) return; const subscription = supabase .channel(`food_comments:${foodId}`) .on('postgres_changes', { event: '*', schema: 'public', table: 'food_comments', filter: `food_id=eq.${foodId}` }, () => { // Refresh comments when changes occur refreshComments(); }) .subscribe(); return () => { supabase.removeChannel(subscription); }; }, [foodId]); // Set up real-time subscription for likes useEffect(() => { if (!foodId) return; const subscription = supabase .channel(`food_likes:${foodId}`) .on('postgres_changes', { event: '*', schema: 'public', table: 'food_likes', filter: `food_id=eq.${foodId}` }, () => { // Refresh likes when changes occur refreshLikes(); }) .subscribe(); return () => { supabase.removeChannel(subscription); }; }, [foodId]); // Check if user has liked/saved when user ID changes useEffect(() => { if (foodId && currentUserId && food) { checkUserInteractions(); } }, [currentUserId, foodId, food]); const checkUserInteractions = async () => { if (!currentUserId || !foodId) return; try { console.log('Checking user interactions with user ID:', currentUserId); const [likedRes, savedRes] = await Promise.all([ checkUserLiked(foodId, currentUserId), checkUserSaved(foodId, currentUserId) ]); console.log('User liked:', !!likedRes.data); console.log('User saved:', !!savedRes.data); setIsLiked(!!likedRes.data); setIsSaved(!!savedRes.data); } catch (error) { console.error('Error checking user interactions:', error); } }; const refreshComments = async () => { if (!foodId) return; try { const { data: commentsData } = await getComments(foodId); if (commentsData) { setComments(commentsData); } const { count } = await getCommentsCount(foodId); setStats(prev => ({ ...prev, comments: count || 0 })); } catch (error) { console.error('Error refreshing comments:', error); } }; const refreshLikes = async () => { if (!foodId || !currentUserId) return; try { const { count } = await getLikesCount(foodId); setStats(prev => ({ ...prev, likes: count || 0 })); const { data } = await checkUserLiked(foodId, currentUserId); setIsLiked(!!data); } catch (error) { console.error('Error refreshing likes:', error); } }; const loadFoodDetails = async () => { if (!foodId) return; setLoading(true); try { console.log('Loading food details for ID:', foodId); // Get food details const { data: foodData, error: foodError } = await getFoods(undefined, undefined, undefined, 1, 0); if (foodError) { console.error('Error loading food:', foodError); return; } if (foodData && foodData.length > 0) { const foodItem = { ...foodData[0], description: foodData[0].description || '', ingredient_count: foodData[0].ingredient_count ?? 0, calories: foodData[0].calories ?? 0, image_url: foodData[0].image_url || '', }; console.log('Food loaded:', foodItem.name); setFood(foodItem); // Get food creator profile if (foodItem.created_by) { console.log('Loading creator profile for:', foodItem.created_by); const { data: creatorProfile } = await getProfile(foodItem.created_by); if (creatorProfile) { setFoodCreator(creatorProfile); } } // Get ingredients const { data: ingredientsData, error: ingredientsError } = await getIngredients(foodItem.id); if (!ingredientsError && ingredientsData) { setIngredients(ingredientsData); } // Get nutrients const { data: nutrientsData, error: nutrientsError } = await getNutrients(foodItem.id); if (!nutrientsError && nutrientsData) { setNutrients(nutrientsData); } // Get comments const { data: commentsData, error: commentsError } = await getComments(foodItem.id); if (!commentsError && commentsData) { console.log('Comments loaded:', commentsData.length); setComments(commentsData); } // Get stats const [likesRes, savesRes, commentsRes] = await Promise.all([ getLikesCount(foodItem.id), getSavesCount(foodItem.id), getCommentsCount(foodItem.id) ]); console.log('Stats loaded:', { likes: likesRes.count || 0, saves: savesRes.count || 0, comments: commentsRes.count || 0 }); setStats({ likes: likesRes.count || 0, saves: savesRes.count || 0, comments: commentsRes.count || 0 }); } } catch (error) { console.error('Error loading food details:', error); Alert.alert('Error', 'Failed to load food details. Please try again.'); } finally { setLoading(false); } }; const handleLike = async () => { if (!isAuthenticated || !currentUserId || !food) { Alert.alert('Authentication Required', 'Please log in to like posts.'); return; } try { console.log('Toggling like with user ID:', currentUserId, 'and food ID:', food.id); // Optimistically update UI setIsLiked(!isLiked); setStats(prev => ({ ...prev, likes: isLiked ? Math.max(0, prev.likes - 1) : prev.likes + 1 })); if (isLiked) { const { error } = await deleteLike(food.id, currentUserId); if (error) { console.error('Error deleting like:', error); // Revert optimistic update if there's an error setIsLiked(true); setStats(prev => ({ ...prev, likes: prev.likes + 1 })); Alert.alert('Error', 'Failed to unlike. Please try again.'); } } else { const { error } = await createLike(food.id, currentUserId); if (error) { console.error('Error creating like:', error); // Revert optimistic update if there's an error setIsLiked(false); setStats(prev => ({ ...prev, likes: Math.max(0, prev.likes - 1) })); Alert.alert('Error', 'Failed to like. Please try again.'); } } } catch (error) { console.error('Error toggling like:', error); // Revert optimistic update if there's an error setIsLiked(!isLiked); setStats(prev => ({ ...prev, likes: !isLiked ? Math.max(0, prev.likes - 1) : prev.likes + 1 })); Alert.alert('Error', 'Failed to update like. Please try again.'); } }; const handleSave = async () => { if (!isAuthenticated || !currentUserId || !food) { Alert.alert('Authentication Required', 'Please log in to save posts.'); return; } try { console.log('Toggling save with user ID:', currentUserId, 'and food ID:', food.id); // Optimistically update UI setIsSaved(!isSaved); setStats(prev => ({ ...prev, saves: isSaved ? Math.max(0, prev.saves - 1) : prev.saves + 1 })); if (isSaved) { const { error } = await deleteSave(food.id, currentUserId); if (error) { console.error('Error deleting save:', error); // Revert optimistic update if there's an error setIsSaved(true); setStats(prev => ({ ...prev, saves: prev.saves + 1 })); Alert.alert('Error', 'Failed to unsave. Please try again.'); } } else { const { error } = await createSave(food.id, currentUserId); if (error) { console.error('Error creating save:', error); // Revert optimistic update if there's an error setIsSaved(false); setStats(prev => ({ ...prev, saves: Math.max(0, prev.saves - 1) })); Alert.alert('Error', 'Failed to save. Please try again.'); } } } catch (error) { console.error('Error toggling save:', error); // Revert optimistic update if there's an error setIsSaved(!isSaved); setStats(prev => ({ ...prev, saves: !isSaved ? Math.max(0, prev.saves - 1) : prev.saves + 1 })); Alert.alert('Error', 'Failed to update save. Please try again.'); } }; const handleSubmitComment = async () => { if (!isAuthenticated || !currentUserId || !food || !commentText.trim()) { if (!isAuthenticated || !currentUserId) { Alert.alert('Authentication Required', 'Please log in to comment.'); } return; } setSubmittingComment(true); try { console.log('Submitting comment with user ID:', currentUserId, 'and food ID:', food.id); const { error } = await createComment(food.id, currentUserId, commentText.trim()); if (error) { console.error('Error creating comment:', error); Alert.alert('Error', 'Failed to submit comment. Please try again.'); return; } // Clear comment text setCommentText(''); // Refresh comments await refreshComments(); console.log('Comment submitted successfully'); } catch (error) { console.error('Error submitting comment:', error); Alert.alert('Error', 'Failed to submit comment. Please try again.'); } finally { setSubmittingComment(false); } }; if (loading) { return ( ); } if (!food) { return ( Post not found router.back()} > Go Back ); } return ( {/* Header */} router.back()} > Post {/* User info and rating */} {foodCreator?.avatar_url ? ( ) : ( {foodCreator?.username?.charAt(0).toUpperCase() || '?'} )} {foodCreator?.username || foodCreator?.full_name || 'Unknown Chef'} 4.2 {/* Food image */} {/* Food title and description */} {food.name} {food.description} {new Date(food.created_at).toLocaleDateString()} - {new Date(food.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {/* Interaction buttons */} {stats.comments} {stats.comments} {stats.likes} {/* Reviews section */} setShowReviews(!showReviews)} > Review {showReviews && ( {comments.length > 0 ? ( comments.map((comment) => ( {comment.user?.avatar_url ? ( ) : ( {comment.user?.username?.charAt(0).toUpperCase() || '?'} )} {comment.user?.username || comment.user?.full_name || 'Unknown User'} {new Date(comment.created_at).toLocaleDateString()} {comment.content} )) ) : ( No reviews yet. Be the first to comment! )} )} {/* Bottom spacing */} {/* Comment input */} {!isAuthenticated && ( Please log in to comment )} ); }