@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.
- package/.nvmrc +1 -1
- package/README.md +31 -2
- package/dist/Store/BuyStoreItemErrorCode.d.ts +9 -1
- package/dist/Tournaments/TournamentRegistrationError.d.ts +3 -1
- package/dist/WSAPI/WSAPI.d.ts +13 -1235
- package/dist/WSAPI/WSAPIAvatars.d.ts +78 -0
- package/dist/WSAPI/WSAPIBase.d.ts +71 -0
- package/dist/WSAPI/WSAPIBonuses.d.ts +26 -0
- package/dist/WSAPI/WSAPIClans.d.ts +388 -0
- package/dist/WSAPI/WSAPIGamePick.d.ts +308 -0
- package/dist/WSAPI/WSAPIGeneral.d.ts +30 -0
- package/dist/WSAPI/WSAPIInbox.d.ts +126 -0
- package/dist/WSAPI/WSAPIJackpots.d.ts +109 -0
- package/dist/WSAPI/WSAPILeaderBoard.d.ts +25 -0
- package/dist/WSAPI/WSAPIMiniGames.d.ts +94 -0
- package/dist/WSAPI/WSAPIMissions.d.ts +517 -0
- package/dist/WSAPI/WSAPIRaffles.d.ts +119 -0
- package/dist/WSAPI/WSAPIStore.d.ts +656 -0
- package/dist/WSAPI/WSAPITournaments.d.ts +501 -0
- package/dist/WSAPI/WSAPITypes.d.ts +145 -85
- package/dist/WSAPI/WSAPIUser.d.ts +383 -0
- package/dist/index.js +3818 -1528
- package/dist/index.js.map +1 -1
- package/dist/index.modern.mjs +3180 -981
- package/dist/index.modern.mjs.map +1 -1
- package/docs/api/README.md +56 -2
- package/docs/api/classes/WSAPIAvatars.md +116 -0
- package/docs/api/classes/WSAPIBonuses.md +50 -0
- package/docs/api/classes/WSAPIClans.md +66 -0
- package/docs/api/classes/WSAPIGamePick.md +181 -0
- package/docs/api/classes/WSAPIGeneral.md +63 -0
- package/docs/api/classes/WSAPIInbox.md +277 -0
- package/docs/api/classes/WSAPIJackpots.md +185 -0
- package/docs/api/classes/WSAPILeaderBoard.md +39 -0
- package/docs/api/classes/WSAPIMiniGames.md +175 -0
- package/docs/api/classes/WSAPIMissions.md +611 -0
- package/docs/api/classes/WSAPIRaffles.md +195 -0
- package/docs/api/classes/WSAPIStore.md +744 -0
- package/docs/api/classes/WSAPITournaments.md +104 -0
- package/docs/api/classes/WSAPIUser.md +200 -0
- package/docs/api/enumerations/AchievementClaimPeriodTypeId.md +13 -0
- package/docs/api/enumerations/BuyStoreItemErrorCode.md +32 -0
- package/docs/api/interfaces/ClanPrizePublicMeta.md +41 -0
- package/docs/api/interfaces/ClanPrizeStructureEntry.md +7 -1
- package/docs/api/interfaces/ClanPublicMeta.md +4 -0
- package/docs/api/interfaces/SAWTemplateUI.md +10 -0
- package/docs/api/interfaces/TAchCategory.md +19 -4
- package/docs/api/interfaces/TBuyStoreItemResult.md +26 -8
- package/docs/api/interfaces/TMissionClaimRewardResult.md +14 -3
- package/docs/api/interfaces/TMissionOptInResult.md +10 -3
- package/docs/api/interfaces/TMissionOrBadge.md +6 -2
- package/docs/api/interfaces/TStoreItem.md +21 -1
- package/docs/api/interfaces/TTournamentDetailed.md +8 -0
- package/docs/api/interfaces/UserAchievement.md +1 -1
- package/docs/ui/clans/UIGuide_getClanInfo.md +153 -0
- package/docs/ui/clans/UIGuide_getClans.md +172 -0
- package/docs/ui/clans/UIGuide_joinClan.md +196 -0
- package/docs/ui/missions/UIGuide_getAchCategories.md +17 -0
- package/docs/ui/missions/UIGuide_getBadges.md +237 -0
- package/docs/ui/missions/UIGuide_getMissions.md +268 -0
- package/docs/ui/missions/UIGuide_requestMissionClaimReward.md +21 -0
- package/docs/ui/missions/UIGuide_requestMissionOptIn.md +16 -0
- package/docs/ui/store/UIGuide_buyStoreItem.md +133 -0
- package/docs/ui/store/UIGuide_getStoreCategories.md +19 -0
- package/docs/ui/store/UIGuide_getStoreItems.md +264 -0
- package/docs/ui/store/UIGuide_getStorePurchasedItems.md +122 -0
- package/docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md +140 -0
- package/docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md +265 -0
- package/docs/ui/tournaments/UIGuide_getTournamentsList.md +236 -0
- package/docs/ui/tournaments/UIGuide_registerInTournament.md +156 -0
- package/docs/ui/user/UIGuide_getLevels.md +162 -0
- package/docs/ui/user/UIGuide_getUserProfile.md +213 -0
- package/package.json +3 -3
- package/scripts/postdoc-cleanup.js +195 -0
- package/src/Missions/MissionsUtils.ts +5 -2
- package/src/Store/BuyStoreItemErrorCode.ts +8 -0
- package/src/Store/StoreItem.ts +1 -0
- package/src/Store/StoreItemPurchased.ts +1 -0
- package/src/Store/StoreItemType.ts +1 -1
- package/src/Tournaments/TournamentRegistrationError.ts +4 -0
- package/src/WSAPI/WSAPI.ts +27 -2007
- package/src/WSAPI/WSAPIAvatars.ts +127 -0
- package/src/WSAPI/WSAPIBase.ts +93 -0
- package/src/WSAPI/WSAPIBonuses.ts +54 -0
- package/src/WSAPI/WSAPIClans.ts +417 -0
- package/src/WSAPI/WSAPIGamePick.ts +382 -0
- package/src/WSAPI/WSAPIGeneral.ts +57 -0
- package/src/WSAPI/WSAPIInbox.ts +222 -0
- package/src/WSAPI/WSAPIJackpots.ts +212 -0
- package/src/WSAPI/WSAPILeaderBoard.ts +40 -0
- package/src/WSAPI/WSAPIMiniGames.ts +181 -0
- package/src/WSAPI/WSAPIMissions.ts +572 -0
- package/src/WSAPI/WSAPIRaffles.ts +175 -0
- package/src/WSAPI/WSAPIStore.ts +732 -0
- package/src/WSAPI/WSAPITournaments.ts +541 -0
- package/src/WSAPI/WSAPITypes.ts +145 -87
- package/src/WSAPI/WSAPIUser.ts +456 -0
- package/tsconfig.json +17 -1
- package/docs/api/classes/WSAPI.md +0 -1927
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import { ECacheContext, OCache } from '../OCache';
|
|
2
|
+
import {
|
|
3
|
+
TTournament,
|
|
4
|
+
TTournamentDetailed,
|
|
5
|
+
TTournamentRegistrationResult,
|
|
6
|
+
TClanTournamentPlayers,
|
|
7
|
+
} from './WSAPITypes';
|
|
8
|
+
import {
|
|
9
|
+
CACHE_DATA_SEC,
|
|
10
|
+
onUpdateContextKey,
|
|
11
|
+
} from './WSAPIBase';
|
|
12
|
+
import { WSAPIStore } from './WSAPIStore';
|
|
13
|
+
|
|
14
|
+
/** @group Tournaments */
|
|
15
|
+
export class WSAPITournaments extends WSAPIStore {
|
|
16
|
+
/**
|
|
17
|
+
* Returns the list of tournaments currently visible to the user, scoped
|
|
18
|
+
* server-side to what the user's segments qualify them to see. Optionally
|
|
19
|
+
* subscribes to live updates via `onUpdate`: the callback is invoked with
|
|
20
|
+
* the full refreshed list whenever the user registers in a tournament.
|
|
21
|
+
*
|
|
22
|
+
* The returned list is the canonical source of truth for tournament
|
|
23
|
+
* lobby state (`is_user_registered`, `registration_status`,
|
|
24
|
+
* `is_in_progress`, `is_can_register`, etc.). Consumers should NOT
|
|
25
|
+
* mutate it optimistically — always wait for the `onUpdate` callback
|
|
26
|
+
* (or a fresh fetch after the 30 s cache window) to observe new state.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* **Subscription model (`onUpdate`)**
|
|
30
|
+
* The callback receives the FULL refreshed list (never a diff/patch).
|
|
31
|
+
* Each subsequent call to `getTournamentsList({ onUpdate })` REPLACES
|
|
32
|
+
* the prior callback — only one active subscriber at a time. Pass
|
|
33
|
+
* `onUpdate: undefined` (or omit it) to keep the prior callback in
|
|
34
|
+
* place; the callback is never auto-cleared.
|
|
35
|
+
*
|
|
36
|
+
* **Update triggers** — the callback fires when:
|
|
37
|
+
*
|
|
38
|
+
* 1. `registerInTournament(...)` resolves on this connection (any
|
|
39
|
+
* `err_code`). The refreshed list reflects the new
|
|
40
|
+
* `is_user_registered` / `registration_status` /
|
|
41
|
+
* `registration_count` on the affected tournament.
|
|
42
|
+
*
|
|
43
|
+
* Does NOT fire for: tournament lifecycle transitions (start, finish,
|
|
44
|
+
* cancellation), score updates, other users' registrations, or
|
|
45
|
+
* operator-side BO edits. Those changes surface only on the next
|
|
46
|
+
* cache miss (after the 30 s TTL) — poll manually if your UI needs
|
|
47
|
+
* sub-30s lifecycle freshness.
|
|
48
|
+
*
|
|
49
|
+
* **Server-side filtering** (what's excluded before the SDK sees it)
|
|
50
|
+
* The server filters by visibility segment and entry segment, removes
|
|
51
|
+
* tournaments past their post-finish display window, and excludes
|
|
52
|
+
* tournaments not yet published. SDK consumers receive only items
|
|
53
|
+
* the user is eligible to see; no client-side gating is required.
|
|
54
|
+
*
|
|
55
|
+
* **Reading state from the returned item**
|
|
56
|
+
* Drive list bucketing and CTA labels from the SDK-computed booleans
|
|
57
|
+
* (`is_active`, `is_can_register`, `is_cancelled`, `is_finished`,
|
|
58
|
+
* `is_in_progress`, `is_upcoming`), NOT from raw `start_time` /
|
|
59
|
+
* `end_time` comparisons — the booleans encode the canonical
|
|
60
|
+
* lifecycle states and already reflect server-side rules around
|
|
61
|
+
* qualifying scores and late registration. `is_user_registered` is
|
|
62
|
+
* independent of `is_can_register` — a registered user has
|
|
63
|
+
* `is_user_registered: true` and `is_can_register: false`. The `me`
|
|
64
|
+
* block (if present) carries the user's own leaderboard position and
|
|
65
|
+
* score; it's undefined for unregistered users, users without any
|
|
66
|
+
* recorded score, and visitor-mode sessions. The `prize_pool_short`
|
|
67
|
+
* field carries the operator-supplied summary string ("$10,000",
|
|
68
|
+
* "Mixed prizes") for compact card rendering; the full per-place
|
|
69
|
+
* `prizes[]` breakdown is also populated here but is typically
|
|
70
|
+
* rendered only in the detail view via
|
|
71
|
+
* {@link getTournamentInstanceInfo}.
|
|
72
|
+
*
|
|
73
|
+
* **Cross-references**
|
|
74
|
+
* Tournaments with `is_clan_based: true` group participants by clan —
|
|
75
|
+
* see {@link getClanTournamentPlayers} for fetching the players list
|
|
76
|
+
* of a specific clan within a clan tournament. The detailed lobby
|
|
77
|
+
* view (with full player leaderboard and prize structure) comes from
|
|
78
|
+
* {@link getTournamentInstanceInfo}. Tournaments with
|
|
79
|
+
* `custom_section_id` belong to an operator-defined custom section
|
|
80
|
+
* (resolve metadata via `getCustomSections()`).
|
|
81
|
+
*
|
|
82
|
+
* **Cache TTL**: the SDK caches the response for 30 seconds. Cache is
|
|
83
|
+
* fully cleared on login / logout.
|
|
84
|
+
*
|
|
85
|
+
* **Idempotency**: safe. Read-only. Repeated calls within the cache
|
|
86
|
+
* window return a deep-cloned cached array without a network
|
|
87
|
+
* round-trip.
|
|
88
|
+
*
|
|
89
|
+
* **Side effects**: none — pure metadata read.
|
|
90
|
+
*
|
|
91
|
+
* **UI guidance**: see [UI Guide — `getTournamentsList`](../../docs/ui/tournaments/UIGuide_getTournamentsList.md).
|
|
92
|
+
*
|
|
93
|
+
* **Visitor mode**: supported. The same shape is returned, scoped to
|
|
94
|
+
* the brand's public tournament list. Per-user fields (`me`,
|
|
95
|
+
* `is_user_registered`, `registration_status`) are not meaningful.
|
|
96
|
+
* The `onUpdate` callback is accepted but never fires (registration
|
|
97
|
+
* mutations are not supported in visitor mode).
|
|
98
|
+
*
|
|
99
|
+
* @param params Optional. Omit to fetch without subscribing.
|
|
100
|
+
* @param params.onUpdate Callback invoked with the full refreshed
|
|
101
|
+
* tournament list after every
|
|
102
|
+
* {@link registerInTournament} round-trip on
|
|
103
|
+
* this connection. Each call overwrites the
|
|
104
|
+
* prior callback. Never fires in visitor mode.
|
|
105
|
+
* @returns Promise resolving to the array of
|
|
106
|
+
* tournaments visible to the user. Empty
|
|
107
|
+
* array if no tournaments are visible.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const tournaments = await window._smartico.api.getTournamentsList({
|
|
112
|
+
* onUpdate: (refreshed) => {
|
|
113
|
+
* console.log('[smartico] tournament list refreshed — re-render the lobby UI from this array, do not merge with prior state:', refreshed);
|
|
114
|
+
* const justRegistered = refreshed.filter(t => t.is_user_registered);
|
|
115
|
+
* console.log('[smartico] user is now registered in these tournaments:', justRegistered.map(t => t.name));
|
|
116
|
+
* },
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* // Bucket items by lifecycle state for a tabbed UI.
|
|
120
|
+
* const live = tournaments.filter(t => t.is_in_progress);
|
|
121
|
+
* const upcoming = tournaments
|
|
122
|
+
* .filter(t => t.is_upcoming && !t.is_user_registered)
|
|
123
|
+
* .sort((a, b) => a.start_time - b.start_time);
|
|
124
|
+
* const mine = tournaments.filter(t => t.is_user_registered);
|
|
125
|
+
* const finished = tournaments.filter(t => t.is_finished);
|
|
126
|
+
*
|
|
127
|
+
* console.log('[smartico] render lobby tabs with these counts: live=', live.length,
|
|
128
|
+
* 'upcoming=', upcoming.length, 'mine=', mine.length, 'finished=', finished.length);
|
|
129
|
+
*
|
|
130
|
+
* // Featured tournament: pin to position 0 of the Overview/Top tabs.
|
|
131
|
+
* const featured = tournaments.find(t => t.is_featured && !t.is_cancelled);
|
|
132
|
+
* if (featured) {
|
|
133
|
+
* console.log('[smartico] featured tournament — pin to position 0 of the Overview tab:', featured.name);
|
|
134
|
+
* }
|
|
135
|
+
*
|
|
136
|
+
* // Visitor mode: onUpdate accepted but never fires; re-poll if needed.
|
|
137
|
+
* // const visitorList = await window._smartico.vapi('EN').getTournamentsList();
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
public async getTournamentsList({ onUpdate }: { onUpdate?: (data: TTournament[]) => void } = {}): Promise<TTournament[]> {
|
|
141
|
+
if (onUpdate) {
|
|
142
|
+
this.onUpdateCallback.set(onUpdateContextKey.TournamentList, onUpdate);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return OCache.use(
|
|
146
|
+
onUpdateContextKey.TournamentList,
|
|
147
|
+
ECacheContext.WSAPI,
|
|
148
|
+
() => this.api.tournamentsGetLobbyT(this.userExtId),
|
|
149
|
+
CACHE_DATA_SEC,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Returns the full detail of a single tournament instance — adds the
|
|
155
|
+
* player leaderboard, the full prize structure, and (for clan
|
|
156
|
+
* tournaments) the clan leaderboard and per-clan prize structure on
|
|
157
|
+
* top of everything already in `TTournament`. Use this to power a
|
|
158
|
+
* tournament detail / lobby screen after the user picks an item from
|
|
159
|
+
* {@link getTournamentsList}.
|
|
160
|
+
*
|
|
161
|
+
* Live leaderboard updates require manual re-fetching — there is no
|
|
162
|
+
* subscription model, no push event, and no client-side cache (each
|
|
163
|
+
* call hits the server). The default Smartico UI polls every 3 seconds
|
|
164
|
+
* while the detail view is open.
|
|
165
|
+
*
|
|
166
|
+
* @remarks
|
|
167
|
+
* **Preconditions**
|
|
168
|
+
* Pass a valid `tournamentInstanceId` read from `TTournament.instance_id`
|
|
169
|
+
* on an item returned by {@link getTournamentsList}. The method works
|
|
170
|
+
* standalone — calling `getTournamentsList()` first is not required by
|
|
171
|
+
* the SDK, but is the only stable source of valid IDs.
|
|
172
|
+
*
|
|
173
|
+
* **Refresh model**
|
|
174
|
+
* - **No subscription.** This is a one-shot promise; call again to
|
|
175
|
+
* refresh.
|
|
176
|
+
* - **No client cache.** Every call sends a network request and
|
|
177
|
+
* returns the latest server snapshot. Safe to poll on an interval —
|
|
178
|
+
* the default Smartico UI uses 3 s while the detail view is visible.
|
|
179
|
+
* - **No push event** invalidates or refreshes detail state. Score
|
|
180
|
+
* changes, registration count changes, lifecycle transitions — all
|
|
181
|
+
* require a fresh `getTournamentInstanceInfo` call.
|
|
182
|
+
*
|
|
183
|
+
* **Returned shape — beyond `TTournament`**
|
|
184
|
+
* `TTournamentDetailed` extends `TTournament` with the following
|
|
185
|
+
* additional data: a `players[]` leaderboard sorted server-side by
|
|
186
|
+
* score (only members with at least one recorded score); the user's
|
|
187
|
+
* own `me` block carrying their rank and score (undefined when not
|
|
188
|
+
* registered or when the user has no score yet); a full per-place
|
|
189
|
+
* `prizes[]` array (place range, type, points / gems / diamonds
|
|
190
|
+
* amounts, image, name); and `related_games[]` if the operator has
|
|
191
|
+
* associated games with this tournament. For clan tournaments
|
|
192
|
+
* (`is_clan_based === true`), the response also carries
|
|
193
|
+
* `clan_leaderboard[]` (ranked clans with `total_score` and
|
|
194
|
+
* `contributing_members`), `clan_prize_structure[]` (per-clan-place
|
|
195
|
+
* prize tiers — Fixed vs Dynamic, with ScoreWeighted /
|
|
196
|
+
* EqualSplit distribution for Dynamic prizes), `user_clan_id` (the
|
|
197
|
+
* user's own clan), and the advisory `user_position_in_clan` /
|
|
198
|
+
* `user_score_in_clan` fields.
|
|
199
|
+
*
|
|
200
|
+
* **Idempotency**: safe. Read-only. Each call returns the latest
|
|
201
|
+
* server snapshot.
|
|
202
|
+
*
|
|
203
|
+
* **Side effects**: none — pure metadata read.
|
|
204
|
+
*
|
|
205
|
+
* **UI guidance**: see [UI Guide — `getTournamentInstanceInfo`](../../docs/ui/tournaments/UIGuide_getTournamentInstanceInfo.md).
|
|
206
|
+
*
|
|
207
|
+
* **Visitor mode**: not supported. Use {@link getTournamentsList} for
|
|
208
|
+
* the public lobby list in visitor mode; the detail endpoint requires
|
|
209
|
+
* an authenticated session.
|
|
210
|
+
*
|
|
211
|
+
* @param tournamentInstanceId The `instance_id` from a `TTournament`
|
|
212
|
+
* returned by {@link getTournamentsList}.
|
|
213
|
+
* @returns Promise resolving to `TTournamentDetailed`.
|
|
214
|
+
* Rejects if the instance ID is invalid
|
|
215
|
+
* or the user lacks visibility for it.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```ts
|
|
219
|
+
* const tournaments = await window._smartico.api.getTournamentsList();
|
|
220
|
+
* const tournament = tournaments[0];
|
|
221
|
+
*
|
|
222
|
+
* console.log('[smartico] loading detail view for tournament', tournament.instance_id);
|
|
223
|
+
* const detail = await window._smartico.api.getTournamentInstanceInfo(tournament.instance_id);
|
|
224
|
+
*
|
|
225
|
+
* console.log('[smartico] render detail with', detail.players?.length ?? 0,
|
|
226
|
+
* 'players,', detail.prizes?.length ?? 0, 'prize tiers');
|
|
227
|
+
*
|
|
228
|
+
* if (detail.is_clan_based && detail.clan_leaderboard) {
|
|
229
|
+
* console.log('[smartico] clan tournament — render clan leaderboard tab with',
|
|
230
|
+
* detail.clan_leaderboard.length, 'clans');
|
|
231
|
+
* if (detail.user_clan_id != null) {
|
|
232
|
+
* console.log('[smartico] user belongs to clan', detail.user_clan_id,
|
|
233
|
+
* '— highlight that row in the clan leaderboard');
|
|
234
|
+
* }
|
|
235
|
+
* }
|
|
236
|
+
*
|
|
237
|
+
* // Live leaderboard updates — poll every 3 seconds while the detail view is open.
|
|
238
|
+
* const pollId = setInterval(async () => {
|
|
239
|
+
* try {
|
|
240
|
+
* const fresh = await window._smartico.api.getTournamentInstanceInfo(tournament.instance_id);
|
|
241
|
+
* console.log('[smartico] detail refreshed — re-render player + clan leaderboards from this new snapshot:', fresh);
|
|
242
|
+
* } catch (e) {
|
|
243
|
+
* console.error('[smartico] tournament detail poll failed — keep showing last snapshot, retry on next tick:', e);
|
|
244
|
+
* }
|
|
245
|
+
* }, 3_000);
|
|
246
|
+
* // clearInterval(pollId) when the detail view closes.
|
|
247
|
+
*
|
|
248
|
+
* if (detail.me) {
|
|
249
|
+
* console.log('[smartico] current user is rank', detail.me.position,
|
|
250
|
+
* 'with', detail.me.scores, 'points — render the sticky "me" panel below the leaderboard');
|
|
251
|
+
* }
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
public async getTournamentInstanceInfo(tournamentInstanceId: number): Promise<TTournamentDetailed> {
|
|
255
|
+
return this.api.tournamentsGetInfoT(this.userExtId, tournamentInstanceId);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Returns the ranked players of a specific clan within a clan-based
|
|
260
|
+
* tournament instance. Used to drill down from the clan-leaderboard
|
|
261
|
+
* tab on the tournament detail view into a single clan's member
|
|
262
|
+
* roster.
|
|
263
|
+
*
|
|
264
|
+
* The server returns up to roughly 20 players (top-N by score
|
|
265
|
+
* descending). Server-side ordering is already by `scores` DESC
|
|
266
|
+
* (equivalent to `position` ASC) — no client-side re-sort is
|
|
267
|
+
* required.
|
|
268
|
+
*
|
|
269
|
+
* @remarks
|
|
270
|
+
* **Preconditions**
|
|
271
|
+
* Pass a valid `tournamentInstanceId` (read from `TTournament.instance_id`
|
|
272
|
+
* returned by {@link getTournamentsList}) and a `clanId` (read from
|
|
273
|
+
* `TTournamentDetailed.clan_leaderboard[].clan_id` returned by
|
|
274
|
+
* {@link getTournamentInstanceInfo}). The tournament should be
|
|
275
|
+
* clan-based (`is_clan_based === true`); calling this against a
|
|
276
|
+
* non-clan tournament typically returns an empty `players` array.
|
|
277
|
+
*
|
|
278
|
+
* **Refresh model**
|
|
279
|
+
* - **No subscription.** This is a one-shot promise.
|
|
280
|
+
* - **No client cache.** Every call hits the server.
|
|
281
|
+
* - **No push event** refreshes the response. Live updates to clan
|
|
282
|
+
* member rankings require a fresh call.
|
|
283
|
+
* - The default Smartico UI fetches this once when the user opens the
|
|
284
|
+
* clan-drill-down modal and does NOT refresh while the modal is
|
|
285
|
+
* open. If your UI needs live updates, re-call on an interval; the
|
|
286
|
+
* clan leaderboard at the parent level (via
|
|
287
|
+
* {@link getTournamentInstanceInfo}) does refresh via its own
|
|
288
|
+
* polling cadence.
|
|
289
|
+
*
|
|
290
|
+
* **Returned shape**
|
|
291
|
+
* `TClanTournamentPlayers` carries the requested clan's identity
|
|
292
|
+
* (`clan_id`, `clan_public_meta.name`, `clan_public_meta.image_url`)
|
|
293
|
+
* alongside the `players[]` array — each entry has `position`,
|
|
294
|
+
* `scores`, `public_username`, `clean_ext_user_id`, `avatar_id`,
|
|
295
|
+
* `avatar_real_id`, the resolved `avatar_url`, `user_id`, and the
|
|
296
|
+
* `is_me` flag identifying the current user's row. The `clan_id` and
|
|
297
|
+
* `clan_public_meta` on the response echo the input clan — the
|
|
298
|
+
* default Smartico UI ignores them and renders the header from the
|
|
299
|
+
* caller-side `ClanLeaderboardEntry` object, but they're available
|
|
300
|
+
* for consumers that don't already have that context.
|
|
301
|
+
*
|
|
302
|
+
* **Username display**: prefer `clean_ext_user_id` when set, falling
|
|
303
|
+
* back to `public_username`. The default Smartico UI uses
|
|
304
|
+
* `clean_ext_user_id || public_username || ''`.
|
|
305
|
+
*
|
|
306
|
+
* **Idempotency**: safe. Read-only. Each call returns the latest
|
|
307
|
+
* server snapshot.
|
|
308
|
+
*
|
|
309
|
+
* **Side effects**: none — pure metadata read.
|
|
310
|
+
*
|
|
311
|
+
* **UI guidance**: see [UI Guide — `getClanTournamentPlayers`](../../docs/ui/tournaments/UIGuide_getClanTournamentPlayers.md).
|
|
312
|
+
*
|
|
313
|
+
* **Visitor mode**: not supported.
|
|
314
|
+
*
|
|
315
|
+
* @param tournamentInstanceId The tournament `instance_id` from a
|
|
316
|
+
* clan-based `TTournament`.
|
|
317
|
+
* @param clanId The clan ID from
|
|
318
|
+
* `TTournamentDetailed.clan_leaderboard[].clan_id`.
|
|
319
|
+
* @returns Promise resolving to
|
|
320
|
+
* `TClanTournamentPlayers`. An invalid
|
|
321
|
+
* ID combination typically yields an
|
|
322
|
+
* empty `players` array; the SDK does
|
|
323
|
+
* not distinguish "no scores yet" from
|
|
324
|
+
* "invalid IDs" — both manifest as an
|
|
325
|
+
* empty list.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* const detail = await window._smartico.api.getTournamentInstanceInfo(tournamentInstanceId);
|
|
330
|
+
* if (!detail.is_clan_based || !detail.clan_leaderboard) {
|
|
331
|
+
* console.log('[smartico] not a clan tournament — skip clan drill-down');
|
|
332
|
+
* return;
|
|
333
|
+
* }
|
|
334
|
+
*
|
|
335
|
+
* // User clicked a clan row in the leaderboard.
|
|
336
|
+
* const clickedClan = detail.clan_leaderboard[0];
|
|
337
|
+
*
|
|
338
|
+
* console.log('[smartico] opening clan drill-down — show modal skeleton while loading');
|
|
339
|
+
* const result = await window._smartico.api.getClanTournamentPlayers(
|
|
340
|
+
* tournamentInstanceId,
|
|
341
|
+
* clickedClan.clan_id,
|
|
342
|
+
* );
|
|
343
|
+
*
|
|
344
|
+
* if (result.players.length === 0) {
|
|
345
|
+
* console.log('[smartico] clan has no ranked players yet — render empty state ("No leaders yet")');
|
|
346
|
+
* } else {
|
|
347
|
+
* console.log('[smartico] render clan member rows with .is-me highlight on the current user;',
|
|
348
|
+
* result.players.length, 'players ranked');
|
|
349
|
+
* const me = result.players.find(p => p.is_me);
|
|
350
|
+
* if (me) {
|
|
351
|
+
* console.log('[smartico] current user rank in this clan:', me.position, 'score:', me.scores);
|
|
352
|
+
* }
|
|
353
|
+
* }
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
public async getClanTournamentPlayers(tournamentInstanceId: number, clanId: number): Promise<TClanTournamentPlayers> {
|
|
357
|
+
return this.api.clanTournamentGetPlayers(this.userExtId, tournamentInstanceId, clanId);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Registers the authenticated user in the specified tournament instance.
|
|
362
|
+
* For tournaments with a buy-in cost, the user's balance is debited
|
|
363
|
+
* synchronously before the response is returned; for free tournaments
|
|
364
|
+
* the call is a simple membership add.
|
|
365
|
+
*
|
|
366
|
+
* A successful response (`err_code === 0`) means the registration row
|
|
367
|
+
* exists server-side, the buy-in (if any) has been deducted, and the
|
|
368
|
+
* SDK has already triggered a refresh of the tournament lobby cache.
|
|
369
|
+
*
|
|
370
|
+
* @remarks
|
|
371
|
+
* **Preconditions**
|
|
372
|
+
* Read the candidate from {@link getTournamentsList} and gate the call
|
|
373
|
+
* on `is_can_register === true`. This composite flag already encodes:
|
|
374
|
+
* not already registered, slot available
|
|
375
|
+
* (`registration_count < players_max_count`), tournament status is
|
|
376
|
+
* REGISTER (or STARTED with late-registration enabled), and the
|
|
377
|
+
* registration type is not AUTO. The SDK forwards the call
|
|
378
|
+
* unconditionally — calling without satisfying these will return one
|
|
379
|
+
* of the error codes below.
|
|
380
|
+
*
|
|
381
|
+
* For clan tournaments (`is_clan_based === true`), the user must
|
|
382
|
+
* already belong to a clan before calling. If they don't, prompt them
|
|
383
|
+
* to pick a clan first via {@link joinClan}, then call
|
|
384
|
+
* `registerInTournament` after the clan-join resolves. See "Error
|
|
385
|
+
* codes" `1010` below for the server-side safety net.
|
|
386
|
+
*
|
|
387
|
+
* **Error codes** (in `err_code`, typed as {@link TournamentRegistrationError})
|
|
388
|
+
* - `0` — success; the registration is persisted, buy-in deducted,
|
|
389
|
+
* tournament lobby refresh fires.
|
|
390
|
+
* - `1010` — `TOURNAMENT_USER_CANNOT_JOIN_WITHOUT_CLAN`: clan-based
|
|
391
|
+
* tournament and the user has no clan. Surface the clan-pick flow
|
|
392
|
+
* (e.g. open a clan-pick modal driven by {@link getClans} +
|
|
393
|
+
* {@link joinClan}), then retry registration.
|
|
394
|
+
* - `30001` — `TOURNAMENT_INSTANCE_NOT_FOUND`: instance ID is invalid
|
|
395
|
+
* or the tournament was deleted. Refresh the lobby via
|
|
396
|
+
* `getTournamentsList()` and hide the entry from the UI.
|
|
397
|
+
* - `30002` — `TOURNAMENT_REGISTRATION_NOT_ENOUGH_POINTS`: insufficient
|
|
398
|
+
* points balance for a points-cost tournament. Show an
|
|
399
|
+
* insufficient-balance UI naming the deficit.
|
|
400
|
+
* - `30003` — `TOURNAMENT_INSTANCE_NOT_IN_STATE`: tournament is no
|
|
401
|
+
* longer in a registerable state (finished, cancelled, finalizing,
|
|
402
|
+
* or not yet published with late registration disabled). Refresh
|
|
403
|
+
* the lobby; the button should hide.
|
|
404
|
+
* - `30004` — `TOURNAMENT_ALREADY_REGISTERED`: user is already
|
|
405
|
+
* registered. Treat as idempotent success — hide the Register button
|
|
406
|
+
* and refresh the lobby. Usually indicates a stale local state
|
|
407
|
+
* (e.g. registered from another tab).
|
|
408
|
+
* - `30005` — `TOURNAMENT_USER_DONT_MATCH_CONDITIONS`: user fails the
|
|
409
|
+
* tournament's segment / entry conditions. Prefer
|
|
410
|
+
* `tournament.segment_dont_match_message` (operator-supplied) over
|
|
411
|
+
* `err_message` when both are available.
|
|
412
|
+
* - `30006` — `TOURNAMENT_USER_NOT_REGISTERED`: anomalous on a fresh
|
|
413
|
+
* register call; usually indicates a server-side race. Treat as a
|
|
414
|
+
* generic transient error.
|
|
415
|
+
* - `30007` — `TOURNAMENT_CANT_CHANGE_REGISTRATION_STATUS`: status
|
|
416
|
+
* transition not allowed (e.g. re-registering after a finished /
|
|
417
|
+
* cancelled tournament). Show a generic error; do not retry.
|
|
418
|
+
* - `30008` — `TOURNAMENT_MAX_REGISTRATIONS_REACHED`: the tournament
|
|
419
|
+
* filled up between the lobby fetch and the registration click.
|
|
420
|
+
* Refresh the lobby — the button will hide on the refreshed item.
|
|
421
|
+
* - `300010` — insufficient **gems** balance (gems-cost tournament).
|
|
422
|
+
* Same UI handling as `30002` but for the gems currency.
|
|
423
|
+
* - `300011` — insufficient **diamonds** balance (diamonds-cost
|
|
424
|
+
* tournament). Same as `300010` but for diamonds.
|
|
425
|
+
* - other non-zero — generic server error. Surface `err_message` if any.
|
|
426
|
+
*
|
|
427
|
+
* Note: clan-tournament registration can occasionally surface error
|
|
428
|
+
* codes from the clan-join domain (e.g. `1011` — joined-after-start)
|
|
429
|
+
* via the same response path. These are members of `JoinClanErrorCode`,
|
|
430
|
+
* not `TournamentRegistrationError`. Branch on the numeric value.
|
|
431
|
+
*
|
|
432
|
+
* **Idempotency**: NOT idempotent. A second call on the same instance
|
|
433
|
+
* returns `30004` once the first call has succeeded. The SDK does NOT
|
|
434
|
+
* enforce an in-flight lock. The consumer MUST guard the call site
|
|
435
|
+
* against double-clicks (set a local "registering" flag on click, clear
|
|
436
|
+
* it on response).
|
|
437
|
+
*
|
|
438
|
+
* **Refresh after success (and after failure)**
|
|
439
|
+
* The SDK automatically refreshes the tournament lobby cache on every
|
|
440
|
+
* response (success OR error) and fires any `onUpdate` callback
|
|
441
|
+
* registered via `getTournamentsList({ onUpdate })`. After
|
|
442
|
+
* `err_code === 0`, the affected item's `is_user_registered` /
|
|
443
|
+
* `registration_status` / `registration_count` reflect the new
|
|
444
|
+
* registered state on the refreshed array. Note that
|
|
445
|
+
* {@link getTournamentInstanceInfo} is NOT auto-refreshed — if the
|
|
446
|
+
* detail screen is open, call it again manually.
|
|
447
|
+
*
|
|
448
|
+
* The user's balance (points / gems / diamonds) is exposed on the
|
|
449
|
+
* user-properties channel and updates independently of this call's
|
|
450
|
+
* response — subscribe to user-property updates separately if your UI
|
|
451
|
+
* shows the balance.
|
|
452
|
+
*
|
|
453
|
+
* **Side effects** (consumer-observable on success)
|
|
454
|
+
* - Balance debited synchronously by the buy-in amount in the
|
|
455
|
+
* tournament's currency (`registration_cost_points`,
|
|
456
|
+
* `registration_cost_gems`, or `registration_cost_diamonds`). For
|
|
457
|
+
* free tournaments no deduction occurs.
|
|
458
|
+
* - Server-side analytics events fire downstream
|
|
459
|
+
* (`tournament_user_registered` for immediate-registration types;
|
|
460
|
+
* `tournament_user_registration_pending` or
|
|
461
|
+
* `tournament_registered_pending_qualification` for
|
|
462
|
+
* manual-approval / qualification-based types).
|
|
463
|
+
* - Any operator-configured on-join prizes are distributed (e.g.
|
|
464
|
+
* welcome bonus). These surface via the relevant follow-up channels
|
|
465
|
+
* (e.g. {@link getBonuses}).
|
|
466
|
+
*
|
|
467
|
+
* **UI guidance**: see [UI Guide — `registerInTournament`](../../docs/ui/tournaments/UIGuide_registerInTournament.md).
|
|
468
|
+
*
|
|
469
|
+
* **Visitor mode**: not supported. Calling from a visitor session
|
|
470
|
+
* returns a generic error, not a typed `TournamentRegistrationError`.
|
|
471
|
+
*
|
|
472
|
+
* @param tournamentInstanceId The `instance_id` from `TTournament`
|
|
473
|
+
* (NOT the template `tournament_id`).
|
|
474
|
+
* @returns `{ err_code, err_message }`; success when `err_code === 0`
|
|
475
|
+
* (or `30004` when treated as idempotent no-op).
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* ```ts
|
|
479
|
+
* const tournaments = await window._smartico.api.getTournamentsList({
|
|
480
|
+
* onUpdate: (refreshed) => console.log('[smartico] tournament list updated — re-render lobby from this array', refreshed),
|
|
481
|
+
* });
|
|
482
|
+
* const tournament = tournaments.find(t => t.instance_id === instanceId);
|
|
483
|
+
*
|
|
484
|
+
* if (!tournament) {
|
|
485
|
+
* console.log('[smartico] tournament not in current lobby — refresh getTournamentsList and retry');
|
|
486
|
+
* return;
|
|
487
|
+
* }
|
|
488
|
+
* if (!tournament.is_can_register) {
|
|
489
|
+
* console.log('[smartico] tournament not currently registerable — keep CTA disabled');
|
|
490
|
+
* return;
|
|
491
|
+
* }
|
|
492
|
+
*
|
|
493
|
+
* // Clan tournament + user has no clan → resolve clan first.
|
|
494
|
+
* if (tournament.is_clan_based) {
|
|
495
|
+
* const userProfile = window._smartico.api.getUserProfile();
|
|
496
|
+
* const userClanId = userProfile?.clan_id;
|
|
497
|
+
* if (userClanId == null) {
|
|
498
|
+
* console.log('[smartico] clan tournament — open the clan-pick modal first; after joinClan resolves, call registerInTournament again');
|
|
499
|
+
* return;
|
|
500
|
+
* }
|
|
501
|
+
* }
|
|
502
|
+
*
|
|
503
|
+
* console.log('[smartico] registration starting — set in-flight flag, show loading dots on the Join button, keep modal open');
|
|
504
|
+
* const r = await window._smartico.api.registerInTournament(tournament.instance_id);
|
|
505
|
+
* console.log('[smartico] registration response received — clear in-flight flag');
|
|
506
|
+
*
|
|
507
|
+
* if (r.err_code === 0 || r.err_code === 30004) {
|
|
508
|
+
* console.log('[smartico] registered (or was already) — close any modal, show a success toast; getTournamentsList onUpdate above will fire with the refreshed array');
|
|
509
|
+
* } else if (r.err_code === 1010) {
|
|
510
|
+
* console.log('[smartico] clan required — open the clan-pick modal again (this is the server-side safety net for a clan-leave race)');
|
|
511
|
+
* } else if (r.err_code === 30002) {
|
|
512
|
+
* console.error('[smartico] insufficient points — show insufficient-balance UI; deficit:', tournament.registration_cost_points);
|
|
513
|
+
* } else if (r.err_code === 300010) {
|
|
514
|
+
* console.error('[smartico] insufficient gems — show insufficient-balance UI; deficit:', tournament.registration_cost_gems);
|
|
515
|
+
* } else if (r.err_code === 300011) {
|
|
516
|
+
* console.error('[smartico] insufficient diamonds — show insufficient-balance UI; deficit:', tournament.registration_cost_diamonds);
|
|
517
|
+
* } else if (r.err_code === 30005) {
|
|
518
|
+
* console.error('[smartico] segment mismatch — prefer the operator-supplied message:', tournament.segment_dont_match_message || r.err_message);
|
|
519
|
+
* } else if (r.err_code === 30008) {
|
|
520
|
+
* console.error('[smartico] tournament full — auto-refresh will hide the button on the refreshed lobby item');
|
|
521
|
+
* } else {
|
|
522
|
+
* console.error('[smartico] registration failed — show a generic error toast with this message:', r.err_message);
|
|
523
|
+
* }
|
|
524
|
+
* ```
|
|
525
|
+
*/
|
|
526
|
+
public async registerInTournament(tournamentInstanceId: number): Promise<TTournamentRegistrationResult> {
|
|
527
|
+
const r = await this.api.registerInTournament(this.userExtId, tournamentInstanceId);
|
|
528
|
+
|
|
529
|
+
const o: TTournamentRegistrationResult = {
|
|
530
|
+
err_code: r.errCode,
|
|
531
|
+
err_message: r.errMsg,
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
return o;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
protected async updateTournaments() {
|
|
538
|
+
const payload = await this.api.tournamentsGetLobbyT(this.userExtId);
|
|
539
|
+
this.updateEntity(onUpdateContextKey.TournamentList, payload);
|
|
540
|
+
}
|
|
541
|
+
}
|