@smartico/public-api 0.0.379 → 0.0.380

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +31 -2
  3. package/dist/Store/BuyStoreItemErrorCode.d.ts +9 -1
  4. package/dist/Tournaments/TournamentRegistrationError.d.ts +3 -1
  5. package/dist/WSAPI/WSAPI.d.ts +13 -1235
  6. package/dist/WSAPI/WSAPIAvatars.d.ts +78 -0
  7. package/dist/WSAPI/WSAPIBase.d.ts +71 -0
  8. package/dist/WSAPI/WSAPIBonuses.d.ts +26 -0
  9. package/dist/WSAPI/WSAPIClans.d.ts +388 -0
  10. package/dist/WSAPI/WSAPIGamePick.d.ts +308 -0
  11. package/dist/WSAPI/WSAPIGeneral.d.ts +30 -0
  12. package/dist/WSAPI/WSAPIInbox.d.ts +126 -0
  13. package/dist/WSAPI/WSAPIJackpots.d.ts +109 -0
  14. package/dist/WSAPI/WSAPILeaderBoard.d.ts +25 -0
  15. package/dist/WSAPI/WSAPIMiniGames.d.ts +94 -0
  16. package/dist/WSAPI/WSAPIMissions.d.ts +517 -0
  17. package/dist/WSAPI/WSAPIRaffles.d.ts +119 -0
  18. package/dist/WSAPI/WSAPIStore.d.ts +656 -0
  19. package/dist/WSAPI/WSAPITournaments.d.ts +501 -0
  20. package/dist/WSAPI/WSAPITypes.d.ts +145 -85
  21. package/dist/WSAPI/WSAPIUser.d.ts +383 -0
  22. package/dist/index.js +3818 -1528
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.modern.mjs +3180 -981
  25. package/dist/index.modern.mjs.map +1 -1
  26. package/docs/api/README.md +56 -2
  27. package/docs/api/classes/WSAPIAvatars.md +116 -0
  28. package/docs/api/classes/WSAPIBonuses.md +50 -0
  29. package/docs/api/classes/WSAPIClans.md +66 -0
  30. package/docs/api/classes/WSAPIGamePick.md +181 -0
  31. package/docs/api/classes/WSAPIGeneral.md +63 -0
  32. package/docs/api/classes/WSAPIInbox.md +277 -0
  33. package/docs/api/classes/WSAPIJackpots.md +185 -0
  34. package/docs/api/classes/WSAPILeaderBoard.md +39 -0
  35. package/docs/api/classes/WSAPIMiniGames.md +175 -0
  36. package/docs/api/classes/WSAPIMissions.md +611 -0
  37. package/docs/api/classes/WSAPIRaffles.md +195 -0
  38. package/docs/api/classes/WSAPIStore.md +744 -0
  39. package/docs/api/classes/WSAPITournaments.md +104 -0
  40. package/docs/api/classes/WSAPIUser.md +200 -0
  41. package/docs/api/enumerations/AchievementClaimPeriodTypeId.md +13 -0
  42. package/docs/api/enumerations/BuyStoreItemErrorCode.md +32 -0
  43. package/docs/api/interfaces/ClanPrizePublicMeta.md +41 -0
  44. package/docs/api/interfaces/ClanPrizeStructureEntry.md +7 -1
  45. package/docs/api/interfaces/ClanPublicMeta.md +4 -0
  46. package/docs/api/interfaces/SAWTemplateUI.md +10 -0
  47. package/docs/api/interfaces/TAchCategory.md +19 -4
  48. package/docs/api/interfaces/TBuyStoreItemResult.md +26 -8
  49. package/docs/api/interfaces/TMissionClaimRewardResult.md +14 -3
  50. package/docs/api/interfaces/TMissionOptInResult.md +10 -3
  51. package/docs/api/interfaces/TMissionOrBadge.md +6 -2
  52. package/docs/api/interfaces/TStoreItem.md +21 -1
  53. package/docs/api/interfaces/TTournamentDetailed.md +8 -0
  54. package/docs/api/interfaces/UserAchievement.md +1 -1
  55. package/docs/ui/clans/UIGuide_getClanInfo.md +153 -0
  56. package/docs/ui/clans/UIGuide_getClans.md +172 -0
  57. package/docs/ui/clans/UIGuide_joinClan.md +196 -0
  58. package/docs/ui/missions/UIGuide_getAchCategories.md +17 -0
  59. package/docs/ui/missions/UIGuide_getBadges.md +237 -0
  60. package/docs/ui/missions/UIGuide_getMissions.md +268 -0
  61. package/docs/ui/missions/UIGuide_requestMissionClaimReward.md +21 -0
  62. package/docs/ui/missions/UIGuide_requestMissionOptIn.md +16 -0
  63. package/docs/ui/store/UIGuide_buyStoreItem.md +133 -0
  64. package/docs/ui/store/UIGuide_getStoreCategories.md +19 -0
  65. package/docs/ui/store/UIGuide_getStoreItems.md +264 -0
  66. package/docs/ui/store/UIGuide_getStorePurchasedItems.md +122 -0
  67. package/docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md +140 -0
  68. package/docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md +265 -0
  69. package/docs/ui/tournaments/UIGuide_getTournamentsList.md +236 -0
  70. package/docs/ui/tournaments/UIGuide_registerInTournament.md +156 -0
  71. package/docs/ui/user/UIGuide_getLevels.md +162 -0
  72. package/docs/ui/user/UIGuide_getUserProfile.md +213 -0
  73. package/package.json +3 -3
  74. package/scripts/postdoc-cleanup.js +195 -0
  75. package/src/Missions/MissionsUtils.ts +5 -2
  76. package/src/Store/BuyStoreItemErrorCode.ts +8 -0
  77. package/src/Store/StoreItem.ts +1 -0
  78. package/src/Store/StoreItemPurchased.ts +1 -0
  79. package/src/Store/StoreItemType.ts +1 -1
  80. package/src/Tournaments/TournamentRegistrationError.ts +4 -0
  81. package/src/WSAPI/WSAPI.ts +27 -2007
  82. package/src/WSAPI/WSAPIAvatars.ts +127 -0
  83. package/src/WSAPI/WSAPIBase.ts +93 -0
  84. package/src/WSAPI/WSAPIBonuses.ts +54 -0
  85. package/src/WSAPI/WSAPIClans.ts +417 -0
  86. package/src/WSAPI/WSAPIGamePick.ts +382 -0
  87. package/src/WSAPI/WSAPIGeneral.ts +57 -0
  88. package/src/WSAPI/WSAPIInbox.ts +222 -0
  89. package/src/WSAPI/WSAPIJackpots.ts +212 -0
  90. package/src/WSAPI/WSAPILeaderBoard.ts +40 -0
  91. package/src/WSAPI/WSAPIMiniGames.ts +181 -0
  92. package/src/WSAPI/WSAPIMissions.ts +572 -0
  93. package/src/WSAPI/WSAPIRaffles.ts +175 -0
  94. package/src/WSAPI/WSAPIStore.ts +732 -0
  95. package/src/WSAPI/WSAPITournaments.ts +541 -0
  96. package/src/WSAPI/WSAPITypes.ts +145 -87
  97. package/src/WSAPI/WSAPIUser.ts +456 -0
  98. package/tsconfig.json +17 -1
  99. package/docs/api/classes/WSAPI.md +0 -1927
