Files
Epicure/apps/web/lib/recipe-card-extras.ts
Arnaud 5763bd3318 feat: merge Explore and Activity Feed, unify recipe cards
Explore and Feed covered overlapping ground (recommendations, following,
trending) in two separate places with three different card designs.
Explore now hosts Discover/Following/For You tabs, and every recipe
card everywhere on the page matches the Recipes page's cover-photo
card instead of the old text-only search result.

v0.36.0
2026-07-17 16:06:17 +02:00

55 lines
2.0 KiB
TypeScript

import { db, recipePhotos, recipeBatchDishes, favorites, inArray, eq, and } from "@epicure/db";
export type CardExtras = {
photos: { storageKey: string; isCover: boolean }[];
dishCount: number;
isFavorited: boolean;
};
/** Batches the photo/batch-dish/favorite lookups a recipe grid card needs, for
* list endpoints whose main query selects individual recipe columns rather
* than going through `db.query.recipes.findMany`'s relational loader. */
export async function attachCardExtras<T extends { id: string }>(
recipeList: T[],
viewerId?: string
): Promise<(T & CardExtras)[]> {
const ids = recipeList.map((r) => r.id);
if (ids.length === 0) return [];
const [photoRows, dishRows, favoritedRows] = await Promise.all([
db
.select({ recipeId: recipePhotos.recipeId, storageKey: recipePhotos.storageKey, isCover: recipePhotos.isCover })
.from(recipePhotos)
.where(inArray(recipePhotos.recipeId, ids)),
db
.select({ recipeId: recipeBatchDishes.recipeId })
.from(recipeBatchDishes)
.where(inArray(recipeBatchDishes.recipeId, ids)),
viewerId
? db
.select({ recipeId: favorites.recipeId })
.from(favorites)
.where(and(eq(favorites.userId, viewerId), inArray(favorites.recipeId, ids)))
: Promise.resolve([]),
]);
const photosByRecipe = new Map<string, { storageKey: string; isCover: boolean }[]>();
for (const p of photoRows) {
const arr = photosByRecipe.get(p.recipeId) ?? [];
arr.push({ storageKey: p.storageKey, isCover: p.isCover });
photosByRecipe.set(p.recipeId, arr);
}
const dishCountByRecipe = new Map<string, number>();
for (const d of dishRows) dishCountByRecipe.set(d.recipeId, (dishCountByRecipe.get(d.recipeId) ?? 0) + 1);
const favoritedSet = new Set(favoritedRows.map((f) => f.recipeId));
return recipeList.map((r) => ({
...r,
photos: photosByRecipe.get(r.id) ?? [],
dishCount: dishCountByRecipe.get(r.id) ?? 0,
isFavorited: favoritedSet.has(r.id),
}));
}