Add designed sticker packs (SVG art) to the reactions system

- 15 hand-designed inline-SVG stickers (faces, Hokm: حکم/کُت/crown/ace,
  Persian: chai/آفرین/rose, taunts: clown/zzz/ضعیف) in components/online/Sticker.tsx
- Sticker packs: faces (free), hokm (earned @rating 1300), persian & taunt (buy)
- In-game tray now tabbed Emoji | Stickers; stickers broadcast as "sticker:<id>"
  and render as large animated bubbles per seat
- Shop sells sticker packs; profile.ownedStickerPacks; gamification helpers
  ownedStickers/ownedStickerPackIds; mock opponents send stickers too

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 11:15:28 +03:30
parent f9425dea01
commit db4eade619
8 changed files with 359 additions and 20 deletions
+19 -1
View File
@@ -3,6 +3,7 @@
import { Check, Coins } 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";
@@ -27,7 +28,9 @@ export function ShopScreen() {
? profile.ownedAvatars.includes(item.id)
: item.kind === "cardstyle"
? profile.ownedCardStyles.includes(item.id)
: profile.ownedReactionPacks.includes(item.id);
: item.kind === "reactionpack"
? profile.ownedReactionPacks.includes(item.id)
: profile.ownedStickerPacks.includes(item.id);
const buy = async (item: ShopItem) => {
const res = await getService().buyItem(item.id);
@@ -42,6 +45,7 @@ export function ShopScreen() {
const avatars = items.filter((i) => i.kind === "avatar");
const cardstyles = items.filter((i) => i.kind === "cardstyle");
const reactions = items.filter((i) => i.kind === "reactionpack");
const stickers = items.filter((i) => i.kind === "stickerpack");
return (
<ScreenShell>
@@ -102,6 +106,20 @@ export function ShopScreen() {
))}
</div>
</Section>
<Section title={t("shop.stickers")}>
<div className="grid grid-cols-3 gap-3">
{stickers.map((item) => (
<ItemCard
key={item.id}
item={item}
owned={owns(item)}
onBuy={() => buy(item)}
preview={<Sticker id={item.preview} size={48} />}
/>
))}
</div>
</Section>
</ScreenShell>
);
}