"use client"; import { AnimatePresence, motion } from "framer-motion"; import { Check, Coins, Sparkles, X } from "lucide-react"; import { useEffect, useState } from "react"; import { ScreenHeader, ScreenShell } from "@/components/online/ScreenHeader"; import { Sticker } from "@/components/online/Sticker"; import { useSessionStore } from "@/lib/session-store"; import { useI18n } from "@/lib/i18n"; import { getService } from "@/lib/online/service"; import { sound } from "@/lib/sound"; import { ShopItem } from "@/lib/online/types"; import { cn } from "@/lib/cn"; /** The product artwork, used on the card and (bigger) in the detail sheet. */ function Preview({ item, size }: { item: ShopItem; size: number }) { switch (item.kind) { case "stickerpack": return ; case "cardfront": return ( ); case "cardback": return ( ); default: // avatar, reactionpack, xp → emoji glyph return {item.kind === "xp" ? "⚡" : item.preview}; } } export function ShopScreen() { const { t, locale } = useI18n(); const profile = useSessionStore((s) => s.profile); const setProfile = useSessionStore((s) => s.setProfile); const [items, setItems] = useState([]); const [msg, setMsg] = useState(""); const [detail, setDetail] = useState(null); useEffect(() => { getService().getShopItems().then(setItems); }, []); if (!profile) return null; const owns = (item: ShopItem) => { switch (item.kind) { case "avatar": return profile.ownedAvatars.includes(item.id); case "cardfront": return profile.ownedCardFronts.includes(item.id); case "cardback": return profile.ownedCardBacks.includes(item.id); case "reactionpack": return profile.ownedReactionPacks.includes(item.id); case "xp": return false; // consumable — always buyable default: return profile.ownedStickerPacks.includes(item.id); } }; const buy = async (item: ShopItem) => { const res = await getService().buyItem(item.id); if (res.ok && res.profile) { setProfile(res.profile); sound.play("purchase"); setDetail(null); } else { setMsg(locale === "fa" ? res.messageFa : res.messageEn); setTimeout(() => setMsg(""), 1800); } }; const sections: { title: string; kind: ShopItem["kind"]; hint?: string }[] = [ { title: t("shop.avatars"), kind: "avatar" }, { title: t("shop.cardfronts"), kind: "cardfront" }, { title: t("shop.cardbacks"), kind: "cardback" }, { title: t("shop.reactions"), kind: "reactionpack" }, { title: t("shop.stickers"), kind: "stickerpack" }, { title: t("shop.xp"), kind: "xp", hint: t("shop.xpHint") }, ]; return ( {profile.coins.toLocaleString()} } /> {msg && (
{msg}
)} {sections.map((sec) => { const list = items.filter((i) => i.kind === sec.kind); if (!list.length) return null; return (
{list.map((item) => ( setDetail(item)} /> ))}
); })} {detail && ( buy(detail)} onClose={() => setDetail(null)} /> )} ); } function Section({ title, hint, children }: { title: string; hint?: string; children: React.ReactNode }) { return (

{title}

{hint &&

{hint}

} {!hint &&
} {children}
); } function ItemCard({ item, owned, onOpen }: { item: ShopItem; owned: boolean; onOpen: () => void }) { const { locale } = useI18n(); const count = item.contents?.length; return ( {owned && ( )}
{locale === "fa" ? item.nameFa : item.nameEn} {/* quick detail hint */} {item.kind === "xp" ? `+${item.xp} XP` : count != null ? locale === "fa" ? `${count} مورد` : `${count} items` : " "} {owned ? ( ) : ( <> {item.price.toLocaleString()} )}
); } function DetailSheet({ item, owned, coins, onBuy, onClose, }: { item: ShopItem; owned: boolean; coins: number; onBuy: () => void; onClose: () => void; }) { const { t, locale } = useI18n(); const name = locale === "fa" ? item.nameFa : item.nameEn; const desc = locale === "fa" ? item.descFa : item.descEn; const canAfford = coins >= item.price; return ( e.stopPropagation()} className="glass rounded-3xl p-6 w-full max-w-sm text-center relative" >

{name}

{desc &&

{desc}

} {/* what's included */} {item.contents && item.contents.length > 0 && (
{t("shop.includes")} ({item.contents.length})
{item.kind === "stickerpack" ? item.contents.map((s) => (
)) : item.contents.map((e, i) => ( {e} ))}
)} {item.kind === "xp" && (
+{item.xp?.toLocaleString()} XP
)} {/* buy */}
); }