@smartico/public-api 0.0.433 → 0.0.435

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.
@@ -2,8 +2,9 @@
2
2
 
3
3
  ## Overview
4
4
  - Returns the rich body (title, preview, icon, action, html_body,
5
- buttons, custom_data) for one message identified by its
6
- `message_guid`.
5
+ buttons, custom_data) and the preview-popup settings
6
+ (`show_preview`, `show_duration_sec`) for one message identified
7
+ by its `message_guid`.
7
8
  - Fetched from a CDN, NOT over the WebSocket — latency depends on
8
9
  CDN proximity. No SDK-level cache (browser HTTP cache may apply
9
10
  via CDN Cache-Control headers).
@@ -42,6 +43,27 @@ payload has `html_body` or `buttons` but the action isn't
42
43
  consumer. Don't try to render `html_body` for non-`'dp:inbox'`
43
44
  messages — it will be `undefined`.
44
45
 
46
+ ## Preview popup vs silent delivery
47
+
48
+ Every message is stored in the inbox list. The preview settings
49
+ decide whether a newly arrived message also pops up briefly (the
50
+ "toast" the default Smartico UI shows while the user is online).
51
+
52
+ | Field | Value | Behaviour |
53
+ |---|---|---|
54
+ | `show_preview` | `true` or missing | Show a preview popup and store the message in the list |
55
+ | `show_preview` | `false` | Store the message silently, with no popup |
56
+ | `show_duration_sec` | number | Hide the popup after this many seconds |
57
+ | `show_duration_sec` | `null` or missing | Hide the popup after 7 seconds (the default Smartico UI's default) |
58
+
59
+ Values are returned exactly as the operator configured them, so
60
+ apply the defaults above yourself (e.g. `body.show_preview !== false`,
61
+ `body.show_duration_sec ?? 7`).
62
+
63
+ Only show the popup for messages that arrive while the user is
64
+ online. Messages loaded from history via {@link getInboxMessages}
65
+ should go straight into the list.
66
+
45
67
  ## Sandboxed iframe (for `html_body`)
46
68
 
47
69
  `html_body` is operator-supplied HTML. **Render in a sandboxed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartico/public-api",
3
- "version": "0.0.433",
3
+ "version": "0.0.435",
4
4
  "description": "Smartico public API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,7 +23,7 @@ export interface InboxMessageBody {
23
23
  // default is 'true'
24
24
  show_preview?: boolean;
25
25
  // duration in seconds for how long to show preview, default is 7 seconds
26
- show_duration_sec?: number;
26
+ show_duration_sec?: number | null;
27
27
  // indicator if the image in html_body can be zoomed out
28
28
  // default is 'false'
29
29
  enable_zoom_mode?: boolean;
@@ -62,6 +62,8 @@ export const InboxMessageBodyTransform = (item: InboxMessageBody): TInboxMessage
62
62
  title: item.title,
63
63
  preview_body: item.body,
64
64
  custom_data: IntUtils.JsonOrText(item?.custom_data),
65
+ show_preview: item.show_preview,
66
+ show_duration_sec: item.show_duration_sec,
65
67
  };
66
68
 
