@smartico/public-api 0.0.379 → 0.0.381

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 (219) hide show
  1. package/.nvmrc +1 -1
  2. package/CLAUDE.md +103 -0
  3. package/README.md +53 -2
  4. package/dist/Base/ClassId.d.ts +2 -0
  5. package/dist/Jackpots/JackpotDetails.d.ts +14 -15
  6. package/dist/Jackpots/JackpotPot.d.ts +10 -6
  7. package/dist/Raffle/GetRaffleWonPrizesRequest.d.ts +9 -0
  8. package/dist/Raffle/GetRaffleWonPrizesResponse.d.ts +52 -0
  9. package/dist/Raffle/index.d.ts +2 -0
  10. package/dist/SmarticoAPI.d.ts +6 -1
  11. package/dist/SmarticoLib/index.d.ts +5 -1
  12. package/dist/Store/BuyStoreItemErrorCode.d.ts +9 -1
  13. package/dist/Tournaments/TournamentRegistrationError.d.ts +3 -1
  14. package/dist/WSAPI/WSAPI.d.ts +13 -1235
  15. package/dist/WSAPI/WSAPIAvatars.d.ts +296 -0
  16. package/dist/WSAPI/WSAPIBase.d.ts +71 -0
  17. package/dist/WSAPI/WSAPIBonuses.d.ts +240 -0
  18. package/dist/WSAPI/WSAPIClans.d.ts +388 -0
  19. package/dist/WSAPI/WSAPIGamePick.d.ts +593 -0
  20. package/dist/WSAPI/WSAPIGeneral.d.ts +172 -0
  21. package/dist/WSAPI/WSAPIInbox.d.ts +615 -0
  22. package/dist/WSAPI/WSAPIJackpots.d.ts +344 -0
  23. package/dist/WSAPI/WSAPILeaderBoard.d.ts +139 -0
  24. package/dist/WSAPI/WSAPIMiniGames.d.ts +510 -0
  25. package/dist/WSAPI/WSAPIMissions.d.ts +517 -0
  26. package/dist/WSAPI/WSAPIRaffles.d.ts +643 -0
  27. package/dist/WSAPI/WSAPIStore.d.ts +656 -0
  28. package/dist/WSAPI/WSAPITournaments.d.ts +501 -0
  29. package/dist/WSAPI/WSAPITypes.d.ts +376 -205
  30. package/dist/WSAPI/WSAPIUser.d.ts +533 -0
  31. package/dist/index.js +6745 -1661
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.modern.mjs +6324 -1342
  34. package/dist/index.modern.mjs.map +1 -1
  35. package/docs/Callbacks.md +162 -0
  36. package/docs/SmarticoObject.md +445 -0
  37. package/docs/Transforms.md +99 -0
  38. package/docs/api/README.md +82 -2
  39. package/docs/api/classes/WSAPIAvatars.md +344 -0
  40. package/docs/api/classes/WSAPIBonuses.md +271 -0
  41. package/docs/api/classes/WSAPIClans.md +436 -0
  42. package/docs/api/classes/WSAPIGamePick.md +710 -0
  43. package/docs/api/classes/WSAPIGeneral.md +212 -0
  44. package/docs/api/classes/WSAPIInbox.md +801 -0
  45. package/docs/api/classes/WSAPIJackpots.md +439 -0
  46. package/docs/api/classes/WSAPILeaderBoard.md +157 -0
  47. package/docs/api/classes/WSAPIMiniGames.md +612 -0
  48. package/docs/api/classes/WSAPIMissions.md +590 -0
  49. package/docs/api/classes/WSAPIRaffles.md +773 -0
  50. package/docs/api/classes/WSAPIStore.md +719 -0
  51. package/docs/api/classes/WSAPITournaments.md +571 -0
  52. package/docs/api/classes/WSAPIUser.md +637 -0
  53. package/docs/api/documents/Callbacks.md +162 -0
  54. package/docs/api/documents/SmarticoObject.md +445 -0
  55. package/docs/api/documents/Transforms.md +99 -0
  56. package/docs/api/enumerations/AchievementClaimPeriodTypeId.md +13 -0
  57. package/docs/api/enumerations/BonusStatus-1.md +11 -6
  58. package/docs/api/enumerations/BuyStoreItemErrorCode.md +32 -0
  59. package/docs/api/enumerations/JoinClanErrorCode.md +81 -0
  60. package/docs/api/enumerations/TournamentRegistrationError.md +12 -0
  61. package/docs/api/functions/clanTournamentPlayersTransform.md +13 -0
  62. package/docs/api/interfaces/AvatarCustomized.md +25 -0
  63. package/docs/api/interfaces/AvatarDefinition.md +94 -0
  64. package/docs/api/interfaces/AvatarPrompt.md +45 -0
  65. package/docs/api/interfaces/BonusMetaMap-1.md +4 -1
  66. package/docs/api/interfaces/BonusTemplateMetaMap-1.md +9 -4
  67. package/docs/api/interfaces/Clan.md +4 -0
  68. package/docs/api/interfaces/ClanInfo.md +109 -0
  69. package/docs/api/interfaces/ClanMember.md +53 -0
  70. package/docs/api/interfaces/ClanPrizePublicMeta.md +41 -0
  71. package/docs/api/interfaces/ClanPrizeStructureEntry.md +7 -1
  72. package/docs/api/interfaces/ClanPublicMeta.md +4 -0
  73. package/docs/api/interfaces/ClanTournamentPlayerRaw.md +67 -0
  74. package/docs/api/interfaces/GemsDiamondsLog.md +43 -0
  75. package/docs/api/interfaces/GetClanInfoResponse.md +61 -0
  76. package/docs/api/interfaces/GetClanTournamentPlayersResponse.md +81 -0
  77. package/docs/api/interfaces/GetRaffleWonPrizesRequest.md +59 -0
  78. package/docs/api/interfaces/GetRaffleWonPrizesResponse.md +95 -0
  79. package/docs/api/interfaces/InboxMarkMessageAction.md +10 -2
  80. package/docs/api/interfaces/JackpotDetails.md +14 -15
  81. package/docs/api/interfaces/JackpotPot.md +9 -6
  82. package/docs/api/interfaces/LeaderBoardDetailsT.md +14 -8
  83. package/docs/api/interfaces/LeaderBoardUserT.md +8 -5
  84. package/docs/api/interfaces/LeaderBoardsRewardsT.md +7 -0
  85. package/docs/api/interfaces/PointsLog.md +43 -0
  86. package/docs/api/interfaces/RaffleWonPrize.md +59 -0
  87. package/docs/api/interfaces/RaffleWonPrizePublicMeta.md +27 -0
  88. package/docs/api/interfaces/RaffleWonPrizeUser.md +43 -0
  89. package/docs/api/interfaces/SAWTemplateUI.md +10 -0
  90. package/docs/api/interfaces/TAchCategory.md +14 -4
  91. package/docs/api/interfaces/TAvatarCustomized.md +4 -4
  92. package/docs/api/interfaces/TAvatarDefinition.md +13 -14
  93. package/docs/api/interfaces/TAvatarPrompt.md +6 -6
  94. package/docs/api/interfaces/TBonus.md +16 -8
  95. package/docs/api/interfaces/TBuyStoreItemResult.md +4 -11
  96. package/docs/api/interfaces/TClaimBonusResult.md +5 -5
  97. package/docs/api/interfaces/TClan.md +8 -4
  98. package/docs/api/interfaces/TClanInfo.md +47 -0
  99. package/docs/api/interfaces/TClanJoinResult.md +11 -1
  100. package/docs/api/interfaces/TClanTournamentPlayer.md +55 -0
  101. package/docs/api/interfaces/TClanTournamentPlayersResult.md +27 -0
  102. package/docs/api/interfaces/TClans.md +3 -1
  103. package/docs/api/interfaces/TGetTranslations.md +4 -0
  104. package/docs/api/interfaces/TInboxMessage.md +14 -6
  105. package/docs/api/interfaces/TInboxMessageBody.md +18 -12
  106. package/docs/api/interfaces/TLevel.md +17 -20
  107. package/docs/api/interfaces/TLevelCurrent.md +19 -18
  108. package/docs/api/interfaces/TMiniGamePlayBatchResult.md +14 -6
  109. package/docs/api/interfaces/TMiniGamePlayResult.md +9 -4
  110. package/docs/api/interfaces/TMissionClaimRewardResult.md +6 -3
  111. package/docs/api/interfaces/TMissionOptInResult.md +6 -3
  112. package/docs/api/interfaces/TMissionOrBadge.md +5 -4
  113. package/docs/api/interfaces/TRaffleOptinResponse.md +4 -2
  114. package/docs/api/interfaces/TSegmentCheckResult.md +10 -0
  115. package/docs/api/interfaces/TSetAvatarResult.md +3 -3
  116. package/docs/api/interfaces/TStoreItem.md +11 -1
  117. package/docs/api/interfaces/TTournament.md +1 -1
  118. package/docs/api/interfaces/TTournamentDetailed.md +9 -1
  119. package/docs/api/interfaces/TTournamentRegistrationResult.md +4 -2
  120. package/docs/api/interfaces/TUICustomSection.md +23 -19
  121. package/docs/api/interfaces/TUserProfile.md +18 -16
  122. package/docs/api/interfaces/TransformedRaffleClaimPrizeResponse.md +6 -2
  123. package/docs/api/interfaces/UserAchievement.md +1 -1
  124. package/docs/api/interfaces/UserLevelExtraCountersT.md +9 -2
  125. package/docs/api/type-aliases/ActivityLogEntry.md +3 -0
  126. package/docs/native/ADDING_METHODS.md +7 -2
  127. package/docs/native/PROTOCOL.md +337 -0
  128. package/docs/ui/avatars/UIGuide_getAvatarPrompts.md +100 -0
  129. package/docs/ui/avatars/UIGuide_getAvatarsCustomized.md +90 -0
  130. package/docs/ui/avatars/UIGuide_getAvatarsList.md +105 -0
  131. package/docs/ui/avatars/UIGuide_setAvatar.md +118 -0
  132. package/docs/ui/bonuses/UIGuide_claimBonus.md +144 -0
  133. package/docs/ui/bonuses/UIGuide_getBonuses.md +176 -0
  134. package/docs/ui/clans/UIGuide_getClanInfo.md +153 -0
  135. package/docs/ui/clans/UIGuide_getClans.md +172 -0
  136. package/docs/ui/clans/UIGuide_joinClan.md +196 -0
  137. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRound.md +99 -0
  138. package/docs/ui/gamepick/UIGuide_gamePickGetActiveRounds.md +76 -0
  139. package/docs/ui/gamepick/UIGuide_gamePickGetBoard.md +81 -0
  140. package/docs/ui/gamepick/UIGuide_gamePickGetGameInfo.md +78 -0
  141. package/docs/ui/gamepick/UIGuide_gamePickGetHistory.md +55 -0
  142. package/docs/ui/gamepick/UIGuide_gamePickGetRoundInfoForUser.md +71 -0
  143. package/docs/ui/gamepick/UIGuide_gamePickGetUserInfo.md +64 -0
  144. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelection.md +103 -0
  145. package/docs/ui/gamepick/UIGuide_gamePickSubmitSelectionQuiz.md +67 -0
  146. package/docs/ui/general/UIGuide_getCustomSections.md +137 -0
  147. package/docs/ui/general/UIGuide_getRelatedItemsForGame.md +140 -0
  148. package/docs/ui/inbox/UIGuide_deleteInboxMessage.md +135 -0
  149. package/docs/ui/inbox/UIGuide_getInboxMessageBody.md +133 -0
  150. package/docs/ui/inbox/UIGuide_getInboxMessages.md +186 -0
  151. package/docs/ui/inbox/UIGuide_markInboxMessageAsRead.md +122 -0
  152. package/docs/ui/inbox/UIGuide_markUnmarkInboxMessageAsFavorite.md +84 -0
  153. package/docs/ui/jackpots/UIGuide_getJackpotEligibleGames.md +73 -0
  154. package/docs/ui/jackpots/UIGuide_getJackpotWinners.md +83 -0
  155. package/docs/ui/jackpots/UIGuide_jackpotGet.md +138 -0
  156. package/docs/ui/jackpots/UIGuide_jackpotOptIn.md +85 -0
  157. package/docs/ui/jackpots/UIGuide_jackpotOptOut.md +65 -0
  158. package/docs/ui/leaderboard/UIGuide_getLeaderBoard.md +166 -0
  159. package/docs/ui/minigames/UIGuide_getMiniGames.md +153 -0
  160. package/docs/ui/minigames/UIGuide_getMiniGamesHistory.md +90 -0
  161. package/docs/ui/minigames/UIGuide_playMiniGame.md +150 -0
  162. package/docs/ui/minigames/UIGuide_playMiniGameBatch.md +128 -0
  163. package/docs/ui/missions/UIGuide_getAchCategories.md +17 -0
  164. package/docs/ui/missions/UIGuide_getBadges.md +237 -0
  165. package/docs/ui/missions/UIGuide_getMissions.md +268 -0
  166. package/docs/ui/missions/UIGuide_requestMissionClaimReward.md +21 -0
  167. package/docs/ui/missions/UIGuide_requestMissionOptIn.md +16 -0
  168. package/docs/ui/raffles/UIGuide_claimRafflePrize.md +99 -0
  169. package/docs/ui/raffles/UIGuide_getRaffleDrawRun.md +117 -0
  170. package/docs/ui/raffles/UIGuide_getRaffleDrawRunsHistory.md +92 -0
  171. package/docs/ui/raffles/UIGuide_getRaffleWonPrizes.md +120 -0
  172. package/docs/ui/raffles/UIGuide_getRaffles.md +175 -0
  173. package/docs/ui/raffles/UIGuide_requestRaffleOptin.md +114 -0
  174. package/docs/ui/store/UIGuide_buyStoreItem.md +133 -0
  175. package/docs/ui/store/UIGuide_getStoreCategories.md +19 -0
  176. package/docs/ui/store/UIGuide_getStoreItems.md +264 -0
  177. package/docs/ui/store/UIGuide_getStorePurchasedItems.md +122 -0
  178. package/docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md +140 -0
  179. package/docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md +265 -0
  180. package/docs/ui/tournaments/UIGuide_getTournamentsList.md +236 -0
  181. package/docs/ui/tournaments/UIGuide_registerInTournament.md +156 -0
  182. package/docs/ui/user/UIGuide_getActivityLog.md +130 -0
  183. package/docs/ui/user/UIGuide_getLevels.md +167 -0
  184. package/docs/ui/user/UIGuide_getUserProfile.md +213 -0
  185. package/package.json +3 -3
  186. package/scripts/postdoc-cleanup.js +195 -0
  187. package/src/Base/ClassId.ts +2 -0
  188. package/src/Jackpots/JackpotDetails.ts +14 -15
  189. package/src/Jackpots/JackpotPot.ts +10 -6
  190. package/src/Missions/MissionsUtils.ts +5 -2
  191. package/src/Raffle/GetRaffleWonPrizesRequest.ts +12 -0
  192. package/src/Raffle/GetRaffleWonPrizesResponse.ts +56 -0
  193. package/src/Raffle/index.ts +3 -1
  194. package/src/SmarticoAPI.ts +14 -1
  195. package/src/SmarticoLib/index.ts +4 -0
  196. package/src/Store/BuyStoreItemErrorCode.ts +8 -0
  197. package/src/Store/StoreItem.ts +1 -0
  198. package/src/Store/StoreItemPurchased.ts +1 -0
  199. package/src/Store/StoreItemType.ts +1 -1
  200. package/src/Tournaments/TournamentRegistrationError.ts +4 -0
  201. package/src/WSAPI/WSAPI.ts +27 -2007
  202. package/src/WSAPI/WSAPIAvatars.ts +345 -0
  203. package/src/WSAPI/WSAPIBase.ts +93 -0
  204. package/src/WSAPI/WSAPIBonuses.ts +268 -0
  205. package/src/WSAPI/WSAPIClans.ts +417 -0
  206. package/src/WSAPI/WSAPIGamePick.ts +667 -0
  207. package/src/WSAPI/WSAPIGeneral.ts +199 -0
  208. package/src/WSAPI/WSAPIInbox.ts +711 -0
  209. package/src/WSAPI/WSAPIJackpots.ts +448 -0
  210. package/src/WSAPI/WSAPILeaderBoard.ts +154 -0
  211. package/src/WSAPI/WSAPIMiniGames.ts +597 -0
  212. package/src/WSAPI/WSAPIMissions.ts +572 -0
  213. package/src/WSAPI/WSAPIRaffles.ts +703 -0
  214. package/src/WSAPI/WSAPIStore.ts +732 -0
  215. package/src/WSAPI/WSAPITournaments.ts +541 -0
  216. package/src/WSAPI/WSAPITypes.ts +376 -207
  217. package/src/WSAPI/WSAPIUser.ts +606 -0
  218. package/tsconfig.json +26 -2
  219. package/docs/api/classes/WSAPI.md +0 -1927
