Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 22x 22x 10x 7x 7x 3x 3x 22x 22x 22x 1x 22x | import { useState, useEffect, useCallback } from 'react'
import { toast } from 'sonner'
import { api, type Profile, type RecipeDetail } from '../api/client'
interface UseFavoritesReturn {
favoriteRecipeIds: Set<number>
toggleFavorite: (recipe: RecipeDetail) => Promise<void>
isFavorite: (recipeId: number) => boolean
clearFavorites: () => void
}
/**
* Manages favorite recipe state: loading, toggling, and checking favorites.
*/
export function useFavorites(profile: Profile | null): UseFavoritesReturn {
const [favoriteRecipeIds, setFavoriteRecipeIds] = useState<Set<number>>(new Set())
useEffect(() => {
if (!profile) {
Promise.resolve().then(() => setFavoriteRecipeIds(new Set()))
return
}
api.favorites
.list()
.then((favorites) => setFavoriteRecipeIds(new Set(favorites.map((f) => f.recipe.id))))
.catch((error) => console.error('Failed to load favorites:', error))
}, [profile])
const toggleFavorite = useCallback(async (recipe: RecipeDetail) => {
const isFav = favoriteRecipeIds.has(recipe.id)
try {
if (isFav) {
await api.favorites.remove(recipe.id)
setFavoriteRecipeIds((prev) => {
const next = new Set(prev)
next.delete(recipe.id)
return next
})
toast.success('Removed from favorites')
} else {
await api.favorites.add(recipe.id)
setFavoriteRecipeIds((prev) => new Set(prev).add(recipe.id))
toast.success('Added to favorites')
}
} catch (error) {
console.error('Failed to toggle favorite:', error)
toast.error('Failed to update favorites')
}
}, [favoriteRecipeIds])
const isFavorite = useCallback((recipeId: number) => {
return favoriteRecipeIds.has(recipeId)
}, [favoriteRecipeIds])
const clearFavorites = useCallback(() => {
setFavoriteRecipeIds(new Set())
}, [])
return { favoriteRecipeIds, toggleFavorite, isFavorite, clearFavorites }
}
|