import { eq } from "drizzle-orm"; import { db, schema } from "../db/index.js"; export type PoemInfo = { id: number; title: string; author: string; source: string | null; year: number | null; }; export type QuoteWithPoem = { id: number; text: string; tags: string[]; isUserSubmitted: boolean; createdAt: string | null; poem: PoemInfo; favoriteCount: number; isFavorited: boolean; }; export function parseTags(tagsJson: string): string[] { try { const value = JSON.parse(tagsJson); return Array.isArray(value) ? value.filter((v) => typeof v === "string") : []; } catch { return []; } } type RawRow = { quote: typeof schema.quotes.$inferSelect; poem: typeof schema.poems.$inferSelect; }; /** * Joins quote rows with their poem, favorite counts, and (when visitorId is * given) whether that visitor has favorited each quote. Shared by every * action that returns quotes so the shape stays identical across list, * random, favorites, and create. */ export async function hydrateQuotes( rows: RawRow[], visitorId?: string, ): Promise { if (rows.length === 0) return []; const quoteIds = rows.map((r) => r.quote.id); const allFavorites = await db().select().from(schema.favorites); const countByQuoteId = new Map(); const favoritedByVisitor = new Set(); for (const fav of allFavorites) { if (!quoteIds.includes(fav.quote_id)) continue; countByQuoteId.set(fav.quote_id, (countByQuoteId.get(fav.quote_id) ?? 0) + 1); if (visitorId && fav.visitor_id === visitorId) { favoritedByVisitor.add(fav.quote_id); } } return rows.map((row) => ({ id: row.quote.id, text: row.quote.text, tags: parseTags(row.quote.tags_json), isUserSubmitted: row.quote.is_user_submitted, createdAt: row.quote.created_at ?? null, poem: { id: row.poem.id, title: row.poem.title, author: row.poem.author, source: row.poem.source ?? null, year: row.poem.year ?? null, }, favoriteCount: countByQuoteId.get(row.quote.id) ?? 0, isFavorited: favoritedByVisitor.has(row.quote.id), })); } export async function fetchAllQuoteRows(): Promise { return db() .select({ quote: schema.quotes, poem: schema.poems }) .from(schema.quotes) .innerJoin(schema.poems, eq(schema.quotes.poem_id, schema.poems.id)); }