diff --git a/app/(tabs)/forum.tsx b/app/(tabs)/forum.tsx index cbf25b5..9e53617 100644 --- a/app/(tabs)/forum.tsx +++ b/app/(tabs)/forum.tsx @@ -1,153 +1,146 @@ -import React, { useState, useEffect } from 'react'; -import { View, Text, Image, TextInput, TouchableOpacity, FlatList, SafeAreaView, ActivityIndicator, Alert } from 'react-native'; -import { Feather, FontAwesome } from '@expo/vector-icons'; -import { router, useFocusEffect } from 'expo-router'; -import { useAuth } from '../../context/auth-context'; -import { supabase } from '../../services/supabase'; -import { - useFoods, - useFoodStats, - useFoodCreators, +"use client" + +import React, { useState, useEffect } from "react" +import { + View, + Text, + Image, + TextInput, + TouchableOpacity, + FlatList, + SafeAreaView, + ActivityIndicator, + Alert, +} from "react-native" +import { Feather, FontAwesome } from "@expo/vector-icons" +import { router, useFocusEffect } from "expo-router" +import { useAuth } from "../../context/auth-context" +import { supabase } from "../../services/supabase" +import { + useFoods, + useFoodStats, + useFoodCreators, useUserInteractions, useLikeMutation, - useSaveMutation -} from '../../hooks/use-foods'; - -// Categories for filtering -const categories = [ - { id: 'main', name: 'Main dish' }, - { id: 'dessert', name: 'Dessert' }, - { id: 'appetizer', name: 'Appetite' }, -]; + useSaveMutation, +} from "../../hooks/use-foods" // Sort options const sortOptions = [ - { id: 'rating', name: 'Rating', icon: 'star' }, - { id: 'newest', name: 'Newest', icon: 'calendar' }, - { id: 'best', name: 'Best', icon: 'fire' }, -]; + { id: "newest", name: "Newest", icon: "calendar" }, + { id: "like_desc", name: "Most Liked", icon: "heart" }, +] export default function ForumScreen() { - const { isAuthenticated } = useAuth(); - const [currentUserId, setCurrentUserId] = useState(null); - const [searchQuery, setSearchQuery] = useState(''); - const [selectedCategory, setSelectedCategory] = useState(''); - const [selectedSort, setSelectedSort] = useState('rating'); - + const { isAuthenticated } = useAuth() + const [currentUserId, setCurrentUserId] = useState(null) + const [searchQuery, setSearchQuery] = useState("") + const [selectedCategory, setSelectedCategory] = useState("") + const [selectedSort, setSelectedSort] = useState("newest") + // 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); + const { data } = await supabase.auth.getSession() + const userId = data.session?.user?.id + console.log("Current user ID:", userId) + setCurrentUserId(userId || null) } else { - setCurrentUserId(null); + setCurrentUserId(null) } } - - getCurrentUser(); - }, [isAuthenticated]); - + + getCurrentUser() + }, [isAuthenticated]) + // Use React Query hooks - const { - data: foods = [], + const { + data: foods = [], isLoading: isLoadingFoods, - refetch: refetchFoods - } = useFoods(selectedCategory, searchQuery, selectedSort); - - const foodIds = foods.map(food => food.id); - - const { - data: foodStats = {}, - isLoading: isLoadingStats - } = useFoodStats(foodIds); - - const creatorIds = foods - .filter(food => food.created_by) - .map(food => food.created_by as string); - - const { - data: foodCreators = {}, - isLoading: isLoadingCreators - } = useFoodCreators(creatorIds); - - const { - data: userInteractions = {}, - isLoading: isLoadingInteractions - } = useUserInteractions(foodIds, currentUserId); - - const likeMutation = useLikeMutation(); - const saveMutation = useSaveMutation(); - + refetch: refetchFoods, + } = useFoods(selectedCategory, searchQuery, selectedSort) + + const foodIds = foods.map((food) => food.id) + + const { data: foodStats = {}, isLoading: isLoadingStats } = useFoodStats(foodIds) + + const creatorIds = foods.filter((food) => food.created_by).map((food) => food.created_by as string) + + const { data: foodCreators = {}, isLoading: isLoadingCreators } = useFoodCreators(creatorIds) + + const { data: userInteractions = {}, isLoading: isLoadingInteractions } = useUserInteractions(foodIds, currentUserId) + + const likeMutation = useLikeMutation() + const saveMutation = useSaveMutation() + // Refetch data when the screen comes into focus useFocusEffect( React.useCallback(() => { - refetchFoods(); - }, [refetchFoods]) - ); - + refetchFoods() + }, [refetchFoods]), + ) + const handleSearch = (text: string) => { - setSearchQuery(text); - }; - + setSearchQuery(text) + } + const navigateToPostDetail = (food: { id: string }) => { - router.push(`/post-detail/${food.id}`); - }; - + router.push(`/post-detail/${food.id}`) + } + const handleLike = async (food: { id: string }) => { if (!isAuthenticated || !currentUserId) { - Alert.alert('Authentication Required', 'Please log in to like posts.'); - return; + Alert.alert("Authentication Required", "Please log in to like posts.") + return } - + try { - const isLiked = userInteractions[food.id]?.liked || false; - + const isLiked = userInteractions[food.id]?.liked || false + likeMutation.mutate({ foodId: food.id, userId: currentUserId, - isLiked - }); + isLiked, + }) } catch (error) { - console.error('Error toggling like:', error); - Alert.alert('Error', 'Failed to update like. Please try again.'); + console.error("Error toggling like:", error) + Alert.alert("Error", "Failed to update like. Please try again.") } - }; - + } + const handleSave = async (food: { id: string }) => { if (!isAuthenticated || !currentUserId) { - Alert.alert('Authentication Required', 'Please log in to save posts.'); - return; + Alert.alert("Authentication Required", "Please log in to save posts.") + return } - + try { - const isSaved = userInteractions[food.id]?.saved || false; - + const isSaved = userInteractions[food.id]?.saved || false + saveMutation.mutate({ foodId: food.id, userId: currentUserId, - isSaved - }); + isSaved, + }) } catch (error) { - console.error('Error toggling save:', error); - Alert.alert('Error', 'Failed to update save. Please try again.'); + console.error("Error toggling save:", error) + Alert.alert("Error", "Failed to update save. Please try again.") } - }; - + } + const renderFoodItem = ({ item }: { item: any }) => { // Get stats for this food - const stats = foodStats[item.id] || { likes: 0, saves: 0, comments: 0 }; - + const stats = foodStats[item.id] || { likes: 0, saves: 0, comments: 0 } + // Get creator profile - const creator = item.created_by ? foodCreators[item.created_by] : null; - + const creator = item.created_by ? foodCreators[item.created_by] : null + // Get user interactions - const interactions = userInteractions[item.id] || { liked: false, saved: false }; - + const interactions = userInteractions[item.id] || { liked: false, saved: false } + return ( - navigateToPostDetail(item)} > @@ -157,90 +150,72 @@ export default function ForumScreen() { {creator?.avatar_url ? ( - + ) : ( - {creator?.username?.charAt(0).toUpperCase() || '?'} + {creator?.username?.charAt(0).toUpperCase() || "?"} )} - {creator?.username || creator?.full_name || 'Unknown Chef'} + {creator?.username || creator?.full_name || "Unknown Chef"} - - 4.2 - - - + {/* Food image */} - - + {/* Food title and description */} {item.name} {item.description} - + {/* Interaction buttons */} - { - e.stopPropagation(); - handleLike(item); + e.stopPropagation() + handleLike(item) }} > - + {stats.likes} - - navigateToPostDetail(item)} - > + + navigateToPostDetail(item)}> {stats.comments} - + { - e.stopPropagation(); - handleSave(item); + e.stopPropagation() + handleSave(item) }} > - + - ); - }; - - const isLoading = isLoadingFoods || isLoadingStats || isLoadingCreators || isLoadingInteractions; - + ) + } + + const isLoading = isLoadingFoods || isLoadingStats || isLoadingCreators || isLoadingInteractions + return ( {/* Search Bar */} @@ -255,25 +230,8 @@ export default function ForumScreen() { /> - - {/* Categories */} - - item.id} - renderItem={({ item }) => ( - setSelectedCategory(item.id === selectedCategory ? '' : item.id)} - > - {item.name} - - )} - /> - - + + {/* Sort Options */} item.id} renderItem={({ item }) => ( - setSelectedSort(item.id)} > - {item.name} - + + {item.name} + + )} /> - + {/* Food Posts */} {isLoading ? ( @@ -308,5 +270,5 @@ export default function ForumScreen() { /> )} - ); -} \ No newline at end of file + ) +} diff --git a/hooks/use-foods.ts b/hooks/use-foods.ts index 2b36b2a..d1cd916 100644 --- a/hooks/use-foods.ts +++ b/hooks/use-foods.ts @@ -47,6 +47,21 @@ export function useFoods(category?: string, search?: string, sort?: string) { sortedData.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); } else if (sort === 'best') { sortedData.sort((a, b) => (b.ingredient_count ?? 0) - (a.ingredient_count ?? 0)); + } else if (sort === 'like_desc') { + // First, we need to get likes count for each food + const likesPromises = sortedData.map(async (food) => { + const { count } = await getLikesCount(food.id); + return { foodId: food.id, likes: count || 0 }; + }); + + const likesData = await Promise.all(likesPromises); + const likesMap = likesData.reduce((acc, item) => { + acc[item.foodId] = item.likes; + return acc; + }, {} as Record); + + // Sort by likes count (high to low) + sortedData.sort((a, b) => (likesMap[b.id] || 0) - (likesMap[a.id] || 0)); } return sortedData.map(food => ({ @@ -284,4 +299,4 @@ export function useSaveMutation() { queryClient.invalidateQueries({ queryKey: [queryKeys.userInteractions] }); }, }); -} \ No newline at end of file +}