fix: varied gradient+icon placeholders for recipes without a cover photo (v0.50.2)
Replaces the single flat emoji-on-muted-bg fallback (same 🍽️/🍹 everywhere) with a deterministic gradient + food/drink icon per recipe id — pulled from a small curated palette/icon pool via a string hash, so it's stable across re-renders but visually varied across a grid of photo-less recipes. New shared lib/recipe-placeholder.ts + components/recipe/recipe-cover- placeholder.tsx, applied everywhere the old inline emoji fallback lived: recipe-grid-card.tsx, recipe-card.tsx, recipes-grid.tsx, and the public profile page's recipe grid. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { useTranslations } from "next-intl";
|
||||
import { Clock, Users, Lock, Globe, Link2, ExternalLink, UserCheck } from "lucide-react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";
|
||||
import { RecipeCoverPlaceholder } from "@/components/recipe/recipe-cover-placeholder";
|
||||
import { getPublicUrl } from "@/lib/storage";
|
||||
|
||||
type Recipe = {
|
||||
@@ -57,9 +58,7 @@ export function RecipeCard({ recipe }: { recipe: Recipe }) {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="aspect-video bg-gradient-to-br from-muted to-muted/50 flex items-center justify-center text-4xl">
|
||||
{recipe.recipeType === "drink" ? "🍹" : "🍽️"}
|
||||
</div>
|
||||
<RecipeCoverPlaceholder recipe={recipe} className="aspect-video" iconClassName="h-12 w-12" />
|
||||
)}
|
||||
<CardHeader className="pb-2">
|
||||
<h3 className="font-semibold leading-tight line-clamp-2 group-hover:text-primary transition-colors">
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { getRecipePlaceholder } from "@/lib/recipe-placeholder";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/** Gradient + food/drink icon shown in place of a cover photo — deterministic per recipe id, so it stays stable across renders instead of flickering between icons/colors. */
|
||||
export function RecipeCoverPlaceholder({
|
||||
recipe,
|
||||
className,
|
||||
iconClassName,
|
||||
}: {
|
||||
recipe: { id: string; recipeType?: "dish" | "drink" };
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
}) {
|
||||
const { gradient, Icon } = getRecipePlaceholder(recipe);
|
||||
return (
|
||||
<div className={cn("w-full h-full flex items-center justify-center bg-gradient-to-br", gradient, className)}>
|
||||
<Icon className={cn("h-10 w-10 text-foreground/30", iconClassName)} strokeWidth={1.5} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { Globe, Lock, Link2, ExternalLink, ChefHat, Clock, Users, Utensils, User
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { FavoriteButton } from "@/components/social/favorite-button";
|
||||
import { RecipeCoverPlaceholder } from "@/components/recipe/recipe-cover-placeholder";
|
||||
import { getPublicUrl } from "@/lib/storage";
|
||||
import { cn, stripMarkdown } from "@/lib/utils";
|
||||
|
||||
@@ -54,9 +55,7 @@ export function RecipeThumb({ recipe, className }: { recipe: GridCardRecipe; cla
|
||||
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-2xl">
|
||||
{recipe.recipeType === "drink" ? "🍹" : "🍽️"}
|
||||
</div>
|
||||
<RecipeCoverPlaceholder recipe={recipe} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { FavoriteButton } from "@/components/social/favorite-button";
|
||||
import { RecipeCoverPlaceholder } from "@/components/recipe/recipe-cover-placeholder";
|
||||
import { toast } from "sonner";
|
||||
import { getPublicUrl } from "@/lib/storage";
|
||||
import { useLocale } from "@/lib/i18n/provider";
|
||||
@@ -92,9 +93,10 @@ function RecipeThumb({ recipe, selectMode, selected, className }: { recipe: Reci
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<div className={cn("w-full h-full flex items-center justify-center text-2xl", selectMode && !selected && "opacity-60")}>
|
||||
{recipe.recipeType === "drink" ? "🍹" : "🍽️"}
|
||||
</div>
|
||||
<RecipeCoverPlaceholder
|
||||
recipe={recipe}
|
||||
className={cn(selectMode && !selected && "opacity-60")}
|
||||
/>
|
||||
)}
|
||||
{selectMode && selected && <div className="absolute inset-0 bg-primary/20" />}
|
||||
{selectMode && (
|
||||
|
||||
Reference in New Issue
Block a user