Turn timer + auto-play, disconnect/reconnect, cosmetics, queue & paid plan

- Turn timer (20s) for play/trump; system auto-plays a smart move on timeout
- Disconnect handling (mock): wait-for-return countdown, system covers turns
- Cosmetics: titles, card-back styles, custom profile-image upload, badges;
  pickers in Profile; shop sells card styles; reward modal shows new titles
- Paid plan (pro): free players queue when server busy, pro skips; upgrade flow
- OnlineService extended (upgradePlan, richer profile patch); mock implements
  queue + plans; gamification adds TITLES + CARD_STYLES

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 10:49:54 +03:30
parent 5776036d78
commit 13ec0d4300
16 changed files with 682 additions and 61 deletions
+9 -6
View File
@@ -25,7 +25,7 @@ export function ShopScreen() {
const owns = (item: ShopItem) =>
item.kind === "avatar"
? profile.ownedAvatars.includes(item.id)
: profile.ownedThemes.includes(item.id);
: profile.ownedCardStyles.includes(item.id);
const buy = async (item: ShopItem) => {
const res = await getService().buyItem(item.id);
@@ -38,7 +38,7 @@ export function ShopScreen() {
};
const avatars = items.filter((i) => i.kind === "avatar");
const themes = items.filter((i) => i.kind === "theme");
const cardstyles = items.filter((i) => i.kind === "cardstyle");
return (
<ScreenShell>
@@ -64,9 +64,9 @@ export function ShopScreen() {
</div>
</Section>
<Section title={t("shop.themes")}>
<Section title={t("shop.cardstyles")}>
<div className="grid grid-cols-3 gap-3">
{themes.map((item) => (
{cardstyles.map((item) => (
<ItemCard
key={item.id}
item={item}
@@ -74,8 +74,11 @@ export function ShopScreen() {
onBuy={() => buy(item)}
preview={
<span
className="size-10 rounded-xl border border-white/20"
style={{ background: item.preview }}
className="w-8 h-11 rounded-md border"
style={{
borderColor: `${item.preview}80`,
background: `repeating-linear-gradient(45deg, ${item.preview}55 0 4px, transparent 4px 8px), #0a142e`,
}}
/>
}
/>