@@ -0,0 +1,166 @@
1
+ # UI Guide — `getLeaderBoard`
2
+
3
+ ## Overview
4
+ - Returns the top-20 ranked entries for one period, the current user's
5
+ own rank, and the configured prize table.
6
+ - One call per (period type, current-or-previous) — the SDK caches
7
+ under a single key, so switching periods or current/previous within
8
+ 30 s returns stale cached data unless `clearCaches()` is invoked.
9
+ - No subscription, no push refresh. Poll manually if the consumer
10
+ needs live updates during an in-progress period.
11
+
12
+ ## List view organization
13
+
14
+ The default Smartico UI renders the leaderboard with three top-level
15
+ tabs inside the page:
16
+
17
+ | Tab | Content |
18
+ |---|---|
19
+ | **Leaders** | The ranked `users[]` list + sticky "me" row |
20
+ | **Prizes** | The `rewards[]` table — one row per paid place |
21
+ | **Rules** | The `rules` HTML (operator-defined terms) |
22
+
23
+ A separate **period selector** sits above the tabs — one tab per
24
+ configured board (DAILY / WEEKLY / MONTHLY). Switching periods
25
+ dispatches a new `getLeaderBoard()` call.
26
+
27
+ A **current ↔ previous** toggle button sits below the Leaders list.
28
+ Label depends on the active period: "Yesterday's winners" /
29
+ "Last week's winners" / "Previous month's winners" / "Today's
30
+ leaders" / "Current standings".
31
+
32
+ ## Rank table layout
33
+
34
+ Columns per row (in the Leaders tab):
35
+
36
+ | Column | Source | Notes |
37
+ |---|---|---|
38
+ | Position | `user.position` | Server-computed DENSE_RANK. Show as `01`, `02`, … `20`. |
39
+ | Avatar | `user.avatar_url` | Hide via operator config `hide_avatars_leaderboards` if applicable. |
40
+ | Username | `user.public_username` | |
41
+ | Points | `user.points` | Operator config `leaderboard_hide_other_points` may suppress points for everyone except the current user. |
42
+ | Level badge | (Drives from separate `getCurrentLevel` lookup, not on this type) | Operator config `leaderboard_hide_levels` may suppress. |
43
+
44
+ **Top-3 podium styling**: apply distinct CSS classes (`place-1`,
45
+ `place-2`, `place-3`) to rows with `position <= 3` (and
46
+ `position !== -1`). The default Smartico UI uses CSS-only treatment
47
+ — no podium banner or 1st/2nd/3rd icons.
48
+
49
+ **Desktop padding**: when `users.length < 8`, the default UI pads the
50
+ list height with a vertical spacer so the card height stays stable
51
+ across periods. Skip this on mobile.
52
+
53
+ ## "Me" sticky row
54
+
55
+ `board.me` is the current authenticated user's entry. Render rules:
56
+
57
+ - **Visitor mode**: `me` is `undefined` — skip the sticky row.
58
+ - **`me.position === -1`**: the user has not yet accumulated points
59
+ this period (or is otherwise unranked). Show a friendly state:
60
+ `"You're unranked — earn points to climb the board"`.
61
+ - **`me.position > 0` AND visible in `users[]` (top 20)**: highlight
62
+ the matching row in the main list (CSS class). The default
63
+ Smartico UI also keeps a sticky bottom row for consistency.
64
+ - **`me.position > 20`**: pin the sticky bottom row with the user's
65
+ rank, avatar, username, and points. The default Smartico UI shows
66
+ this on desktop only (mobile uses a stub spacer to keep layout
67
+ height stable but does not render the sticky row).
68
+
69
+ ## Prize table (Prizes tab)
70
+
71
+ Render `rewards[]` as a two-column table:
72
+
73
+ | Column | Source |
74
+ |---|---|
75
+ | Place | `reward.place` (e.g. `1`, `2`, `3`) formatted as "1st place", "2nd place", etc. — use the locale's plural-ordinal helper for proper rendering. |
76
+ | Prize | `reward.points` formatted with locale separators + the brand's points unit (e.g. `"100 points"`, `"1,000 pts"`). |
77
+
78
+ Show in order — the first array element is place 1.
79
+
80
+ The default Smartico UI does NOT inline rewards on the main leaders
81
+ list (only in the dedicated Prizes tab). Custom UIs may want to
82
+ inline a hint on the top-3 rows.
83
+
84
+ ## Rules tab
85
+
86
+ Render `board.rules` as sanitized HTML. The default Smartico UI uses
87
+ `dangerouslySetInnerHTML` — **sanitize before injecting** to
88
+ prevent XSS if your product surface is exposed to untrusted operator
89
+ content.
90
+
91
+ ## Period selector (current ↔ previous)
92
+
93
+ When the user switches between current and previous period:
94
+
95
+ 1. Call `clearCaches()` to bust the shared OCache key
96
+ (otherwise the second call returns stale data within 30 s).
97
+ 2. Call `getLeaderBoard(periodType, getPreviousPeriod)` with the new
98
+ `getPreviousPeriod` value.
99
+ 3. Replace the rendered data.
100
+
101
+ ```ts
102
+ const onTogglePrevious = async (showPrevious: boolean) => {
103
+ await window._smartico.api.clearCaches();
104
+ const board = await window._smartico.api.getLeaderBoard(
105
+ activePeriodType,
106
+ showPrevious,
107
+ );
108
+ console.log('[smartico] switched to', showPrevious ? 'previous' : 'current', 'period — re-render with this board:', board);
109
+ };
110
+ ```
111
+
112
+ Same pattern applies when switching between period types
113
+ (`DAILY` → `WEEKLY` → `MONTHLY`).
114
+
115
+ ## Status-specific visual treatments
116
+
117
+ - **Current period**: full-color rendering; live point counts.
118
+ - **Previous period** (`getPreviousPeriod: true`): identical
119
+ rendering — the default Smartico UI does not visually
120
+ differentiate. Custom UIs may want to apply subtle desaturation
121
+ or an "ENDED" pill to signal that the rankings are final.
122
+ - **Unranked `me`** (`position === -1`): friendly "earn points to
123
+ enter the board" copy in the sticky panel.
124
+
125
+ ## Empty / loading / error states
126
+
127
+ - **Loading (cold fetch)**: render a skeleton table (8 placeholder
128
+ rows) sized to the eventual layout. The default Smartico UI uses
129
+ a `Loader` spinner.
130
+ - **Empty `users[]`**: render a Lottie / static illustration with
131
+ copy like "No Leaders Yet". Common early in a fresh period.
132
+ - **`board === undefined`** (no board configured for this period):
133
+ hide the leaderboard surface entirely or surface a neutral
134
+ "Leaderboard not available" message.
135
+ - **Error**: keep the prior list if any; show a non-blocking error
136
+ banner; retry on the next user-driven action.
137
+
138
+ ## Animations / transitions
139
+
140
+ - **Initial fade-in**: rows fade in on first paint.
141
+ - **Rank change between fetches**: the default Smartico UI does NOT
142
+ animate rank changes — rows re-render in-place. Custom UIs that
143
+ poll may want to diff snapshots and apply a FLIP animation to
144
+ highlight position movement.
145
+ - **Period switch**: cross-fade table contents (~150 ms ease-out).
146
+
147
+ ## Mobile vs desktop
148
+
149
+ - **Period tab strip**: desktop uses a standard tab component;
150
+ mobile uses a scrollable custom-styled strip.
151
+ - **Sticky "me" row**: desktop renders it; mobile renders only a
152
+ stub spacer (no sticky row, to save vertical space).
153
+ - **Desktop padding**: list height padded to ≥ 8 rows; mobile does
154
+ not pad.
155
+
156
+ ## Performance
157
+
158
+ - The 30 s cache deduplicates rapid refetches, but the single-key
159
+ design means switching periods within the window returns stale
160
+ data. `clearCaches()` is the only way to force a fresh fetch
161
+ before the TTL expires.
162
+ - For live polling during an in-progress period, ~30 s cadence is
163
+ appropriate (matching the cache TTL — additional polls within
164
+ the window are free).
165
+ - The leaderboard list is small (max 20 rows + 1 me row); no
166
+ virtualization needed.
@@ -0,0 +1,153 @@
1
+ # UI Guide — `getMiniGames`
2
+
3
+ ## Overview
4
+ - Returns the operator-configured mini-game templates ("SAW" =
5
+ Spin And Win — covers wheel, scratch card, lootbox, gift box,
6
+ treasure hunt, plinko, coin flip, quiz, and several other formats).
7
+ - Templates carry per-user state (`spin_count`,
8
+ `next_available_spin_ts`, `jackpot_current`) — these update via
9
+ `onUpdate` after any spin / acknowledge / server-pushed grant.
10
+ - The list is NOT filtered by spin availability — apply
11
+ `visibile_when_can_spin` client-side to hide templates the user
12
+ can't currently play.
13
+ - Visitor mode supported.
14
+
15
+ ## List view organization
16
+
17
+ The default Smartico UI presents mini-game templates as a **flat
18
+ list** (no per-game-type tabs). Templates of different
19
+ `saw_game_type` values appear together, sorted by the operator's
20
+ configured priority.
21
+
22
+ Client-side visibility filter:
23
+
24
+ ```ts
25
+ const visible = games.filter(g =>
26
+ !g.visibile_when_can_spin || (g.spin_count && g.spin_count > 0)
27
+ );
28
+ ```
29
+
30
+ `visibile_when_can_spin` is operator-configured per template. When
31
+ `true`, the template should be hidden from the lobby until the user
32
+ has spins (or balance for paid templates).
33
+
34
+ ## Item card
35
+
36
+ Fields rendered per game card:
37
+
38
+ | Field | Source | Notes |
39
+ |---|---|---|
40
+ | Thumbnail | `thumbnail` (256×256 px source from template UI definition) | Render at 1:1 aspect ratio. |
41
+ | Name | `name` | Server-side `{{jackpot}}` substitution inlines the live jackpot value. |
42
+ | Description | `description` | Short marketing tagline. |
43
+ | Game-type pill | `saw_game_type` | E.g. "Spin", "Scratch", "Lootbox" — translate the enum value to a display label. |
44
+ | Spin-count badge | `spin_count` (only when `saw_buyin_type === 'spins'`) | Show as a chip on the card. |
45
+ | Buy-in indicator | `saw_buyin_type` + `buyin_cost_points` / `_gems` / `_diamonds` | "Free", "100 pts", "5 gems", etc. |
46
+ | Jackpot display | `jackpot_current` + `jackpot_symbol` | Only when `jackpot_add_on_attempt > 0`. Animate the value count-up when it changes via push. |
47
+ | Promo image | `promo_image` (500×240 px) | Optional larger artwork for featured / hero rendering. |
48
+ | Countdown | `next_available_spin_ts` (epoch ms) | Show "Available in HH:MM:SS" when set and in the future. |
49
+ | Play CTA | (derived) | See "Action button decision matrix". |
50
+
51
+ **Whole-card click target**: opens the detail / play view for that
52
+ template.
53
+
54
+ ## Detail / play view per game type
55
+
56
+ The `saw_game_type` value drives the renderer:
57
+
58
+ | `SAWGameType` | Renderer | Behavior |
59
+ |---|---|---|
60
+ | `SpinAWheel` (1) | Canvas wheel; spin to a stop position on a 360° track | ~15 s animation; bezier ease-out |
61
+ | `ScratchCard` (2) | Canvas scratch-off reveal | User drags / taps to scratch; prize revealed underneath |
62
+ | `MatchX` (3) | Score-prediction UI (separate games server) | DON'T call `playMiniGame` — the games server fires the spin internally |
63
+ | `GiftBox` (4) | Tap-to-reveal gift boxes | Bubbles layout; tap chooses a box |
64
+ | `PrizeDrop` (5) | None (push-only) | Don't call `playMiniGame`; prizes arrive via push |
65
+ | `Quiz` (6) | Q&A UI (separate games server) | DON'T call `playMiniGame` |
66
+ | `LootboxWeekdays` (7) | Pick-N-from-grid | Grid layout or vertical-map (`game_layout` determines) |
67
+ | `LootboxCalendarDays` (8) | Pick-N-from-grid | Same; pool filtered to current calendar date |
68
+ | `TreasureHunt` (9) | Map exploration | `steps_to_finish_game` cells |
69
+ | `Voyager` (10) | Journey UI | `min_steps_to_finish_game` floor |
70
+ | `Plinko` (11) | Physics ball-drop | Animated drop through pegs |
71
+ | `CoinFlip` (12) | Binary coin flip | Heads-or-tails reveal |
72
+
73
+ The default Smartico UI uses a lazy-loaded component dispatcher
74
+ keyed on `saw_game_type`. For custom UIs, render the matching
75
+ component per game type — they share the same `playMiniGame`
76
+ trigger (except `PrizeDrop`, `MatchX`, `Quiz` as noted).
77
+
78
+ ## Action button decision matrix
79
+
80
+ CTA on the card / detail view (evaluate in priority order):
81
+
82
+ | Condition | CTA | Notes |
83
+ |---|---|---|
84
+ | `next_available_spin_ts > now` | "Available in HH:MM:SS" (countdown, disabled) | Countdown to next allowed spin |
85
+ | `saw_buyin_type === 'spins'` AND `spin_count === 0` | "No spins" (disabled) | Show `no_attempts_message` from the template |
86
+ | Insufficient balance (paid templates) | "Not enough {currency}" (disabled) | Compare `buyin_cost_*` against `getUserProfile` balance |
87
+ | User not in segment / template expired | (hidden — server filtered) | Template typically excluded by server visibility |
88
+ | Default (playable) | "Play" / "Spin" | Enabled — invokes `playMiniGame` |
89
+
90
+ `over_limit_message` and `no_attempts_message` are operator-supplied
91
+ messages; surface them under the disabled CTA rather than a generic
92
+ error.
93
+
94
+ ## Image / asset specs
95
+
96
+ | Field | Aspect | Notes |
97
+ |---|---|---|
98
+ | `thumbnail` | 1:1 (square), 256×256 px | Card icon |
99
+ | `promo_image` | ~2:1 wide, 500×240 px | Featured / hero artwork |
100
+ | `prizes[].icon` | 1:1 (square) | Per-prize icon |
101
+
102
+ ## Status-specific visual treatments
103
+
104
+ - **Playable**: full-color card; Play CTA enabled.
105
+ - **No spins / insufficient balance**: card desaturated or dimmed;
106
+ CTA disabled with operator message.
107
+ - **Countdown**: card normal; CTA replaced with countdown timer
108
+ (update every second).
109
+ - **`visibile_when_can_spin` filter**: card hidden entirely.
110
+ - **Jackpot increasing live**: pulse / glow the jackpot value when
111
+ it ticks up via push (detect by diffing previous snapshot in
112
+ `onUpdate`).
113
+
114
+ ## Countdown / timing format
115
+
116
+ `next_available_spin_ts` — epoch ms — drives the countdown. Use:
117
+
118
+ - `> 1 hour` → `"HH:MM"` ("01:23")
119
+ - `1 minute – 1 hour` → `"MM:SS"`
120
+ - `< 1 minute` → `"0:SS"`, ticking down per second
121
+
122
+ Update once per second while visible. Hide the countdown and show
123
+ the Play CTA when the timestamp passes.
124
+
125
+ ## Empty / loading / error states
126
+
127
+ - **Loading**: skeleton grid (3–6 placeholder cards).
128
+ - **Empty**: `[]` means no templates configured. Render a neutral
129
+ empty-state.
130
+ - **Error**: keep prior list if any; show non-blocking error banner.
131
+
132
+ ## Animations / transitions
133
+
134
+ - **Card entry**: fade-in stagger.
135
+ - **Jackpot tick**: count-up animation when value increases via
136
+ push (detected by diffing prior snapshot in `onUpdate`).
137
+ - **Spin-count flash**: highlight the spin-count badge when it
138
+ changes (campaign grant, prize win that grants spins).
139
+
140
+ ## Mobile vs desktop
141
+
142
+ - **Card density**: desktop 3–4 columns; mobile 2 columns
143
+ (sometimes 1 for large promo cards).
144
+ - **Detail view**: desktop centered modal; mobile full-screen.
145
+ - **Game canvas**: scales with viewport.
146
+
147
+ ## Performance
148
+
149
+ - 30 s cache deduplicates rapid refetches.
150
+ - Diff snapshots in `onUpdate` to detect jackpot / spin-count
151
+ changes for animations.
152
+ - Lazy-load game-type renderers (each game type is a substantial
153
+ component bundle).
@@ -0,0 +1,90 @@
1
+ # UI Guide — `getMiniGamesHistory`
2
+
3
+ ## Overview
4
+ - Paginated, newest-first list of past spins. Each row carries the
5
+ won prize ID, the spin's `client_request_id`, an `is_claimed`
6
+ flag, and the template that produced it.
7
+ - Use to power a "My prizes" / win-history surface, and to surface
8
+ Claim CTAs on unacknowledged spins (`is_claimed === false`).
9
+ - 30 s cache. No subscription / push refresh.
10
+
11
+ ## List view organization
12
+
13
+ Single flat list, server-sorted newest-first by spin creation
14
+ timestamp. No client-side re-sort required. No tabs in the default
15
+ Smartico UI — but a custom UI might tab by:
16
+
17
+ - "Claimed" vs "Unclaimed" (filter by `is_claimed`)
18
+ - Per-template (use `saw_template_id` filter argument)
19
+
20
+ ## Item card / row
21
+
22
+ Fields rendered per row:
23
+
24
+ | Field | Source | Notes |
25
+ |---|---|---|
26
+ | Template icon | `template.thumbnail` (or similar) | 1:1 |
27
+ | Prize name | Look up `template.prizes[i]` by `saw_prize_id` | Display the prize's `name`. |
28
+ | Prize amount | `template.prizes[i].prize_value` or `prize_amount` | E.g. "100 points" |
29
+ | Spin date | `create_date_ts` | Format as user-local relative time ("2h ago") or short date |
30
+ | Claim status | `is_claimed` | Show check-mark when true; "Pending claim" CTA when false |
31
+ | Acknowledge date | `acknowledge_date_ts` | Only when `is_claimed === true` |
32
+
33
+ **Click target**: typically opens the template detail / play view
34
+ (to "play again" or read prize terms). Optional.
35
+
36
+ ## Action button — Claim CTA on unacknowledged rows
37
+
38
+ When `is_claimed === false`, render a "Claim" button:
39
+
40
+ ```ts
41
+ const onClaimHistoryRow = async (row: TSawHistory) => {
42
+ await window._smartico.api.miniGameWinAcknowledgeRequest(row.client_request_id);
43
+ console.log('[smartico] re-acknowledged spin — refresh history list to flip is_claimed');
44
+ const refreshed = await window._smartico.api.getMiniGamesHistory({ limit: 20 });
45
+ // ...
46
+ };
47
+ ```
48
+
49
+ Note: a server-side fallback job auto-acknowledges stale spins
50
+ every ~60 seconds, so unclaimed rows usually self-resolve without
51
+ explicit user action. The manual Claim CTA is for instant recovery
52
+ when the user is actively viewing the history surface.
53
+
54
+ ## Pagination
55
+
56
+ `limit` defaults to 20; `offset` defaults to 0. For load-more
57
+ pagination, advance `offset` by the prior page size. Detect end of
58
+ list when the returned array length is less than `limit`.
59
+
60
+ `saw_template_id` filter scopes to a single template's history —
61
+ useful for a per-game "previous wins" panel.
62
+
63
+ ## Empty / loading / error states
64
+
65
+ - **Loading (cold fetch)**: skeleton list (3–5 placeholder rows).
66
+ - **Empty**: "No mini-game history yet" with optional illustration.
67
+ - **Empty (filtered)**: "No history for this game".
68
+ - **Error**: keep prior list if any; non-blocking error banner.
69
+
70
+ ## Refresh
71
+
72
+ No subscription / push refresh. Re-call after:
73
+
74
+ - A successful `playMiniGame` / `playMiniGameBatch` to surface the
75
+ new row at the top.
76
+ - A successful `miniGameWinAcknowledgeRequest` to flip `is_claimed`.
77
+
78
+ The 30 s cache deduplicates rapid refetches.
79
+
80
+ ## Mobile vs desktop
81
+
82
+ - **Row density**: identical on both.
83
+ - **Claim CTA**: inline button on each row (both platforms).
84
+
85
+ ## Performance
86
+
87
+ - The 30 s cache helps when the user navigates between tabs and
88
+ re-renders the history.
89
+ - Don't over-fetch — limit to 20–50 rows per page.
90
+ - Filter by `saw_template_id` when the surface is per-template.
@@ -0,0 +1,150 @@
1
+ # UI Guide — `playMiniGame`
2
+
3
+ ## Overview
4
+ - Action-focused mutation. User clicks "Play" / "Spin" on a mini-
5
+ game card; the server runs the prize selection and returns the
6
+ won prize.
7
+ - Auto-acknowledges on success (fire-and-forget) — most consumers
8
+ don't need to handle acknowledge manually.
9
+ - The SDK does NOT prevent double-clicks at the public API level;
10
+ guard the call site.
11
+
12
+ ## Loading state — the game animation IS the loading state
13
+
14
+ For most mini-game types, the **game animation itself serves as the
15
+ loading state**. Start the animation immediately on click (before
16
+ the response arrives); the server response arrives during the
17
+ animation; sync the final reveal to land at the animation's end
18
+ position.
19
+
20
+ Typical animation durations by game type:
21
+
22
+ - Wheel: ~15 s (long enough to mask any reasonable latency)
23
+ - Scratch card: user-driven (no time pressure)
24
+ - Lootbox: ~3 s (open animation)
25
+ - Gift box: ~2 s (reveal)
26
+ - Treasure hunt / Voyager / Plinko / Coin flip: ~2–5 s
27
+ - Match X / Quiz: not applicable (separate game server)
28
+
29
+ If the server response arrives late (network slow), pause the
30
+ animation or hold at a "still spinning" loop until the result
31
+ lands.
32
+
33
+ ## Idempotency guard
34
+
35
+ The SDK does NOT prevent double-clicks. Guard the call site:
36
+
37
+ ```ts
38
+ const [playing, setPlaying] = useState(false);
39
+
40
+ const onPlay = async () => {
41
+ if (playing) return;
42
+ setPlaying(true);
43
+ startAnimation();
44
+ const r = await window._smartico.api.playMiniGame(template_id);
45
+ syncAnimationToResult(r);
46
+ setPlaying(false);
47
+ handleResult(r);
48
+ };
49
+ ```
50
+
51
+ **Don't skip this guard** — a double-tap deducts the buy-in twice.
52
+ The server has its own per-user-per-template lock that serialises
53
+ concurrent requests, but it still processes both sequentially.
54
+
55
+ ## Action button decision matrix
56
+
57
+ UI behavior per `err_code`:
58
+
59
+ | `err_code` | Action |
60
+ |---|---|
61
+ | `0` (success) | Animate the result; show acknowledge modal per `template.acknowledge_type` (see below); refresh balance widgets. |
62
+ | `40001` (no spins) | Disable Play; show `no_attempts_message` from the template. |
63
+ | `40002` (prize pool empty) | Disable Play; show "All prizes claimed for now". |
64
+ | `40003` (insufficient points) | Disable Play; show insufficient-balance UI naming the deficit. |
65
+ | `40004` (max spins reached) | Show countdown to next available spin. `first_spin_in_period` is on the batch result type; for the single-play path, derive next-available from `template.next_available_spin_ts`. |
66
+ | `40007` (template not active) | Disable / hide the template. |
67
+ | `40009` (segment mismatch / lootbox empty for today) | Hide the template OR show "Not available today" for lootboxes. |
68
+ | `40011` / `40012` (insufficient gems / diamonds) | Same as `40003` for the matching currency. |
69
+ | `-40001` (visitor stopped) | Silently end the game; no prize modal. Default Smartico UI doesn't surface this as an error. |
70
+ | Other non-zero | Show generic error toast. Allow retry. |
71
+
72
+ ## Prize reveal modals
73
+
74
+ `template.acknowledge_type` controls the post-animation modal:
75
+
76
+ | Value | Behavior |
77
+ |---|---|
78
+ | `Silent` (1) | No modal; auto-acknowledge fires silently. |
79
+ | `QuickMessage` (2) | Toast popup; auto-closes after ~5 s; auto-acknowledge fires. |
80
+ | `FullMessage` (3) | Full-screen modal; user dismisses; auto-acknowledge fires. |
81
+ | `ExplicitAcknowledge` (4) | Modal with explicit "Claim" CTA; acknowledge sends only on tap. Use this for high-value or lootbox-style rewards. |
82
+
83
+ For `ExplicitAcknowledge`, suppress the SDK's auto-acknowledge by
84
+ NOT using this method's `playMiniGame` directly — call the
85
+ acknowledge via [`miniGameWinAcknowledgeRequest`](../../api/classes/WSAPIMiniGames.md#minigamewinacknowledgerequest)
86
+ when the user taps Claim. (The SDK still auto-fires the
87
+ acknowledge from `playMiniGame`, but the visual gate is still
88
+ meaningful.)
89
+
90
+ Modal contents per prize:
91
+
92
+ - Icon: `prize.icon` (1:1)
93
+ - Title: `prize.name`
94
+ - Message: `prize.aknowledge_message` (note the typo in the field
95
+ name — `aknowledge` not `acknowledge`)
96
+ - CTA: `prize.acknowledge_action_title` with action
97
+ `prize.acknowledge_dp` (pass through `_smartico.dp()` for safe
98
+ execution)
99
+ - Optional additional CTA: `prize.acknowledge_action_title_additional`
100
+ + `prize.acknowledge_dp_additional`
101
+
102
+ ## "No prize" handling
103
+
104
+ When the won prize has `prize_type === 'no-prize'`, the user has
105
+ "lost" — the buy-in was still deducted. Show a friendly "Better
106
+ luck next time" treatment rather than a celebration. The operator
107
+ configures these slots intentionally — they're not errors.
108
+
109
+ ## Refresh after success
110
+
111
+ The SDK auto-refreshes the templates cache after every spin. The
112
+ `onUpdate` callback registered via `getMiniGames` fires with the
113
+ updated array — observe the new `spin_count`, `jackpot_current`,
114
+ and `next_available_spin_ts`.
115
+
116
+ The user's balance updates (points / gems / diamonds credited from
117
+ prize wins) flow via
118
+ [`getUserProfile`](../../api/classes/WSAPIUser.md#getuserprofile)'s
119
+ `props_change` channel.
120
+
121
+ ## Per-game-type animation nuances
122
+
123
+ | Game type | Animation pattern |
124
+ |---|---|
125
+ | Spin a Wheel | Start spinning on click; sync the final position to land on the won prize sector at ~15 s. Cubic-bezier deceleration. |
126
+ | Scratch Card | User-driven; the won prize is hidden under the scratch layer. Reveal at scratch completion threshold (~60% scratched). |
127
+ | Gift Box | All boxes shake on play; user taps to "open" their chosen box; the won prize appears in the tapped box. |
128
+ | Lootbox (Weekdays / CalendarDays) | Grid layout; user picks N from grid; pre-determined positions reveal the won prize. |
129
+ | Treasure Hunt | Multi-step map; each play advances one step; final step reveals the prize. |
130
+ | Voyager | Journey-style multi-step. |
131
+ | Plinko | Ball drops through pegs; landing position determines the won prize. Physics-driven path or pre-computed to match server result. |
132
+ | Coin Flip | Binary heads/tails reveal. |
133
+
134
+ The default Smartico UI ships components for all these — for
135
+ custom UIs, the consumer is responsible for the animation per type.
136
+
137
+ ## Mobile vs desktop
138
+
139
+ - **Game canvas**: scales with viewport.
140
+ - **Modal**: desktop centered with backdrop; mobile full-screen.
141
+ - **Touch targets**: mobile needs 44×44 px minimum for game
142
+ interactions.
143
+
144
+ ## Performance
145
+
146
+ - Single round-trip per spin.
147
+ - Pre-load game animation assets at template card render time so
148
+ the animation starts immediately on click.
149
+ - The `onUpdate` callback fires after each spin — use it to refresh
150
+ the card's spin_count badge.
@@ -0,0 +1,128 @@
1
+ # UI Guide — `playMiniGameBatch`
2
+
3
+ ## Overview
4
+ - Plays the same template `spin_count` times in a single
5
+ round-trip. Returns an array of per-spin results.
6
+ - Each result is independent — the server does NOT roll back on
7
+ partial failures.
8
+ - Use for "Spin N times" / "Open N lootboxes" affordances.
9
+
10
+ ## Result shape note
11
+
12
+ `TMiniGamePlayBatchResult` uses **camelCase** `errCode` /
13
+ `errMessage` (differs from `TMiniGamePlayResult` which uses
14
+ `err_code` / `err_message`). Branch on the camelCase keys.
15
+
16
+ ## Loading state
17
+
18
+ For multi-spin batches, the UI needs to choose between:
19
+
20
+ 1. **Sequential animation** — animate each spin one at a time using
21
+ the response array as the "script". The default Smartico UI
22
+ uses this pattern for multi-spin lootboxes. ~2-3 s per spin.
23
+ 2. **Bulk reveal** — animate all spins in parallel (faster but
24
+ visually busy). Suitable for "open 10 lootboxes" power-user
25
+ affordances.
26
+ 3. **Skip animation** — show a results table immediately.
27
+ Suitable for "auto-spin N" power affordances.
28
+
29
+ Set an in-flight flag on click; clear it when the response lands
30
+ AND any animation completes.
31
+
32
+ ## Idempotency guard
33
+
34
+ Same as `playMiniGame` — guard against double-clicks. A
35
+ double-tap sends two batches, each deducting `spin_count` worth of
36
+ buy-in.
37
+
38
+ ## Per-spin result handling
39
+
40
+ Iterate `results[]` and handle each result independently:
41
+
42
+ ```ts
43
+ for (const [i, r] of results.entries()) {
44
+ if (r.errCode === 0) {
45
+ const prize = template.prizes.find(p => p.id === r.saw_prize_id);
46
+ if (prize?.prize_type === 'no-prize') {
47
+ console.log('[smartico] spin', i, 'lost');
48
+ } else {
49
+ console.log('[smartico] spin', i, 'won:', prize?.name);
50
+ }
51
+ } else if (r.errCode === 40001) {
52
+ console.log('[smartico] spin', i, '— no spins left; abort the rest of the animation early');
53
+ break;
54
+ } else if (r.errCode === 40004) {
55
+ const msTillNext = template.max_spins_period_ms! - (Date.now() - (r.first_spin_in_period ?? 0));
56
+ console.log('[smartico] spin', i, 'capped — ' + Math.ceil(msTillNext / 1000) + 's until next');
57
+ } else {
58
+ console.error('[smartico] spin', i, 'failed:', r.errMessage);
59
+ }
60
+ }
61
+ ```
62
+
63
+ ## Partial-success handling
64
+
65
+ Common scenario: user requests 5 spins but only has 3 spin
66
+ tickets. Results[0–2] succeed; results[3–4] fail with
67
+ `SAW_NO_SPINS`.
68
+
69
+ UI options:
70
+
71
+ - Show all 3 successful prizes; then surface a "You ran out of
72
+ spins" message for the remaining 2 attempts.
73
+ - Or skip the failed animations entirely and only render the
74
+ successful results.
75
+
76
+ The default Smartico UI shows successful results then a brief
77
+ "No more spins" toast.
78
+
79
+ ## `first_spin_in_period` for cooldown UX
80
+
81
+ When any result has `errCode === SAW_FAILED_MAX_SPINS_REACHED`
82
+ (40004), the result's `first_spin_in_period` field carries the
83
+ epoch ms of the user's first spin in the current cooldown
84
+ period. Compute the countdown:
85
+
86
+ ```ts
87
+ const msTillNext = template.max_spins_period_ms! - (Date.now() - r.first_spin_in_period!);
88
+ ```
89
+
90
+ Show "Available in HH:MM:SS" countdown on the disabled Play CTA.
91
+
92
+ ## Jackpot wins in batches
93
+
94
+ When a result has `jackpot_amount` set, the user won a jackpot on
95
+ that specific spin. Drain the template's `jackpot_current` in the
96
+ UI (the next `getMiniGames` push will confirm the reset).
97
+
98
+ For multi-jackpot wins in a single batch (rare but possible), sum
99
+ the amounts visually.
100
+
101
+ ## Animations / transitions
102
+
103
+ Sequential animation pattern (most common):
104
+
105
+ 1. Disable the Play CTA, show loading state.
106
+ 2. For each result in order:
107
+ - Trigger the game-type animation (wheel spin, scratch
108
+ reveal, lootbox open, etc.).
109
+ - Sync the final position / reveal to the result's prize.
110
+ - Show a brief prize callout (~500 ms) between spins.
111
+ 3. After the last spin, show a summary modal with total wins.
112
+
113
+ The default Smartico UI uses this pattern for lootboxes; wheel /
114
+ plinko / coin flip typically animate one at a time.
115
+
116
+ ## Mobile vs desktop
117
+
118
+ - **Sequential animation**: identical on both, just smaller canvas
119
+ on mobile.
120
+ - **Summary modal**: desktop centered; mobile full-screen sheet.
121
+
122
+ ## Performance
123
+
124
+ - Single round-trip per batch — much cheaper than N sequential
125
+ `playMiniGame` calls (one WS roundtrip vs N).
126
+ - Pre-determine the animation sequence from the result array
127
+ before starting — don't await each spin's animation completion
128
+ before deciding the next.