"use client"; /** * Stickers on a message, rendered without a chat bubble. * * Laid out as rows of at most two, NOT a fixed 2-column grid: a grid reserves * the second cell whether or not anything fills it, so a single sticker sat * beside a gap the width of another sticker. Rows size themselves — a lone * sticker takes the row and is drawn larger, a pair shares it and both shrink. * An odd count therefore ends on one big sticker rather than a hole. * * Unlike the media grid, this never collapses into "+N more" — every sticker * sent is drawn. They are small and deliberately playful, and hiding some * behind a counter would make a wall of them read as an attachment list * rather than what it is. * * Long-pressing or right-clicking one offers to keep it, which is the only * way a sticker spreads beyond the person who earned it. */ import { useState } from "react"; import Image from "next/image"; import { toast } from "sonner"; import { chat, type Attachment } from "@/lib/chat"; import { cn } from "@/lib/utils"; /** At most two per row, as WhatsApp and Telegram do. */ const STICKERS_PER_ROW = 2; export function StickerGrid({ conversationId, messageId, stickers, /** Own stickers are already in your collection — no point offering to save. */ mine, }: { conversationId: string; messageId: string; stickers: Attachment[]; mine: boolean; }) { const [saving, setSaving] = useState(null); if (stickers.length === 0) return null; const save = async (a: Attachment) => { const key = a.entity_id; if (!key || mine || saving) return; setSaving(key); try { await chat.saveSticker(conversationId, messageId, key); toast.success(`Saved ${a.title ?? "sticker"} to your collection`); } catch { toast.error("Could not save that sticker"); } finally { setSaving(null); } }; // Chunk into rows of at most two. Doing this in JS rather than CSS is what // lets a row know how many it holds, which is what sets the size. const rows: Attachment[][] = []; for (let i = 0; i < stickers.length; i += STICKERS_PER_ROW) { rows.push(stickers.slice(i, i + STICKERS_PER_ROW)); } return ( // w-fit so the block hugs its rows: any fixed width would reintroduce the // empty space this layout exists to remove.
{rows.map((row, i) => (
{row.map((a) => { const alone = row.length === 1; return ( ); })}
))}
); }