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
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
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),
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user