@@ -0,0 +1,265 @@
1
+ # UI Guide — `getTournamentInstanceInfo`
2
+
3
+ ## Overview
4
+ - Powers the **tournament detail / lobby screen** — the surface a user
5
+ lands on after picking an item from
6
+ [`getTournamentsList`](../../api/classes/WSAPITournaments.md#gettournamentslist).
7
+ - One-shot fetch. There is no subscription and no client cache.
8
+ Re-call to refresh; the default Smartico UI polls every 3 s while
9
+ the detail view is open.
10
+ - For a clan tournament (`is_clan_based === true`), the response also
11
+ carries clan-level data (`clan_leaderboard[]`,
12
+ `clan_prize_structure[]`, `user_clan_id`).
13
+
14
+ ## Detail view layout
15
+
16
+ Top-to-bottom layout (desktop):
17
+
18
+ 1. **Header image** — `image2` (920×200 px) rendered as
19
+ `background-image` on the header block; falls back to `image1` if
20
+ missing.
21
+ 2. **Tournament name + back affordance** — above the image (desktop)
22
+ or overlaid (mobile).
23
+ 3. **Countdown ticker** — `DD:HH:MM:SS` live ticker. Counts to
24
+ `end_time` while `is_in_progress`; to `start_time` while
25
+ `is_upcoming`. Hidden when `is_finished` / `is_cancelled`.
26
+ Updates every 1 second.
27
+ 4. **Info row** — date range, buy-in, registration count
28
+ (`registration_count / players_max_count`), duration. On desktop,
29
+ this lives inside the header image; on mobile it sits below.
30
+ 5. **Registration CTA** — see "Registration CTA" below.
31
+ 6. **Tab strip** — General · Players (or ClanBoard) · Prizes ·
32
+ RelatedGames (when non-empty).
33
+ 7. **Active tab content** — see per-tab sections below.
34
+ 8. **Sidebar (desktop only)** — if `related_games?.length > 0`,
35
+ render the related-games list; otherwise render a "Prizes
36
+ overview" panel.
37
+
38
+ Mobile layout collapses the sidebar into a tab (`RelatedGames`) and
39
+ moves the tournament name into the header overlay.
40
+
41
+ ## Header image specs
42
+
43
+ | Field | Source size | Used where | Fallback |
44
+ |---|---|---|---|
45
+ | `image2` | 920×200 px | Desktop header background | Falls back to `image1` (544×216 px) |
46
+ | `image2_mobile` | 720×400 px | Mobile header background | Falls back to `image1` |
47
+
48
+ Render as CSS `background-image`, not `<img>`. No built-in placeholder
49
+ — provide a neutral fallback color/illustration if all three are
50
+ missing.
51
+
52
+ ## Registration CTA
53
+
54
+ Same rules as the card-level CTA in
55
+ [`UIGuide_getTournamentsList`](./UIGuide_getTournamentsList.md), but
56
+ this time clicking it actually **invokes registration** via
57
+ [`registerInTournament`](../../api/classes/WSAPITournaments.md#registerintournament)
58
+ rather than opening the detail view (you're already in it).
59
+
60
+ | Condition | CTA label | Action |
61
+ |---|---|---|
62
+ | `is_can_register === true` (no clan constraint) | "Join tournament" | Call `registerInTournament(instance_id)`; show loading state while the promise is pending |
63
+ | `is_can_register === true` AND `is_clan_based === true` AND `user_clan_id` is null | "Join tournament" | First prompt the user to pick a clan ([`joinClan`](../../api/classes/WSAPIClans.md#joinclan)), then call `registerInTournament` |
64
+ | `registration_status === 'REGISTERED'` (before start) | "You are registered" | Disabled / informational |
65
+ | `registration_status === 'REGISTERED'` AND `is_finished` | "You are registered" | Disabled / informational |
66
+ | `registration_status === 'PENDING'` | "Pending approval" | Disabled |
67
+ | `registration_status === 'REGISTERED_PENDING_QUALIFICATION'` | "Pending qualification" | Disabled |
68
+ | `registration_type === 'AUTO'` AND active | "Automatic registration" | Disabled / informational |
69
+ | Cancelled / expired / segment-blocked | "Registration closed" | Disabled |
70
+
71
+ For segment-blocked tournaments where the user fails entry conditions,
72
+ surface `segment_dont_match_message` (operator-supplied) below the
73
+ CTA or in a tooltip on hover.
74
+
75
+ ## Tabs
76
+
77
+ | Tab | When shown | Content source |
78
+ |---|---|---|
79
+ | **General / Info** | Always | `description` HTML (sanitize before injecting) + optional `hint_text` (T&C) |
80
+ | **Players** | `is_clan_based === false` | `players[]` — individual leaderboard |
81
+ | **ClanBoard** | `is_clan_based === true` (replaces Players) | `clan_leaderboard[]` — clan leaderboard |
82
+ | **Prizes** | Always | `prizes[]` (non-clan) or `clan_prize_structure[]` (clan) |
83
+ | **Related games** | `related_games?.length > 0` AND mobile (desktop renders this in the sidebar) | `related_games[]` |
84
+
85
+ Default selected tab: General.
86
+
87
+ ## Player leaderboard (non-clan)
88
+
89
+ Sourced from `players[]`. Columns:
90
+
91
+ | Column | Source | Notes |
92
+ |---|---|---|
93
+ | Position | `player.position`, zero-padded (`01`, `09`, `10`, …) | Show `'-'` when tournament status is REGISTER (no one has scored yet) OR `player.position === null` |
94
+ | Avatar | `player.avatar_url` (CDN-resized; 128×128 typical) | Operator setting `config.hide_avatars_leaderboards` may suppress |
95
+ | Username | `player.public_username` | |
96
+ | Score | `player.scores` | Hidden on rows where `is_me === false` if operator setting `show_other_users_score === false`. Always shown for `me`. |
97
+
98
+ **Top-3 styling**: rows where `position <= 3` get optional podium
99
+ treatment (gold / silver / bronze background or accent). CSS classes
100
+ in the default Smartico UI: `place-1`, `place-2`, `place-3`.
101
+
102
+ **"Me" row**: when `player.is_me === true`, highlight the row (e.g.
103
+ `place-my` class — background tint, slightly larger font). The user's
104
+ position is also surfaced separately in the dedicated "me" panel
105
+ below the leaderboard (see "`me` panel").
106
+
107
+ **Empty state**: when `players[]` is empty (e.g. tournament hasn't
108
+ started yet, no scores recorded), render an empty-state illustration
109
+ with copy like "No players yet" or "Be the first to register".
110
+
111
+ ## Clan leaderboard (clan tournaments)
112
+
113
+ Sourced from `clan_leaderboard[]`, filtered to entries where
114
+ `contributing_members > 0`. Each row:
115
+
116
+ | Element | Source |
117
+ |---|---|
118
+ | Position badge | `clan.position` — podium styling for 1/2/3 |
119
+ | Clan image | `clan.public_meta.image_url`; fallback to first-letter avatar |
120
+ | Clan name | `clan.public_meta.name` |
121
+ | "Your clan" badge | If `clan.clan_id === user_clan_id`, add a "Your clan" pill |
122
+ | Contributing members | `clan.contributing_members` |
123
+ | Total score | `clan.total_score`, space-formatted (`1 234 567`) + "pts" suffix |
124
+
125
+ **User's clan highlight**: row gets a distinct background when
126
+ `clan.clan_id === user_clan_id`.
127
+
128
+ **Sticky "my clan" footer**: when `clan_leaderboard.length >= 5` and
129
+ `user_clan_id` is set, pin the user's clan row at the bottom of the
130
+ scrollable list as a sticky footer.
131
+
132
+ **Clan row click** opens a modal showing that clan's individual
133
+ player breakdown via
134
+ [`getClanTournamentPlayers`](../../api/classes/WSAPITournaments.md#getclantournamentplayers).
135
+
136
+ **Empty state**: render a Lottie animation (or static illustration) +
137
+ "No leaders" copy.
138
+
139
+ ## `me` panel
140
+
141
+ When `detail.me` is populated, render a **sticky "me" panel** below
142
+ (or alongside) the leaderboard with:
143
+
144
+ - Rank badge — `me.position` (or `'-'` if not yet ranked)
145
+ - Avatar — `me.avatar_url`
146
+ - Username — `me.public_username`
147
+ - Score — `me.scores` (always shown, regardless of
148
+ `show_other_users_score`)
149
+
150
+ This panel is **not** rendered when `me` is undefined (visitor mode,
151
+ unregistered users, or registered users with no recorded score yet).
152
+
153
+ The `user_position_in_clan` and `user_score_in_clan` fields on the
154
+ response are not rendered by the default Smartico UI but are
155
+ available on `TTournamentDetailed` if your custom UI needs them.
156
+
157
+ ## Prize structure
158
+
159
+ ### Non-clan tournaments — `prizes[]`
160
+
161
+ Render as a ranked table. Each row:
162
+
163
+ - **Place range**: `place_from` – `place_to`; if equal, render the
164
+ single value (e.g. `1st place`); otherwise a range (e.g.
165
+ `2nd–5th place`).
166
+ - **Prize image**: `prizes[i].image_url` (1:1 aspect ratio).
167
+ - **Prize description**: derived from `type` + `points`:
168
+ - `'POINTS_ADD'` → "{points} pts"
169
+ - `'TANGIBLE'` / `'BONUS'` / `'MINI_GAME_ATTEMPT'` → `name`
170
+ - Other types → `name` fallback
171
+
172
+ ### Clan tournaments — `clan_prize_structure[]`
173
+
174
+ More complex — render as accordions, one per `clan_place` entry:
175
+
176
+ - **Group header**: `<ordinal> Place` for clan placement (e.g.
177
+ `1st place`, `2nd place`). Overridable by
178
+ `clan_place_entry.public_meta.prize_name`.
179
+ - **Prize type label**:
180
+ - `prize_type_id === 1` (Fixed) → "Fixed prizes"
181
+ - `prize_type_id === 2` (Dynamic) → "Dynamic prizes" (uses
182
+ `prize_pool_amount`)
183
+ - **Per-tier rows** (from `tiers[]` inside each clan_place):
184
+ - Player place range (`player_place_from` – `player_place_to`)
185
+ - Tier image — `tier.public_meta.image_url` (optional)
186
+ - Prize amount derived from `prize_type_id` + `distribution_type`:
187
+ - Fixed → render `public_meta.name` (or points / gems / diamonds
188
+ from `details_json`)
189
+ - Dynamic + `distribution_type === 1` (ScoreWeighted) → render
190
+ `pool_amount` as "total pool" with "Score-weighted split"
191
+ secondary label
192
+ - Dynamic + `distribution_type === 2` (EqualSplit) → render
193
+ `pool_amount / range_size` as "avg per player" with "equal
194
+ split" secondary label
195
+
196
+ The default Smartico UI uses a `compact` variant for mobile that
197
+ collapses each clan_place group into a single line.
198
+
199
+ ## Countdown ticker
200
+
201
+ `DD:HH:MM:SS` exact-digit format with 1-second tick. Each segment
202
+ in its own `<span>` for styling; RTL-aware (segment order reverses
203
+ in right-to-left locales).
204
+
205
+ Timing source:
206
+ - `is_upcoming` → counts down to `start_time`
207
+ - `is_in_progress` → counts down to `end_time`
208
+ - `is_finished` / `is_cancelled` → ticker hidden
209
+
210
+ When the countdown reaches zero, don't auto-flip the UI state — wait
211
+ for the next poll-driven refresh to pick up the lifecycle transition.
212
+ (The detail-info call doesn't push; lifecycle reads come from polling.)
213
+
214
+ ## Empty / loading / error states
215
+
216
+ - **Loading (first fetch)**: render a skeleton layout — header
217
+ placeholder, blank tab strip, shimmer rows in the active tab area.
218
+ Don't render `null` (avoid a layout jump when data arrives).
219
+ - **Loading (poll refresh)**: keep the prior snapshot visible; do not
220
+ blank the UI. Stale state for ≤3 s is fine.
221
+ - **`players[]` empty**: empty-state illustration with copy like
222
+ "No players yet". Common when the tournament is upcoming and no
223
+ one has registered yet.
224
+ - **`clan_leaderboard[]` empty**: Lottie animation + "No leaders"
225
+ text.
226
+ - **Error**: keep last snapshot, show a small non-blocking error
227
+ banner, retry on next poll tick. If the first fetch fails, show a
228
+ full-screen "Failed to load — Try again" with a retry button.
229
+
230
+ ## Animations / transitions
231
+
232
+ - **Tab switch**: cross-fade content (~150 ms).
233
+ - **Leaderboard position change**: when a player's `position` shifts
234
+ between polls (detect by diffing the prior snapshot), animate the
235
+ row to its new position (slide / FLIP animation). Helps users see
236
+ live movement.
237
+ - **Countdown final 10 s**: optional pulse / color change on the
238
+ countdown digits.
239
+
240
+ ## Mobile vs desktop
241
+
242
+ - **Header**: desktop renders tournament name above the image and
243
+ info blocks (buy-in / registration / duration) inside the image;
244
+ mobile overlays name inside the image and moves info blocks below.
245
+ - **Image source**: desktop uses `image2`, mobile uses
246
+ `image2_mobile`; both fall back to `image1`.
247
+ - **Tab strip**: desktop shows all 3–4 tabs; mobile drops the
248
+ RelatedGames tab if there are no related games (and the sidebar
249
+ becomes an in-tab section on mobile).
250
+ - **Clan prize structure**: desktop uses the full accordion; mobile
251
+ uses a compact variant.
252
+ - **Leaderboard**: desktop shows full 4-column rows; mobile may
253
+ collapse avatar + username into a single column to save horizontal
254
+ space.
255
+
256
+ ## Performance
257
+
258
+ - Diff snapshots between polls to detect position changes and animate
259
+ only the affected rows. Avoid full re-renders of the leaderboard
260
+ table on every poll tick.
261
+ - Virtualize the player list if `players.length > 100` (uncommon —
262
+ the server usually returns a top-N slice).
263
+ - Stop polling when the detail view is no longer visible
264
+ (`IntersectionObserver` or your router-driven unmount). Each poll
265
+ is a real network round-trip.
@@ -0,0 +1,236 @@
1
+ # UI Guide — `getTournamentsList`
2
+
3
+ ## Overview
4
+ - The list is the lobby surface — designed for a tabbed grid where each
5
+ card opens a detail view (powered by
6
+ [`getTournamentInstanceInfo`](../../api/classes/WSAPITournaments.md#gettournamentinstanceinfo)).
7
+ - Register `onUpdate` on first render; treat each invocation as
8
+ "discard prior list, re-render from this array". `onUpdate` fires
9
+ only after `registerInTournament` resolves — lifecycle transitions
10
+ (start/finish/cancel) don't push.
11
+ - Loading indicator: the first call is a server round-trip
12
+ (typically 100–500 ms); subsequent calls within the 30 s cache window
13
+ resolve effectively synchronously.
14
+
15
+ ## List view organization
16
+
17
+ The default Smartico UI groups tournaments into category tabs. A single
18
+ tournament can appear in multiple tabs simultaneously.
19
+
20
+ | Tab | Assignment rule | Sort |
21
+ |---|---|---|
22
+ | **Overview** (default) | `is_in_progress` OR `is_user_registered` OR `is_can_register` OR `is_upcoming` — i.e. anything not finished/cancelled | Featured pinned first; then by `start_time` ASC for upcoming, registered/registrable next |
23
+ | **MyTournaments** | `is_user_registered === true` | Insertion order |
24
+ | **InProgress** | `is_in_progress === true` | Insertion order |
25
+ | **Upcoming** | `is_upcoming === true` | `start_time` ASC |
26
+ | **Finished** | `is_finished === true` | Insertion order (server returns newest finished first) |
27
+ | **Top** | `is_can_register === true` | Featured pinned first |
28
+
29
+ **Featured pinning**: tournaments with `is_featured === true` AND
30
+ `is_cancelled === false` get moved to index 0 in the Overview and Top
31
+ tabs only. No visual difference on the card itself — just position.
32
+
33
+ **Default visible tabs**:
34
+ - Desktop: Overview · MyTournaments · InProgress · Finished
35
+ - Mobile: Overview · MyTournaments · InProgress
36
+
37
+ **Custom section filtering**: if your UI scopes the lobby to a
38
+ specific custom section (e.g. a deep-link `?custom_section_id=42`),
39
+ filter to tournaments whose `custom_section_id` matches; the SDK does
40
+ not filter for you.
41
+
42
+ **Bucketing helper** (raw-state to bucket — for reference):
43
+
44
+ ```ts
45
+ function bucket(t: TTournament) {
46
+ if (t.is_in_progress) return 'live';
47
+ if (t.is_user_registered && !t.is_finished) return 'mine';
48
+ if (t.is_can_register) return 'available';
49
+ if (t.is_upcoming) return 'upcoming';
50
+ if (t.is_finished) return 'finished';
51
+ if (t.is_cancelled) return 'cancelled';
52
+ return 'other';
53
+ }
54
+ ```
55
+
56
+ Drive UI tabs from the boolean flags directly. Avoid recomputing
57
+ state from `start_time` / `end_time` / `registration_status` — the
58
+ SDK already encodes the canonical lifecycle.
59
+
60
+ ## Tournament card / tile
61
+
62
+ Fields rendered on the card (top-to-bottom):
63
+
64
+ - **Background image** — `image1`, documented source 544×216 px.
65
+ Render at a 16:9-ish aspect ratio with `background-image`. No
66
+ built-in placeholder — provide a neutral fallback if missing.
67
+ - **Status badge** — top-left, color-coded by lifecycle state.
68
+ See "Status-specific visual treatments" below.
69
+ - **Clan badge** — appended to the status block when
70
+ `is_clan_based === true`.
71
+ - **Countdown line** — relative time string driven by `start_time`
72
+ (upcoming) or `end_time` (in-progress / finished). See "Countdown
73
+ / timing format".
74
+ - **Tournament name** — `name`, prominent.
75
+ - **Ribbon** — `ribbon` field. Presets (`'sale'`, `'hot'`, `'new'`,
76
+ `'vip'`) are CSS classes; a custom URL (250×300 px) renders as an
77
+ image overlay top-right.
78
+ - **Footer row** (3 columns on desktop, may collapse on mobile):
79
+ - Registration count: `registration_count` / `players_max_count`
80
+ - Buy-in: derived from `registration_cost_points` /
81
+ `registration_cost_gems` / `registration_cost_diamonds` (only
82
+ one is set per tournament) — show "Free" if all are falsy.
83
+ `custom_price_text` (operator-supplied string) overrides if set.
84
+ - Prize pool: `prize_pool_short` if set, else a derived label
85
+ from `prizes[]` (or "Mixed" / "—" as fallback).
86
+ - **CTA button** — see "Action button decision matrix".
87
+
88
+ **Mobile card variant**: ribbon renders ABOVE the image (not as an
89
+ overlay); footer typically collapses to inline buy-in/prize line.
90
+
91
+ **Whole-card click** opens the detail view via
92
+ [`getTournamentInstanceInfo`](../../api/classes/WSAPITournaments.md#gettournamentinstanceinfo);
93
+ the CTA button click triggers the same detail-view open (it does NOT
94
+ invoke `registerInTournament` from the card — registration is always
95
+ performed from inside the detail view).
96
+
97
+ **Fields NOT on the card** (detail-view-only):
98
+ - `description` (HTML), full `prizes[]` table, `me` block, full player
99
+ leaderboard (via `getTournamentInstanceInfo`), clan leaderboard,
100
+ `related_games`, `min_scores_win`.
101
+
102
+ ## Action button decision matrix
103
+
104
+ CTA label is driven by `is_can_register`, then by
105
+ `registration_status`. Resolve in priority order — first match wins.
106
+
107
+ | Condition | CTA label | Enabled? |
108
+ |---|---|---|
109
+ | `is_can_register === true` | "Join tournament" | Enabled — opens detail view |
110
+ | `registration_status === 'REGISTERED'` AND not yet started | "You are registered" | Disabled (informational) |
111
+ | `registration_status === 'REGISTERED'` AND `is_finished` | "You are registered" | Disabled (informational) |
112
+ | `registration_status === 'PENDING'` | "Pending approval" | Disabled |
113
+ | `registration_status === 'REGISTERED_PENDING_QUALIFICATION'` | "Pending qualification" | Disabled |
114
+ | `registration_type === 'AUTO'` AND tournament active | "Automatic registration" | Disabled (informational) |
115
+ | `is_clan_based` AND user has no clan | "Registration closed" | Disabled |
116
+ | Everything else (cancelled, expired, segment-blocked, max reached, etc.) | "Registration closed" | Disabled |
117
+
118
+ When CTA is "Registration closed" and the user fails segment
119
+ conditions, surface `segment_dont_match_message` (operator-supplied)
120
+ inside the detail view, not on the card.
121
+
122
+ The CTA button is also part of the card click target — clicking it
123
+ opens the detail view rather than invoking registration directly.
124
+ Registration only happens from the detail-view CTA.
125
+
126
+ ## Image / asset specs
127
+
128
+ | Field | Documented source | Aspect ratio | Used where | Fallback |
129
+ |---|---|---|---|---|
130
+ | `image1` | 544×216 px | ~5:2 (landscape banner) | Card grid background | Neutral placeholder if missing |
131
+ | `image2` | 920×200 px | ~4.6:1 | Desktop lobby-detail header (rendered by detail view, not card) | Falls back to `image1` |
132
+ | `image2_mobile` | 720×400 px | 9:5 | Mobile lobby-detail header | Falls back to `image1` |
133
+ | `ribbon` (custom URL) | 250×300 px | 5:6 (portrait) | Card overlay | Hide ribbon if URL fails |
134
+ | `prizes[].image_url` | 1:1 square | 1:1 | Detail-view prize table only | Hide image if missing |
135
+
136
+ Do not render `image2` / `image2_mobile` on the card — they're sized
137
+ for the detail-view header.
138
+
139
+ ## Status-specific visual treatments
140
+
141
+ Drive from the lifecycle booleans and `registration_status`. Recommended
142
+ treatments:
143
+
144
+ | State | Card visual | Badge text |
145
+ |---|---|---|
146
+ | `is_upcoming` (PUBLISHED) | Full-color card | "New" |
147
+ | `is_upcoming` (REGISTER — accepting registrations) | Full-color card, "live registration" pulse on the CTA | "Gathering" |
148
+ | `is_in_progress` | Full-color card, optional shimmer/glow accent | "Started" / "Live" |
149
+ | `is_finished` | Subtle desaturation (e.g. 80% opacity), no shimmer | "Finished" |
150
+ | `is_cancelled` | Status badge collapses / hidden; card itself may be hidden by the consumer's tab logic | (none — badge hidden) |
151
+ | `is_user_registered && !is_finished` | Color-restored card, add a "Registered" check-mark overlay | derived from `registration_status` |
152
+
153
+ The default Smartico UI collapses the cancelled card's status block to
154
+ `height: 10 px` to keep layout consistent without surfacing the
155
+ "Cancelled" label prominently — many consumers prefer to hide cancelled
156
+ items entirely.
157
+
158
+ **Clan tournaments**: append a "Clan" pill next to the status badge
159
+ when `is_clan_based === true`. No other structural change on the card.
160
+
161
+ ## Countdown / timing format
162
+
163
+ The default Smartico UI uses moment.js-style relative formatting
164
+ (`"in 3 days"`, `"in 2 hours"`, `"a few seconds ago"`) with a status
165
+ prefix:
166
+
167
+ | State | Prefix | Time source |
168
+ |---|---|---|
169
+ | `is_upcoming` | "Starts in" | `start_time` |
170
+ | `is_in_progress` | "Ends in" | `end_time` |
171
+ | `is_finished` | "Finished" | (no relative time, or `end_time` past-tense) |
172
+ | `is_cancelled` | "Cancelled" | (no time) |
173
+
174
+ Suggested format tiers (compact):
175
+ - > 7 days → `"Starts <date>"` (e.g. `"Starts 12 Jun"`)
176
+ - 1–7 days → `"in N days"`
177
+ - 1–24 h → `"in Hh Mm"`
178
+ - < 1 h → `"in Mm Ss"`, update every second
179
+ - Within 60 s → exact seconds with 1-second tick
180
+
181
+ The detail view uses a more precise `DD:HH:MM:SS` ticker; the card
182
+ uses the compact relative form.
183
+
184
+ ## Empty / loading / error states
185
+
186
+ - **Loading (cold fetch)**: render a shimmer grid (skeleton cards) at
187
+ the eventual grid layout. The first call is a network round-trip.
188
+ - **Loading (cache hit)**: do not render a loading state — the promise
189
+ resolves within a microtask.
190
+ - **Empty list**: `[]` means the user has no visible tournaments.
191
+ Show an empty-state illustration; copy varies by tab:
192
+ - Finished tab: "No tournaments finished yet"
193
+ - MyTournaments tab: "You haven't joined any tournaments yet"
194
+ - All other tabs: "No tournaments available"
195
+ - **Error**: keep the prior list visible if any, show a small
196
+ non-blocking error banner, retry on the next user-driven action.
197
+
198
+ ## Animations / transitions
199
+
200
+ - **List entry**: cards fade-in with a ~30 ms stagger on first render.
201
+ - **Registration success celebration**: when `is_user_registered` flips
202
+ from `false` to `true` between fetches (detect by diffing prior list
203
+ in your `onUpdate` handler), play a check-mark scale-in on the
204
+ registered card. The default Smartico UI also briefly highlights the
205
+ whole card.
206
+ - **Countdown ticker**: update once per second when remaining time is
207
+ under 1 hour; once per minute otherwise.
208
+ - **Status badge transitions**: when a tournament moves from
209
+ `is_upcoming` to `is_in_progress` mid-render, animate the badge
210
+ recolor over ~300 ms. This transition happens only on cache miss /
211
+ refresh — not pushed live.
212
+
213
+ ## Mobile vs desktop
214
+
215
+ - **Grid columns**: desktop 2–3, tablet 2, mobile 1 (full-width cards
216
+ due to the landscape image aspect ratio).
217
+ - **Tab strip**: desktop shows Overview · MyTournaments · InProgress ·
218
+ Finished; mobile drops Finished to reduce horizontal scroll.
219
+ - **Card footer**: desktop renders 3-column footer (registrations /
220
+ buy-in / prize pool); mobile collapses to a single-line summary.
221
+ - **Ribbon position**: desktop overlays on the image (top-right);
222
+ mobile renders above the image to avoid overlap on small cards.
223
+
224
+ ## Performance
225
+
226
+ - The 30 s cache deduplicates rapid refetches; a poll loop hitting
227
+ `getTournamentsList` every 5 s effectively translates to one
228
+ network round-trip per 30 s.
229
+ - Diff lists between fetches to detect lifecycle transitions
230
+ (`is_upcoming → is_in_progress`, registration flips). Avoid full
231
+ re-renders on every poll tick if you animate the card transitions —
232
+ keyed per-card rendering matters because the celebration animations
233
+ break if the DOM nodes are recreated.
234
+ - The hardest perf hit is the countdown ticker. Consider rendering the
235
+ full countdown only on currently-visible cards (use
236
+ `IntersectionObserver` for off-screen virtualization on long lists).
@@ -0,0 +1,156 @@
1
+ # UI Guide — `registerInTournament`
2
+
3
+ ## Overview
4
+ - Action-focused mutation. The user is on a tournament card or detail
5
+ view, the CTA is "Join tournament", and they click it.
6
+ - A successful response (`err_code === 0`) means: registration row
7
+ exists, buy-in already debited, lobby refresh fired. The SDK has
8
+ already updated the cache by the time you receive the response — the
9
+ `onUpdate` callback on `getTournamentsList` fires shortly after.
10
+ - Latency: typically 100–500 ms for a healthy WebSocket connection.
11
+
12
+ ## Loading state
13
+
14
+ - **Trigger**: set an in-flight flag (`registering = true`) immediately
15
+ on click, BEFORE the call. This is the consumer's responsibility —
16
+ the SDK does NOT have an in-flight guard.
17
+ - **Visual**: replace the button label with animated dots (or a
18
+ spinner). Disable the button. Keep the modal / panel open.
19
+ - **Reset**: clear the in-flight flag on response — success or error.
20
+
21
+ ## Idempotency UI guard
22
+
23
+ The SDK does NOT prevent double-clicks. Two simultaneous calls both
24
+ send to the server; both responses resolve. The first will succeed
25
+ (`err_code = 0`); the second usually returns `30004`
26
+ (already-registered) but may also succeed if the race is exactly tied.
27
+
28
+ **Two practical guards**:
29
+
30
+ ```ts
31
+ // 1. Component-local in-flight flag (simple)
32
+ const [registering, setRegistering] = useState(false);
33
+
34
+ const onJoin = async () => {
35
+ if (registering) return;
36
+ setRegistering(true);
37
+ const r = await window._smartico.api.registerInTournament(tournament.instance_id);
38
+ setRegistering(false);
39
+ handleResponse(r);
40
+ };
41
+
42
+ // 2. Disable the button while is_user_registered hasn't flipped yet
43
+ const disabled = registering || tournament.is_user_registered;
44
+ ```
45
+
46
+ Treat `err_code = 30004` as **idempotent success** in the UI — hide
47
+ the Register button and show "Registered". Don't show an error.
48
+
49
+ ## Action button decision matrix
50
+
51
+ | `err_code` | Action |
52
+ |---|---|
53
+ | `0` (success) | Close any registration modal · Show a success toast · Refresh the detail view if open (call `getTournamentInstanceInfo` again) · The lobby list auto-refreshes via `onUpdate` — DON'T manually re-fetch `getTournamentsList` |
54
+ | `30004` (already registered) | Same as success — treat as idempotent success |
55
+ | `1010` (no clan) | Open a clan-pick modal driven by [`getClans`](../../api/classes/WSAPIClans.md#getclans) and [`joinClan`](../../api/classes/WSAPIClans.md#joinclan); after the clan-join resolves, call `registerInTournament` again. Don't show an error |
56
+ | `30002` (insufficient points) | Show insufficient-points UI: `"You need X more points to join"`. Optional: deep-link to a points-earning surface. |
57
+ | `300010` (insufficient gems) | Show insufficient-gems UI (analogous to `30002` but for gems currency). NOT in the public enum — branch on the literal numeric value. |
58
+ | `300011` (insufficient diamonds) | Show insufficient-diamonds UI (analogous, for diamonds). NOT in the public enum — branch on the literal. |
59
+ | `30001` (instance not found) | Refresh the lobby; the tournament is gone. Show a brief "no longer available" toast. |
60
+ | `30003` (not in registerable state) | Refresh the lobby; the button should disappear. |
61
+ | `30005` (segment mismatch) | Show the operator-supplied `tournament.segment_dont_match_message` if set; fall back to `err_message`. Don't retry. |
62
+ | `30006` (anomalous "not registered") | Show generic error toast with `err_message`. Allow retry. |
63
+ | `30007` (status-change blocked) | Show generic error toast. Don't retry. |
64
+ | `30008` (tournament full) | Show "Tournament is full" message; the lobby auto-refresh will hide the button on the refreshed item. |
65
+ | Other non-zero / unknown | Show generic error toast with `err_message` if any. |
66
+
67
+ ## Per-code error message strategy
68
+
69
+ | `err_code` | Use `err_message`? | Use operator copy? | Best UX text source |
70
+ |---|---|---|---|
71
+ | `0` (success) | — | — | `"You're registered in {tournament.name}"` (localized) |
72
+ | `30005` (segment mismatch) | Fallback only | **Preferred** | `tournament.segment_dont_match_message` → `err_message` → localized generic |
73
+ | `30002` / `300010` / `300011` (no balance) | Fallback | — | Localized `"Insufficient {currency}"` with deficit amount derived from `tournament.registration_cost_*` and the user's profile balance |
74
+ | `30008` (full) | Fallback | — | Localized `"Tournament is full"` |
75
+ | All other `3000x` | Fallback | — | Localized error string for the code → fall back to `err_message` |
76
+
77
+ `segment_dont_match_message` is a field on the root `TTournament`
78
+ object — read it directly as `tournament.segment_dont_match_message`.
79
+
80
+ ## Clan tournament gating
81
+
82
+ When `tournament.is_clan_based === true`, the user must already be in
83
+ a clan before calling `registerInTournament`. Two paths:
84
+
85
+ 1. **Pre-call check** (recommended): in the click handler, read the
86
+ user's clan membership (e.g. via {@link getUserProfile}). If they
87
+ have no clan, open a clan-pick modal (driven by
88
+ [`getClans`](../../api/classes/WSAPIClans.md#getclans) +
89
+ [`joinClan`](../../api/classes/WSAPIClans.md#joinclan)). After
90
+ `joinClan` resolves, call `registerInTournament` — typically
91
+ automatically, without requiring a second click.
92
+ 2. **Server-side safety net**: if a race occurs (e.g. the user leaves
93
+ their clan between the pre-check and the server-side check), the
94
+ server returns `err_code = 1010`. Handle this the same way —
95
+ re-open the clan-pick flow, then retry.
96
+
97
+ The default Smartico UI uses approach 1 with 2 as a fallback.
98
+
99
+ ## Optimistic update
100
+
101
+ **Do NOT optimistically flip `is_user_registered`** before the
102
+ response. The local tournament object should only update when the
103
+ auto-refreshed lobby array arrives via `getTournamentsList`'s
104
+ `onUpdate` callback. The `registering` in-flight flag provides
105
+ sufficient visual feedback during the wait.
106
+
107
+ If you DO use an optimistic state (for instant UI feedback), reconcile
108
+ on the response: if `err_code !== 0` AND `err_code !== 30004`, revert
109
+ the optimistic flip. Most consumers should skip optimistic UI for
110
+ this mutation — the latency is short enough that a loading spinner
111
+ suffices.
112
+
113
+ ## Refresh after response
114
+
115
+ The SDK auto-refreshes `getTournamentsList` after every response
116
+ (success or error) via a server push. The `onUpdate` callback on
117
+ `getTournamentsList({ onUpdate })` fires with the refreshed array
118
+ shortly after `registerInTournament` resolves. **Don't manually call
119
+ `getTournamentsList()` after a register response — the cache is
120
+ already being refreshed.**
121
+
122
+ The detail view (`getTournamentInstanceInfo`) is NOT auto-refreshed.
123
+ If the detail screen is open, call it again to get the updated
124
+ leaderboard, registration count, and `me` block.
125
+
126
+ The user's balance (after a buy-in deduction) updates via the
127
+ SDK's user-properties channel — subscribe to user-property
128
+ changes separately if your UI shows the balance.
129
+
130
+ ## Animations / transitions
131
+
132
+ - **Loading dots**: 3-dot bouncing animation on the button while the
133
+ call is in flight.
134
+ - **Success celebration**: when `is_user_registered` flips from
135
+ `false` to `true` on the refreshed lobby item, play a
136
+ check-mark scale-in on the registered card. The default Smartico UI
137
+ also fires a success toast with the tournament name.
138
+ - **Error modal**: dismissable; auto-close after ~5 s for
139
+ recoverable errors (`30002`, `300010`, `300011`); persistent for
140
+ `30005` (operator-supplied message is usually important).
141
+
142
+ ## Mobile vs desktop
143
+
144
+ - **Loading state**: identical (dots animation on the button).
145
+ - **Error surfacing**: desktop typically uses a toast/snackbar;
146
+ mobile may use a centered modal with a clear "OK" affordance.
147
+ - **Clan-pick flow**: full-screen slide-up sheet on mobile;
148
+ centered modal on desktop.
149
+
150
+ ## Performance
151
+
152
+ - Single round-trip per click. No batching needed.
153
+ - The lobby refresh (server push + SDK cache write + `onUpdate` fire)
154
+ adds ~50–100 ms after the response — fine for UI purposes.
155
+ - Don't poll `getTournamentsList` after a register response — the
156
+ refresh is already happening.