67
69
  if (item.action === 'dp:inbox') {
@@ -486,6 +486,35 @@ export interface SAWTemplateUI {
486
486
  */
487
487
  use_custom?: boolean;
488
488
 
489
+ /**
490
+ * Whether a custom mini-game fills the whole mini-game window instead of
491
+ * the default fixed-size frame (800×516 on desktop, 400×745 on mobile).
492
+ *
493
+ * Applies wherever the game is opened — a spin attempt shown to the user,
494
+ * a deep link, or `_smartico.miniGame()` in standalone or inline mode.
495
+ * The `fullscreen` option of `_smartico.miniGame()` overrides it per call.
496
+ *
497
+ * When `false` or omitted, the default frame size is used.
498
+ * Back-Office label: _"Standalone full screen"_.
499
+ */
500
+ standalone_fullscreen?: boolean;
501
+
502
+ /**
503
+ * Width of the custom mini-game frame on desktop when opened standalone or inline,
504
+ * as a CSS size (`800px`, `100%`, `90vw`; a bare number is treated as pixels).
505
+ * Empty or invalid values fall back to the default 800px. Ignored on mobile.
506
+ * Back-Office label: _"Standalone width"_.
507
+ */
508
+ standalone_width?: string;
509
+
510
+ /**
511
+ * Height of the custom mini-game frame on desktop when opened standalone or inline,
512
+ * as a CSS size (`516px`, `100%`, `90vh`; a bare number is treated as pixels).
513
+ * Empty or invalid values fall back to the default 516px. Ignored on mobile.
514
+ * Back-Office label: _"Standalone height"_.
515
+ */
516
+ standalone_height?: string;
517
+
489
518
  /**
490
519
  * Minutes for the Voyager seed window.
491
520
  * The seed window is the range of seeds that can be used to generate the map.
@@ -1,12 +1,19 @@
1
- import { IntUtils } from "../IntUtils";
2
1
  import { AchievementAvailabilityStatus } from "./AchievementAvailabilityStatus";
3
2
  import { AchievementStatus } from "./AchievementStatus";
4
3
  import { UserAchievement } from "./UserAchievement";
5
4
  import { UserAchievementTask } from "./UserAchievementTask";
6
5
  import { BadgesTimeLimitStates } from "./BadgesTimeLimitStates";
7
6
 
8
- type UserStateParamsKeys = 'core_fav_game_top3' | 'core_fav_game_type_top3' | 'core_fav_game_provider_top3' | 'core_recommended_deposit_amount' | 'core_recommended_casino_bet_amount' | 'core_recommended_sport_bet_amount';
9
- const USER_STATE_PARAMS_KEYS_GAMES: UserStateParamsKeys[] = ['core_fav_game_top3', 'core_fav_game_type_top3', 'core_fav_game_provider_top3'];
7
+ type UserStateParamsKeys =
8
+ | 'core_fav_game_top3' | 'core_fav_game_type_top3' | 'core_fav_game_provider_top3'
9
+ | 'core_fav_sport_type_top3' | 'core_fav_sport_league_top3'
10
+ | 'core_recommended_deposit_amount' | 'core_recommended_casino_bet_amount' | 'core_recommended_sport_bet_amount';
11
+
12
+ // tag -> favorites (top 3 arrays) that fill it; a task has at most one condition per tag (enforced in the BO)
13
+ const FAV_TAGS: [string, UserStateParamsKeys[]][] = [
14
+ ['{{suggested_games}}', ['core_fav_game_top3', 'core_fav_game_type_top3', 'core_fav_game_provider_top3']],
15
+ ['{{suggested_sport}}', ['core_fav_sport_type_top3', 'core_fav_sport_league_top3']],
16
+ ];
10
17
  const USER_STATE_PARAMS_KEYS_BET_AMOUNT: UserStateParamsKeys[] = [
11
18
  'core_recommended_deposit_amount',
12
19
  'core_recommended_casino_bet_amount',
@@ -212,64 +219,47 @@ export class MissionUtils {
212
219
 
213
220
  const userStateParams = (task.user_state_params || {});
214
221
  const userStateOperator = task.task_public_meta?.user_state_operations;
215
- const userStateParamsKeys = Object.keys(userStateParams);
216
-
217
- if (userStateParamsKeys.length === 0 || !userStateOperator) {
222
+ if (Object.keys(userStateParams).length === 0 || !userStateOperator) {
218
223
  return result;
219
224
  }
220
225
 
221
- const operatorsMulti = ['has', '!has'];
222
- const operatorsPos = ['pos1', 'pos2', 'pos3'];
223
-
224
- let suggestedGames: string = '';
225
- let suggestedValue: string = '';
226
-
227
- userStateParamsKeys.forEach((k: UserStateParamsKeys) => {
228
- const operator = userStateOperator[k]?.op;
229
-
230
- if (USER_STATE_PARAMS_KEYS_GAMES.includes(k)) {
231
- if (operatorsMulti.includes(operator)) {
232
- const value = userStateParams[k]?.filter(v => Boolean(v));
233
- if (value && value.length > 0) {
234
- suggestedGames = value.map((v: string) => {
235
- const cleaned = v.replace(/_/g, ' ');
236
- return toGameTitleCase(cleaned);
237
- }).join(', ');
238
- }
239
- }
240
-
241
- if (operatorsPos.includes(operator)) {
242
- const value = userStateParams[k];
243
- const pos = Number(operator.replace('pos', '')) - 1;
226
+ const formatFav = (v: string): string => toGameTitleCase(v.replace(/_/g, ' '));
244
227
 
245
- if (IntUtils.isNotNull(pos) && value && value[pos]) {
246
- suggestedGames = value[pos];
228
+ // 'has' / '!has' -> all favorites, 'posN' -> the N-th one
229
+ const favValue = (k: UserStateParamsKeys): string => {
230
+ const values = userStateParams[k];
231
+ const operator: string = userStateOperator[k]?.op;
247
232
 
248
- if (suggestedGames) {
249
- suggestedGames = toGameTitleCase(suggestedGames.replace(/_/g, ' '));
250
- }
233
+ if (!Array.isArray(values)) {
234
+ return '';
235
+ }
251
236
 
252
- }
253
- }
237
+ if (operator === 'has' || operator === '!has') {
238
+ return values.filter(Boolean).map(formatFav).join(', ');
254
239
  }
255
240
 
256
- if (USER_STATE_PARAMS_KEYS_BET_AMOUNT.includes(k)) {
257
- suggestedValue = userStateParams[k];
241
+ const pos = /^pos[1-3]$/.test(operator) ? values[Number(operator.substring(3)) - 1] : null;
258
242
 
259
- if (suggestedValue) {
260
- const currencyFromTheTask = userStateParams?.core_wallet_currency;
243
+ return pos ? formatFav(pos) : '';
244
+ };
261
245
 
262
- suggestedValue = `${suggestedValue} ${currencySymbol || currencyFromTheTask || ''}`;
263
- }
246
+ // split/join replaces every occurrence of the tag, not only the first one
247
+ const replaceTag = (tag: string, value: string) => {
248
+ if (value && result) {
249
+ result = result.split(tag).join(value);
264
250
  }
251
+ };
252
+
253
+ FAV_TAGS.forEach(([tag, keys]) => {
254
+ replaceTag(tag, keys.map(favValue).find(Boolean));
265
255
  });
266
256
 
267
- if (suggestedGames && result) {
268
- result = result.replace('{{suggested_games}}', suggestedGames);
269
- }
257
+ const amountKey = USER_STATE_PARAMS_KEYS_BET_AMOUNT.find((k) => userStateParams[k]);
258
+
259
+ if (amountKey) {
260
+ const currencyFromTheTask = userStateParams.core_wallet_currency;
270
261
 
271
- if (suggestedValue && result) {
272
- result = result.replace('{{suggested_value}}', suggestedValue);
262
+ replaceTag('{{suggested_value}}', `${userStateParams[amountKey]} ${currencySymbol || currencyFromTheTask || ''}`);
273
263
  }
274
264
 
275
265
  return result;
@@ -76,13 +76,13 @@ export const UserAchievementTransform = (items: UserAchievement[]): TMissionOrBa
76
76
  if (missionName?.includes('{{suggested_') || missionSubHeader?.includes('{{suggested_') || missionDescription?.includes('{{suggested_')) {
77
77
  r.achievementTasks.forEach(t => {
78
78
  if (r.ach_public_meta.name?.includes('{{suggested_')) {
79
- missionName = MissionUtils.replaceTagsFavMissionTask({ task: t, valueToReplace: r.ach_public_meta.name });
79
+ missionName = MissionUtils.replaceTagsFavMissionTask({ task: t, valueToReplace: missionName });
80
80
  }
81
81
  if (r.ach_public_meta.sub_header?.includes('{{suggested_')) {
82
- missionSubHeader = MissionUtils.replaceTagsFavMissionTask({ task: t, valueToReplace: r.ach_public_meta.sub_header });
82
+ missionSubHeader = MissionUtils.replaceTagsFavMissionTask({ task: t, valueToReplace: missionSubHeader });
83
83
  }
84
84
  if (r.ach_public_meta.description?.includes('{{suggested_')) {
85
- missionDescription = MissionUtils.replaceTagsFavMissionTask({ task: t, valueToReplace: r.ach_public_meta.description });
85
+ missionDescription = MissionUtils.replaceTagsFavMissionTask({ task: t, valueToReplace: missionDescription });
86
86
  }
87
87
  });
88
88
  }
@@ -50,6 +50,24 @@ export interface SmarticoWidgetParams {
50
50
  zoom?: number;
51
51
  /** Force the mobile layout regardless of viewport. */
52
52
  force_mobile?: boolean;
53
+ /**
54
+ * Custom mini-games only ({@link SmarticoGlobal.miniGame}): `true` fills the whole mini-game window,
55
+ * `false` keeps the default fixed-size frame. Overrides the template's "Standalone full screen" setting;
56
+ * when omitted, the template setting is used.
57
+ */
58
+ fullscreen?: boolean;
59
+ /**
60
+ * Custom mini-games only ({@link SmarticoGlobal.miniGame}): desktop width of the game frame as a CSS size
61
+ * (`1000px`, `100%`, `90vw`; a number is treated as pixels). Overrides the template's "Standalone width"
62
+ * and "Standalone full screen" settings. Ignored on mobile.
63
+ */
64
+ frame_width?: string | number;
65
+ /**
66
+ * Custom mini-games only ({@link SmarticoGlobal.miniGame}): desktop height of the game frame as a CSS size
67
+ * (`600px`, `100%`, `90vh`; a number is treated as pixels). Overrides the template's "Standalone height"
68
+ * and "Standalone full screen" settings. Ignored on mobile.
69
+ */
70
+ frame_height?: string | number;
53
71
  }
54
72
 
55
73
  /**
@@ -363,6 +363,16 @@ export class WSAPIInbox extends WSAPILeaderBoard {
363
363
  * for safe execution — that helper handles URL vs deep-link
364
364
  * routing.
365
365
  *
366
+ * **Preview vs silent delivery**
367
+ * Every message belongs in your inbox list. `show_preview` decides
368
+ * whether it should also pop up briefly when it arrives:
369
+ * - `show_preview !== false` → show a preview popup for
370
+ * `show_duration_sec ?? 7` seconds.
371
+ * - `show_preview === false` → store it silently, with no popup.
372
+ * These fields are returned exactly as the operator configured them;
373
+ * a missing value means the default above, the same as the default
374
+ * Smartico UI.
375
+ *
366
376
  * **Idempotency / Side effects**: safe. Read-only HTTP fetch.
367
377
  *
368
378
  * **UI guidance**: see [UI Guide — `getInboxMessageBody`](../../docs/ui/inbox/UIGuide_getInboxMessageBody.md).
@@ -388,6 +398,12 @@ export class WSAPIInbox extends WSAPILeaderBoard {
388
398
  * console.log('[smartico] simple message — show preview_body + single CTA wired to:', body.action,
389
399
  * '— execute via _smartico.dp(body.action)');
390
400
  * }
401
+ *
402
+ * if (body.show_preview !== false) {
403
+ * console.log('[smartico] new message — show a preview popup for', body.show_duration_sec ?? 7, 'seconds');
404
+ * } else {
405
+ * console.log('[smartico] new message — store it in the inbox silently, no popup');
406
+ * }
391
407
  * ```
392
408
  */
393
409
  public async getInboxMessageBody(messageGuid: string): Promise<TInboxMessageBody> {
@@ -1146,6 +1146,12 @@ export interface TInboxMessageBody {
1146
1146
  /** Operator-defined custom data. The SDK auto-parses JSON-looking
1147
1147
  * strings, so at runtime this is `any` despite the `string` type. */
1148
1148
  custom_data?: string;
1149
+ /** Whether to show a short preview popup when the message arrives.
1150
+ * `false` means store it in the inbox silently; missing means `true`. */
1151
+ show_preview?: boolean;
1152
+ /** How long the preview popup stays visible, in seconds.
1153
+ * `null` or missing means 7 seconds. */
1154
+ show_duration_sec?: number | null;
1149
1155
  }
1150
1156
 
1151
1157
  /**