@thestatic-tv/dcl-sdk 2.3.0 → 2.4.0
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/README.md +55 -427
- package/dist/index.d.mts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +50 -3
- package/dist/index.mjs +50 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,489 +5,117 @@ Connect your Decentraland scene to [thestatic.tv](https://thestatic.tv) - the de
|
|
|
5
5
|
[](https://www.npmjs.com/package/@thestatic-tv/dcl-sdk)
|
|
6
6
|
[](https://docs.decentraland.org/)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
- Track visitors to your Decentraland scene
|
|
10
|
-
- Display the full thestatic.tv channel lineup in their scenes
|
|
11
|
-
- Track video watching metrics
|
|
12
|
-
- Enable likes/follows from within DCL
|
|
13
|
-
- Get referral credit for driving traffic to channels
|
|
8
|
+
## Features
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
- **Visitor Analytics** - Track visitors and session metrics
|
|
11
|
+
- **Channel Guide** - Display live streams and VODs in your scene
|
|
12
|
+
- **Watch Metrics** - Track video viewing time
|
|
13
|
+
- **Interactions** - Enable likes/follows from within DCL
|
|
14
|
+
- **Built-in UI** - Pre-built Guide and Chat components
|
|
15
|
+
- **Admin Panel** - In-scene controls for Pro tier
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|------|-------|----------|
|
|
19
|
-
| **Free** | $5/mo | Session/visitor tracking only |
|
|
20
|
-
| **Standard** | $10/mo | Free + Guide UI, Chat UI, Heartbeat, Interactions |
|
|
21
|
-
| **Pro** | $15/mo | Standard + Admin Panel (Video tab, Mod tab, Custom scene tabs) |
|
|
22
|
-
|
|
23
|
-
> **All keys use `dcls_` prefix** - the server determines your subscription level.
|
|
24
|
-
>
|
|
25
|
-
> **Free Trial**: All new scene keys include a 7-day free trial!
|
|
26
|
-
>
|
|
27
|
-
> **Expiry Warnings**: You'll receive dashboard notifications at 7, 3, and 1 days before your subscription expires.
|
|
28
|
-
|
|
29
|
-
Get your key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) → DCL Scenes tab.
|
|
30
|
-
|
|
31
|
-
### Coordinate Locking
|
|
32
|
-
|
|
33
|
-
When you create a scene key, you'll be asked to confirm your parcel coordinates. **Once confirmed, coordinates are permanently locked** to prevent key reuse across different scenes. This ensures analytics accurately reflect a single scene.
|
|
34
|
-
|
|
35
|
-
### Key Rotation
|
|
36
|
-
|
|
37
|
-
If you need to rotate your API key (e.g., if compromised), you can do so from the dashboard without losing your subscription or analytics history. The old key is immediately invalidated.
|
|
38
|
-
|
|
39
|
-
## Example Scene
|
|
40
|
-
|
|
41
|
-
Clone our example scene to get started quickly:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
git clone https://github.com/thestatic-tv/thestatic-dcl-example.git
|
|
45
|
-
cd thestatic-dcl-example
|
|
46
|
-
npm install
|
|
47
|
-
npm start
|
|
48
|
-
```
|
|
17
|
+
## Quick Start
|
|
49
18
|
|
|
50
|
-
|
|
19
|
+
1. Get your API key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) (DCL Scenes tab)
|
|
51
20
|
|
|
52
|
-
|
|
21
|
+
2. Install the SDK:
|
|
53
22
|
|
|
54
23
|
```bash
|
|
55
24
|
npm install @thestatic-tv/dcl-sdk
|
|
56
25
|
```
|
|
57
26
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
1. Get your API key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) (DCL Scenes tab)
|
|
61
|
-
|
|
62
|
-
2. Initialize the client in your scene's `main()` function:
|
|
27
|
+
3. Initialize in your scene:
|
|
63
28
|
|
|
64
29
|
```typescript
|
|
65
|
-
import {} from '@dcl/sdk/math'
|
|
66
|
-
import { engine } from '@dcl/sdk/ecs'
|
|
67
30
|
import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
|
|
68
31
|
|
|
69
32
|
let staticTV: StaticTVClient
|
|
70
33
|
|
|
71
34
|
export function main() {
|
|
72
35
|
staticTV = new StaticTVClient({
|
|
73
|
-
apiKey: '
|
|
36
|
+
apiKey: 'your_api_key_here'
|
|
74
37
|
})
|
|
75
|
-
// Session tracking starts automatically
|
|
38
|
+
// Session tracking starts automatically
|
|
76
39
|
}
|
|
77
40
|
```
|
|
78
41
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
// Get all channels
|
|
83
|
-
const channels = await staticTV.guide.getChannels()
|
|
42
|
+
## Example Scene
|
|
84
43
|
|
|
85
|
-
|
|
86
|
-
const liveChannels = await staticTV.guide.getLiveChannels()
|
|
44
|
+
Clone our starter scene to get up and running quickly:
|
|
87
45
|
|
|
88
|
-
|
|
89
|
-
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/thestatic-tv/thestatic-dcl-starter.git
|
|
48
|
+
cd thestatic-dcl-starter
|
|
49
|
+
npm install
|
|
50
|
+
npm start
|
|
90
51
|
```
|
|
91
52
|
|
|
92
|
-
|
|
53
|
+
## Basic Usage
|
|
93
54
|
|
|
94
55
|
```typescript
|
|
95
|
-
//
|
|
96
|
-
staticTV.
|
|
56
|
+
// Get channels (Standard/Pro tier)
|
|
57
|
+
const channels = await staticTV.guide.getChannels()
|
|
58
|
+
const liveChannels = await staticTV.guide.getLiveChannels()
|
|
97
59
|
|
|
98
|
-
//
|
|
60
|
+
// Track video watching
|
|
61
|
+
staticTV.heartbeat.startWatching('channel-slug')
|
|
99
62
|
staticTV.heartbeat.stopWatching()
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
5. Enable interactions:
|
|
103
63
|
|
|
104
|
-
|
|
105
|
-
// Like a channel (requires wallet connection)
|
|
64
|
+
// User interactions (requires wallet)
|
|
106
65
|
await staticTV.interactions.like('channel-slug')
|
|
107
|
-
|
|
108
|
-
// Follow a channel
|
|
109
66
|
await staticTV.interactions.follow('channel-slug')
|
|
110
|
-
```
|
|
111
67
|
|
|
112
|
-
|
|
68
|
+
// Check your tier
|
|
69
|
+
console.log('Tier:', staticTV.tier) // 'free', 'standard', or 'pro'
|
|
113
70
|
|
|
114
|
-
|
|
71
|
+
// Cleanup
|
|
115
72
|
await staticTV.destroy()
|
|
116
73
|
```
|
|
117
74
|
|
|
118
|
-
##
|
|
119
|
-
|
|
120
|
-
If you don't have a channel but want to track visitors to your scene:
|
|
121
|
-
|
|
122
|
-
1. Get a scene key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) (DCL Scenes tab)
|
|
123
|
-
|
|
124
|
-
2. Initialize with your scene key in `main()`:
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import {} from '@dcl/sdk/math'
|
|
128
|
-
import { engine } from '@dcl/sdk/ecs'
|
|
129
|
-
import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
|
|
130
|
-
|
|
131
|
-
let staticTV: StaticTVClient
|
|
132
|
-
|
|
133
|
-
export function main() {
|
|
134
|
-
staticTV = new StaticTVClient({
|
|
135
|
-
apiKey: 'dcls_your_scene_key_here'
|
|
136
|
-
})
|
|
137
|
-
// Session tracking starts automatically!
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
3. Check the current tier:
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
// Check tier (free, standard, or pro)
|
|
145
|
-
console.log('Current tier:', staticTV.tier)
|
|
146
|
-
|
|
147
|
-
if (staticTV.isFree) {
|
|
148
|
-
console.log('Running in free tier - session tracking only')
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// guide, heartbeat, and interactions are null in free tier
|
|
152
|
-
if (staticTV.guide) {
|
|
153
|
-
const channels = await staticTV.guide.getChannels()
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## API Reference
|
|
158
|
-
|
|
159
|
-
### StaticTVClient
|
|
160
|
-
|
|
161
|
-
Main client class for interacting with thestatic.tv.
|
|
162
|
-
|
|
163
|
-
#### Constructor Options
|
|
164
|
-
|
|
165
|
-
| Option | Type | Default | Description |
|
|
166
|
-
|--------|------|---------|-------------|
|
|
167
|
-
| `apiKey` | `string` | required | Your API key (all keys use `dcls_` prefix) |
|
|
168
|
-
| `autoStartSession` | `boolean` | `true` | Automatically start session tracking |
|
|
169
|
-
| `sessionHeartbeatInterval` | `number` | `30000` | Session heartbeat interval (ms) |
|
|
170
|
-
| `watchHeartbeatInterval` | `number` | `60000` | Watch heartbeat interval (ms) |
|
|
171
|
-
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
172
|
-
|
|
173
|
-
#### Properties
|
|
174
|
-
|
|
175
|
-
| Property | Type | Description |
|
|
176
|
-
|----------|------|-------------|
|
|
177
|
-
| `keyType` | `'channel' \| 'scene'` | The detected key type |
|
|
178
|
-
| `tier` | `'free' \| 'standard' \| 'pro'` | Current subscription tier |
|
|
179
|
-
| `isFree` | `boolean` | `true` if free tier (session tracking only) |
|
|
180
|
-
| `hasStandardFeatures` | `boolean` | `true` if standard features enabled |
|
|
181
|
-
| `hasProFeatures` | `boolean` | `true` if pro features enabled |
|
|
182
|
-
| `guide` | `GuideModule \| null` | Guide module (null in free tier) |
|
|
183
|
-
| `session` | `SessionModule` | Session module (always available) |
|
|
184
|
-
| `heartbeat` | `HeartbeatModule \| null` | Heartbeat module (null in free tier) |
|
|
185
|
-
| `interactions` | `InteractionsModule \| null` | Interactions module (null in free tier) |
|
|
186
|
-
| `guideUI` | `GuideUIModule \| null` | Guide UI module (null in free tier) |
|
|
187
|
-
| `chatUI` | `ChatUIModule \| null` | Chat UI module (null in free tier) |
|
|
188
|
-
| `adminPanel` | `AdminPanelUIModule \| null` | Admin panel (null until enableProFeatures() called) |
|
|
189
|
-
| `isLite` | `boolean` | **Deprecated**: Use `isFree` instead |
|
|
190
|
-
|
|
191
|
-
#### Methods
|
|
192
|
-
|
|
193
|
-
| Method | Description |
|
|
194
|
-
|--------|-------------|
|
|
195
|
-
| `enableProFeatures(config)` | Enable Pro tier admin panel |
|
|
196
|
-
| `registerSceneTab(tab)` | Add custom scene tab (Pro tier) |
|
|
197
|
-
| `destroy()` | Cleanup before scene unload |
|
|
198
|
-
|
|
199
|
-
### Guide Module (Standard/Pro Tier)
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
staticTV.guide.getChannels() // Get all channels (cached 30s)
|
|
203
|
-
staticTV.guide.getLiveChannels() // Get only live channels
|
|
204
|
-
staticTV.guide.getChannel(slug) // Get a specific channel
|
|
205
|
-
staticTV.guide.getVods() // Get VODs
|
|
206
|
-
staticTV.guide.clearCache() // Clear the channel cache
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Session Module
|
|
210
|
-
|
|
211
|
-
Session tracking starts automatically by default.
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
staticTV.session.startSession() // Manually start session
|
|
215
|
-
staticTV.session.endSession() // End session
|
|
216
|
-
staticTV.session.getSessionId() // Get current session ID
|
|
217
|
-
staticTV.session.isSessionActive() // Check if session is active
|
|
218
|
-
|
|
219
|
-
// Get scene stats (visitors, sessions, etc.)
|
|
220
|
-
const stats = await staticTV.session.getStats()
|
|
221
|
-
if (stats) {
|
|
222
|
-
console.log('Total sessions today:', stats.totalSessions)
|
|
223
|
-
console.log('Unique visitors:', stats.uniqueVisitors)
|
|
224
|
-
console.log('Total minutes watched:', stats.totalMinutes)
|
|
225
|
-
console.log('You are visitor #', stats.visitorNumber)
|
|
226
|
-
}
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
### Heartbeat Module (Standard/Pro Tier)
|
|
230
|
-
|
|
231
|
-
Track video watching. Each heartbeat represents 1 minute watched.
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
staticTV.heartbeat.startWatching(channelSlug) // Start tracking
|
|
235
|
-
staticTV.heartbeat.stopWatching() // Stop tracking
|
|
236
|
-
staticTV.heartbeat.getCurrentChannel() // Get currently watched channel
|
|
237
|
-
staticTV.heartbeat.isCurrentlyWatching() // Check if watching
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Interactions Module (Standard/Pro Tier)
|
|
75
|
+
## Built-in UI Components
|
|
241
76
|
|
|
242
|
-
|
|
77
|
+
The SDK includes pre-built UI for channel browsing and chat:
|
|
243
78
|
|
|
244
79
|
```typescript
|
|
245
|
-
staticTV
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
Built-in channel browser UI. You provide a callback to handle video selection.
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
let staticTV: StaticTVClient
|
|
255
|
-
|
|
256
|
-
export function main() {
|
|
257
|
-
staticTV = new StaticTVClient({
|
|
258
|
-
apiKey: 'dcls_your_api_key',
|
|
259
|
-
guideUI: {
|
|
260
|
-
onVideoSelect: (video) => {
|
|
261
|
-
// Handle video playback in your scene
|
|
262
|
-
console.log('Playing:', video.name, video.src)
|
|
263
|
-
}
|
|
80
|
+
staticTV = new StaticTVClient({
|
|
81
|
+
apiKey: 'your_api_key',
|
|
82
|
+
guideUI: {
|
|
83
|
+
onVideoSelect: (video) => {
|
|
84
|
+
// Handle video playback
|
|
85
|
+
console.log('Playing:', video.name)
|
|
264
86
|
}
|
|
265
|
-
}
|
|
87
|
+
},
|
|
88
|
+
chatUI: {
|
|
89
|
+
position: 'right'
|
|
90
|
+
}
|
|
91
|
+
})
|
|
266
92
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
93
|
+
// Initialize UI components
|
|
94
|
+
staticTV.guideUI.init()
|
|
95
|
+
staticTV.chatUI.init()
|
|
270
96
|
|
|
271
|
-
// Show/hide
|
|
272
|
-
staticTV.guideUI.show()
|
|
273
|
-
staticTV.guideUI.hide()
|
|
97
|
+
// Show/hide
|
|
274
98
|
staticTV.guideUI.toggle()
|
|
275
|
-
|
|
276
|
-
// Check visibility
|
|
277
|
-
if (staticTV.guideUI.isVisible) { ... }
|
|
278
|
-
|
|
279
|
-
// Update "PLAYING" indicator
|
|
280
|
-
staticTV.guideUI.currentVideoId = 'video-id'
|
|
281
|
-
|
|
282
|
-
// Refresh data
|
|
283
|
-
await staticTV.guideUI.refresh()
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Chat UI Module (Standard/Pro Tier)
|
|
287
|
-
|
|
288
|
-
Real-time chat with Firebase authentication.
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
let staticTV: StaticTVClient
|
|
292
|
-
|
|
293
|
-
export function main() {
|
|
294
|
-
staticTV = new StaticTVClient({
|
|
295
|
-
apiKey: 'dcls_your_api_key',
|
|
296
|
-
chatUI: {
|
|
297
|
-
position: 'right', // 'left', 'center', or 'right'
|
|
298
|
-
fontScale: 1.0
|
|
299
|
-
}
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
// Initialize the chat (call once after client is created)
|
|
303
|
-
staticTV.chatUI.init()
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Show/hide the chat
|
|
307
|
-
staticTV.chatUI.show()
|
|
308
|
-
staticTV.chatUI.hide()
|
|
309
99
|
staticTV.chatUI.toggle()
|
|
310
|
-
|
|
311
|
-
// Check visibility
|
|
312
|
-
if (staticTV.chatUI.isVisible) { ... }
|
|
313
|
-
|
|
314
|
-
// Get unread message count (for badge display)
|
|
315
|
-
const unread = staticTV.chatUI.unreadCount
|
|
316
|
-
|
|
317
|
-
// Switch channels
|
|
318
|
-
staticTV.chatUI.setChannel('channel-slug')
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Admin Panel Module (Pro Tier)
|
|
322
|
-
|
|
323
|
-
In-scene admin panel with Video and Mod tabs. Allows scene owners/admins to:
|
|
324
|
-
- Control live streaming (start/stop, rotate keys)
|
|
325
|
-
- Play videos from URL or predefined slots
|
|
326
|
-
- Manage scene admins and banned wallets
|
|
327
|
-
- Send broadcast messages to all players
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
|
|
331
|
-
import ReactEcs, { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
|
|
332
|
-
|
|
333
|
-
let staticTV: StaticTVClient
|
|
334
|
-
|
|
335
|
-
export function main() {
|
|
336
|
-
staticTV = new StaticTVClient({
|
|
337
|
-
apiKey: 'dcls_your_api_key'
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
// Enable admin panel (Pro tier)
|
|
341
|
-
staticTV.enableProFeatures({
|
|
342
|
-
// sceneId is optional - defaults to your API key ID
|
|
343
|
-
title: 'MY SCENE ADMIN', // Panel header title
|
|
344
|
-
onVideoPlay: (url) => {
|
|
345
|
-
// Handle video playback in your scene
|
|
346
|
-
videoPlayer.play(url)
|
|
347
|
-
},
|
|
348
|
-
onVideoStop: () => {
|
|
349
|
-
videoPlayer.stop()
|
|
350
|
-
},
|
|
351
|
-
onVideoSlotPlay: (slot) => {
|
|
352
|
-
// Play a predefined video slot (slot1-slot5)
|
|
353
|
-
fetchAndPlaySlot(slot)
|
|
354
|
-
},
|
|
355
|
-
onBroadcast: (text) => {
|
|
356
|
-
// Show notification to all players
|
|
357
|
-
showNotification(text)
|
|
358
|
-
},
|
|
359
|
-
onCommand: (type, payload) => {
|
|
360
|
-
// Handle custom commands (kickAll, kickBanned, etc.)
|
|
361
|
-
handleCommand(type, payload)
|
|
362
|
-
}
|
|
363
|
-
})
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Render the admin panel
|
|
367
|
-
ReactEcsRenderer.setUiRenderer(() => {
|
|
368
|
-
return staticTV.adminPanel?.getComponent()
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
// Toggle panel visibility
|
|
372
|
-
staticTV.adminPanel.toggle()
|
|
373
|
-
|
|
374
|
-
// Check if user has admin access
|
|
375
|
-
if (staticTV.adminPanel.hasAccess) { ... }
|
|
376
100
|
```
|
|
377
101
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
| Option | Type | Default | Description |
|
|
381
|
-
|--------|------|---------|-------------|
|
|
382
|
-
| `sceneId` | `string` | API key ID | Scene ID for API calls (optional - defaults to your API key ID) |
|
|
383
|
-
| `title` | `string` | `'ADMIN PANEL'` | Header title |
|
|
384
|
-
| `headerColor` | `{r,g,b,a}` | red | Header background color |
|
|
385
|
-
| `showVideoTab` | `boolean` | `true` | Show Video tab |
|
|
386
|
-
| `showModTab` | `boolean` | `true` | Show Mod tab (owners only) |
|
|
387
|
-
| `onVideoPlay` | `(url) => void` | - | Called when video should play |
|
|
388
|
-
| `onVideoStop` | `() => void` | - | Called when video should stop |
|
|
389
|
-
| `onVideoSlotPlay` | `(slot) => void` | - | Called when slot is selected |
|
|
390
|
-
| `onBroadcast` | `(text) => void` | - | Called for broadcast messages |
|
|
391
|
-
| `onCommand` | `(type, payload) => void` | - | Called for custom commands |
|
|
392
|
-
| `footerLink` | `string` | scene page | Link shown in footer |
|
|
393
|
-
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
394
|
-
|
|
395
|
-
### Custom Scene Tabs (Pro Tier)
|
|
102
|
+
## Pricing & Tiers
|
|
396
103
|
|
|
397
|
-
|
|
104
|
+
See [thestatic.tv/info](https://thestatic.tv/info) for current pricing and tier features.
|
|
398
105
|
|
|
399
|
-
|
|
400
|
-
import ReactEcs, { UiEntity, Button, Label } from '@dcl/sdk/react-ecs'
|
|
401
|
-
|
|
402
|
-
// Enable pro features first
|
|
403
|
-
staticTV.enableProFeatures({ sceneId: 'my-scene', ... })
|
|
404
|
-
|
|
405
|
-
// Register custom tabs
|
|
406
|
-
staticTV.registerSceneTab({
|
|
407
|
-
label: 'LIGHTS',
|
|
408
|
-
id: 'lights',
|
|
409
|
-
render: () => (
|
|
410
|
-
<UiEntity uiTransform={{ flexDirection: 'column', padding: 8 }}>
|
|
411
|
-
<Label value="Light Controls" fontSize={14} />
|
|
412
|
-
<Button value="Disco Mode" onMouseDown={() => setDiscoLights()} />
|
|
413
|
-
<Button value="Ambient" onMouseDown={() => setAmbientLights()} />
|
|
414
|
-
<Button value="Off" onMouseDown={() => turnOffLights()} />
|
|
415
|
-
</UiEntity>
|
|
416
|
-
)
|
|
417
|
-
})
|
|
418
|
-
|
|
419
|
-
staticTV.registerSceneTab({
|
|
420
|
-
label: 'DOORS',
|
|
421
|
-
id: 'doors',
|
|
422
|
-
render: () => <MyDoorsControls />
|
|
423
|
-
})
|
|
424
|
-
```
|
|
106
|
+
All new keys include a **7-day free trial**.
|
|
425
107
|
|
|
426
|
-
|
|
108
|
+
## Documentation
|
|
427
109
|
|
|
428
|
-
|
|
110
|
+
Full API reference and Pro tier features (Admin Panel, Custom Tabs) available at:
|
|
429
111
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
import ReactEcs, { ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
|
|
434
|
-
|
|
435
|
-
// Outside main() - required by DCL
|
|
436
|
-
ReactEcsRenderer.setUiRenderer(() => {
|
|
437
|
-
if (!staticTV) return null
|
|
438
|
-
return ReactEcs.createElement(UiEntity, {
|
|
439
|
-
uiTransform: { width: '100%', height: '100%', positionType: 'absolute' },
|
|
440
|
-
children: [
|
|
441
|
-
staticTV.guideUI?.getComponent(),
|
|
442
|
-
staticTV.chatUI?.getComponent()
|
|
443
|
-
].filter(Boolean)
|
|
444
|
-
})
|
|
445
|
-
})
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
## Metrics Attribution
|
|
449
|
-
|
|
450
|
-
This SDK implements dual attribution:
|
|
451
|
-
|
|
452
|
-
- **Watched Channel**: Gets view metrics (minutes watched, likes, follows)
|
|
453
|
-
- **Scene Owner**: Gets referral metrics (unique visitors, traffic driven)
|
|
454
|
-
|
|
455
|
-
Your channel (linked to your API key) receives credit for all traffic your scene drives to thestatic.tv channels.
|
|
456
|
-
|
|
457
|
-
## Types
|
|
458
|
-
|
|
459
|
-
```typescript
|
|
460
|
-
interface Channel {
|
|
461
|
-
id: string
|
|
462
|
-
slug: string
|
|
463
|
-
name: string
|
|
464
|
-
streamUrl: string
|
|
465
|
-
isLive: boolean
|
|
466
|
-
currentViewers: number
|
|
467
|
-
poster: string | null
|
|
468
|
-
logo: string | null
|
|
469
|
-
description: string
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
interface Vod {
|
|
473
|
-
id: string
|
|
474
|
-
title: string
|
|
475
|
-
url: string
|
|
476
|
-
thumbnail: string | null
|
|
477
|
-
channelId: string
|
|
478
|
-
}
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
## Requirements
|
|
482
|
-
|
|
483
|
-
- Decentraland SDK 7.0.0 or higher
|
|
484
|
-
- API key from thestatic.tv
|
|
112
|
+
- **Docs**: [thestatic.tv/info](https://thestatic.tv/info)
|
|
113
|
+
- **Examples**: [github.com/thestatic-tv/thestatic-dcl-starter](https://github.com/thestatic-tv/thestatic-dcl-starter)
|
|
485
114
|
|
|
486
115
|
## Support
|
|
487
116
|
|
|
488
|
-
- Documentation: [thestatic.tv/info](https://thestatic.tv/info)
|
|
489
|
-
- Issues: [GitHub Issues](https://github.com/thestatic-tv/dcl-sdk/issues)
|
|
490
117
|
- Discord: [thestatic.tv Discord](https://discord.gg/thestatic)
|
|
118
|
+
- Issues: [GitHub Issues](https://github.com/thestatic-tv/dcl-sdk/issues)
|
|
491
119
|
|
|
492
120
|
## License
|
|
493
121
|
|
package/dist/index.d.mts
CHANGED
|
@@ -863,6 +863,9 @@ declare class StaticTVClient {
|
|
|
863
863
|
private _standardFeaturesEnabled;
|
|
864
864
|
private _proFeaturesEnabled;
|
|
865
865
|
private _pendingProConfig;
|
|
866
|
+
private _featuresReadyPromise;
|
|
867
|
+
private _featuresReadyResolve;
|
|
868
|
+
private _featuresResolved;
|
|
866
869
|
/** Guide module - fetch channel lineup (standard/pro tier) */
|
|
867
870
|
guide: GuideModule | null;
|
|
868
871
|
/** Session module - track visitor sessions (all tiers, null when disabled) */
|
|
@@ -933,6 +936,33 @@ declare class StaticTVClient {
|
|
|
933
936
|
* @deprecated Use `isFree` instead. Kept for backward compatibility.
|
|
934
937
|
*/
|
|
935
938
|
get isLite(): boolean;
|
|
939
|
+
/**
|
|
940
|
+
* Wait for tier confirmation from the server.
|
|
941
|
+
* Resolves when the SDK knows which features are available.
|
|
942
|
+
*
|
|
943
|
+
* Use this instead of polling `isLite` or `isFree` in a while loop.
|
|
944
|
+
*
|
|
945
|
+
* @returns Promise that resolves with the confirmed tier ('free', 'standard', or 'pro')
|
|
946
|
+
*
|
|
947
|
+
* @example
|
|
948
|
+
* ```typescript
|
|
949
|
+
* const staticTV = new StaticTVClient({ apiKey: 'dcls_...' })
|
|
950
|
+
*
|
|
951
|
+
* // Wait for tier to be confirmed
|
|
952
|
+
* const tier = await staticTV.onFeaturesReady()
|
|
953
|
+
*
|
|
954
|
+
* // Now modules are guaranteed to be initialized (or confirmed unavailable)
|
|
955
|
+
* if (staticTV.guideUI) {
|
|
956
|
+
* await staticTV.guideUI.init()
|
|
957
|
+
* console.log('Guide ready!')
|
|
958
|
+
* }
|
|
959
|
+
*
|
|
960
|
+
* if (tier === 'free') {
|
|
961
|
+
* console.log('Upgrade at thestatic.tv for Guide & Chat!')
|
|
962
|
+
* }
|
|
963
|
+
* ```
|
|
964
|
+
*/
|
|
965
|
+
onFeaturesReady(): Promise<SDKTier>;
|
|
936
966
|
/**
|
|
937
967
|
* Make an authenticated API request
|
|
938
968
|
* @internal
|
|
@@ -995,6 +1025,11 @@ declare class StaticTVClient {
|
|
|
995
1025
|
* @internal
|
|
996
1026
|
*/
|
|
997
1027
|
private _initProModules;
|
|
1028
|
+
/**
|
|
1029
|
+
* Resolve the features ready promise (only once)
|
|
1030
|
+
* @internal
|
|
1031
|
+
*/
|
|
1032
|
+
private _resolveFeaturesReady;
|
|
998
1033
|
/**
|
|
999
1034
|
* Called by SessionModule when server returns the tier
|
|
1000
1035
|
* Enables modules based on tier level
|
package/dist/index.d.ts
CHANGED
|
@@ -863,6 +863,9 @@ declare class StaticTVClient {
|
|
|
863
863
|
private _standardFeaturesEnabled;
|
|
864
864
|
private _proFeaturesEnabled;
|
|
865
865
|
private _pendingProConfig;
|
|
866
|
+
private _featuresReadyPromise;
|
|
867
|
+
private _featuresReadyResolve;
|
|
868
|
+
private _featuresResolved;
|
|
866
869
|
/** Guide module - fetch channel lineup (standard/pro tier) */
|
|
867
870
|
guide: GuideModule | null;
|
|
868
871
|
/** Session module - track visitor sessions (all tiers, null when disabled) */
|
|
@@ -933,6 +936,33 @@ declare class StaticTVClient {
|
|
|
933
936
|
* @deprecated Use `isFree` instead. Kept for backward compatibility.
|
|
934
937
|
*/
|
|
935
938
|
get isLite(): boolean;
|
|
939
|
+
/**
|
|
940
|
+
* Wait for tier confirmation from the server.
|
|
941
|
+
* Resolves when the SDK knows which features are available.
|
|
942
|
+
*
|
|
943
|
+
* Use this instead of polling `isLite` or `isFree` in a while loop.
|
|
944
|
+
*
|
|
945
|
+
* @returns Promise that resolves with the confirmed tier ('free', 'standard', or 'pro')
|
|
946
|
+
*
|
|
947
|
+
* @example
|
|
948
|
+
* ```typescript
|
|
949
|
+
* const staticTV = new StaticTVClient({ apiKey: 'dcls_...' })
|
|
950
|
+
*
|
|
951
|
+
* // Wait for tier to be confirmed
|
|
952
|
+
* const tier = await staticTV.onFeaturesReady()
|
|
953
|
+
*
|
|
954
|
+
* // Now modules are guaranteed to be initialized (or confirmed unavailable)
|
|
955
|
+
* if (staticTV.guideUI) {
|
|
956
|
+
* await staticTV.guideUI.init()
|
|
957
|
+
* console.log('Guide ready!')
|
|
958
|
+
* }
|
|
959
|
+
*
|
|
960
|
+
* if (tier === 'free') {
|
|
961
|
+
* console.log('Upgrade at thestatic.tv for Guide & Chat!')
|
|
962
|
+
* }
|
|
963
|
+
* ```
|
|
964
|
+
*/
|
|
965
|
+
onFeaturesReady(): Promise<SDKTier>;
|
|
936
966
|
/**
|
|
937
967
|
* Make an authenticated API request
|
|
938
968
|
* @internal
|
|
@@ -995,6 +1025,11 @@ declare class StaticTVClient {
|
|
|
995
1025
|
* @internal
|
|
996
1026
|
*/
|
|
997
1027
|
private _initProModules;
|
|
1028
|
+
/**
|
|
1029
|
+
* Resolve the features ready promise (only once)
|
|
1030
|
+
* @internal
|
|
1031
|
+
*/
|
|
1032
|
+
private _resolveFeaturesReady;
|
|
998
1033
|
/**
|
|
999
1034
|
* Called by SessionModule when server returns the tier
|
|
1000
1035
|
* Enables modules based on tier level
|
package/dist/index.js
CHANGED
|
@@ -2540,7 +2540,7 @@ var AdminPanelUIModule = class {
|
|
|
2540
2540
|
{
|
|
2541
2541
|
uiTransform: { height: this.s(24) },
|
|
2542
2542
|
uiBackground: { color: import_math5.Color4.create(0, 0, 0, 0) },
|
|
2543
|
-
value: "Edit slots at thestatic.tv
|
|
2543
|
+
value: "Edit slots at thestatic.tv",
|
|
2544
2544
|
fontSize: t.labelSmall,
|
|
2545
2545
|
color: C.cyan,
|
|
2546
2546
|
onMouseDown: () => (0, import_RestrictedActions3.openExternalUrl)({ url: this.config.footerLink || `https://thestatic.tv/scene/${this.config.sceneId}` })
|
|
@@ -2695,7 +2695,7 @@ var AdminPanelUIModule = class {
|
|
|
2695
2695
|
{
|
|
2696
2696
|
uiTransform: { height: this.s(24) },
|
|
2697
2697
|
uiBackground: { color: import_math5.Color4.create(0, 0, 0, 0) },
|
|
2698
|
-
value: "Manage at thestatic.tv
|
|
2698
|
+
value: "Manage at thestatic.tv",
|
|
2699
2699
|
fontSize: t.labelSmall,
|
|
2700
2700
|
color: C.cyan,
|
|
2701
2701
|
onMouseDown: () => (0, import_RestrictedActions3.openExternalUrl)({ url: this.config.footerLink || `https://thestatic.tv/scene/${this.config.sceneId}` })
|
|
@@ -2821,7 +2821,7 @@ var AdminPanelUIModule = class {
|
|
|
2821
2821
|
{
|
|
2822
2822
|
uiTransform: { height: this.s(24) },
|
|
2823
2823
|
uiBackground: { color: import_math5.Color4.create(0, 0, 0, 0) },
|
|
2824
|
-
value: `thestatic.tv/scene/${this.config.sceneId}
|
|
2824
|
+
value: `thestatic.tv/scene/${this.config.sceneId}`,
|
|
2825
2825
|
fontSize: t.labelSmall,
|
|
2826
2826
|
color: C.cyan,
|
|
2827
2827
|
onMouseDown: () => (0, import_RestrictedActions3.openExternalUrl)({ url: this.config.footerLink || `https://thestatic.tv/scene/${this.config.sceneId}` })
|
|
@@ -3535,6 +3535,7 @@ var StaticTVClient = class {
|
|
|
3535
3535
|
this._standardFeaturesEnabled = false;
|
|
3536
3536
|
this._proFeaturesEnabled = false;
|
|
3537
3537
|
this._pendingProConfig = null;
|
|
3538
|
+
this._featuresResolved = false;
|
|
3538
3539
|
/** Guide module - fetch channel lineup (standard/pro tier) */
|
|
3539
3540
|
this.guide = null;
|
|
3540
3541
|
/** Session module - track visitor sessions (all tiers, null when disabled) */
|
|
@@ -3555,6 +3556,9 @@ var StaticTVClient = class {
|
|
|
3555
3556
|
// --- VIDEO PLAYBACK (Internal handler for videoScreen) ---
|
|
3556
3557
|
// =============================================================================
|
|
3557
3558
|
this._currentVideoUrl = "";
|
|
3559
|
+
this._featuresReadyPromise = new Promise((resolve) => {
|
|
3560
|
+
this._featuresReadyResolve = resolve;
|
|
3561
|
+
});
|
|
3558
3562
|
this.config = {
|
|
3559
3563
|
autoStartSession: true,
|
|
3560
3564
|
sessionHeartbeatInterval: 3e4,
|
|
@@ -3572,6 +3576,7 @@ var StaticTVClient = class {
|
|
|
3572
3576
|
this.interactions = null;
|
|
3573
3577
|
this.guideUI = null;
|
|
3574
3578
|
this.chatUI = null;
|
|
3579
|
+
this._resolveFeaturesReady("free");
|
|
3575
3580
|
return;
|
|
3576
3581
|
}
|
|
3577
3582
|
if (config.apiKey.startsWith("dclk_")) {
|
|
@@ -3582,12 +3587,14 @@ var StaticTVClient = class {
|
|
|
3582
3587
|
console.warn("[TheStatic] Invalid API key format - get your key at thestatic.tv/dashboard");
|
|
3583
3588
|
this._disabled = true;
|
|
3584
3589
|
this._keyType = null;
|
|
3590
|
+
this._resolveFeaturesReady("free");
|
|
3585
3591
|
return;
|
|
3586
3592
|
}
|
|
3587
3593
|
this.session = new SessionModule(this);
|
|
3588
3594
|
if (this._keyType === KEY_TYPE_CHANNEL) {
|
|
3589
3595
|
this._tier = "standard";
|
|
3590
3596
|
this._initStandardModules();
|
|
3597
|
+
this._resolveFeaturesReady("standard");
|
|
3591
3598
|
}
|
|
3592
3599
|
if (this.config.autoStartSession) {
|
|
3593
3600
|
fetchUserData().then(() => {
|
|
@@ -3638,6 +3645,35 @@ var StaticTVClient = class {
|
|
|
3638
3645
|
get isLite() {
|
|
3639
3646
|
return this.isFree;
|
|
3640
3647
|
}
|
|
3648
|
+
/**
|
|
3649
|
+
* Wait for tier confirmation from the server.
|
|
3650
|
+
* Resolves when the SDK knows which features are available.
|
|
3651
|
+
*
|
|
3652
|
+
* Use this instead of polling `isLite` or `isFree` in a while loop.
|
|
3653
|
+
*
|
|
3654
|
+
* @returns Promise that resolves with the confirmed tier ('free', 'standard', or 'pro')
|
|
3655
|
+
*
|
|
3656
|
+
* @example
|
|
3657
|
+
* ```typescript
|
|
3658
|
+
* const staticTV = new StaticTVClient({ apiKey: 'dcls_...' })
|
|
3659
|
+
*
|
|
3660
|
+
* // Wait for tier to be confirmed
|
|
3661
|
+
* const tier = await staticTV.onFeaturesReady()
|
|
3662
|
+
*
|
|
3663
|
+
* // Now modules are guaranteed to be initialized (or confirmed unavailable)
|
|
3664
|
+
* if (staticTV.guideUI) {
|
|
3665
|
+
* await staticTV.guideUI.init()
|
|
3666
|
+
* console.log('Guide ready!')
|
|
3667
|
+
* }
|
|
3668
|
+
*
|
|
3669
|
+
* if (tier === 'free') {
|
|
3670
|
+
* console.log('Upgrade at thestatic.tv for Guide & Chat!')
|
|
3671
|
+
* }
|
|
3672
|
+
* ```
|
|
3673
|
+
*/
|
|
3674
|
+
async onFeaturesReady() {
|
|
3675
|
+
return this._featuresReadyPromise;
|
|
3676
|
+
}
|
|
3641
3677
|
/**
|
|
3642
3678
|
* Make an authenticated API request
|
|
3643
3679
|
* @internal
|
|
@@ -3818,6 +3854,16 @@ var StaticTVClient = class {
|
|
|
3818
3854
|
});
|
|
3819
3855
|
this.log(`Pro features enabled (admin panel) - sceneId: ${sceneId}`);
|
|
3820
3856
|
}
|
|
3857
|
+
/**
|
|
3858
|
+
* Resolve the features ready promise (only once)
|
|
3859
|
+
* @internal
|
|
3860
|
+
*/
|
|
3861
|
+
_resolveFeaturesReady(tier) {
|
|
3862
|
+
if (!this._featuresResolved) {
|
|
3863
|
+
this._featuresResolved = true;
|
|
3864
|
+
this._featuresReadyResolve(tier);
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3821
3867
|
/**
|
|
3822
3868
|
* Called by SessionModule when server returns the tier
|
|
3823
3869
|
* Enables modules based on tier level
|
|
@@ -3839,6 +3885,7 @@ var StaticTVClient = class {
|
|
|
3839
3885
|
this.log("Pro tier detected but no video config - call enableProFeatures() or set videoScreen to enable admin panel");
|
|
3840
3886
|
}
|
|
3841
3887
|
}
|
|
3888
|
+
this._resolveFeaturesReady(tier);
|
|
3842
3889
|
}
|
|
3843
3890
|
/**
|
|
3844
3891
|
* @deprecated Use `_enableFeaturesForTier` instead
|
package/dist/index.mjs
CHANGED
|
@@ -2497,7 +2497,7 @@ var AdminPanelUIModule = class {
|
|
|
2497
2497
|
{
|
|
2498
2498
|
uiTransform: { height: this.s(24) },
|
|
2499
2499
|
uiBackground: { color: Color45.create(0, 0, 0, 0) },
|
|
2500
|
-
value: "Edit slots at thestatic.tv
|
|
2500
|
+
value: "Edit slots at thestatic.tv",
|
|
2501
2501
|
fontSize: t.labelSmall,
|
|
2502
2502
|
color: C.cyan,
|
|
2503
2503
|
onMouseDown: () => openExternalUrl3({ url: this.config.footerLink || `https://thestatic.tv/scene/${this.config.sceneId}` })
|
|
@@ -2652,7 +2652,7 @@ var AdminPanelUIModule = class {
|
|
|
2652
2652
|
{
|
|
2653
2653
|
uiTransform: { height: this.s(24) },
|
|
2654
2654
|
uiBackground: { color: Color45.create(0, 0, 0, 0) },
|
|
2655
|
-
value: "Manage at thestatic.tv
|
|
2655
|
+
value: "Manage at thestatic.tv",
|
|
2656
2656
|
fontSize: t.labelSmall,
|
|
2657
2657
|
color: C.cyan,
|
|
2658
2658
|
onMouseDown: () => openExternalUrl3({ url: this.config.footerLink || `https://thestatic.tv/scene/${this.config.sceneId}` })
|
|
@@ -2778,7 +2778,7 @@ var AdminPanelUIModule = class {
|
|
|
2778
2778
|
{
|
|
2779
2779
|
uiTransform: { height: this.s(24) },
|
|
2780
2780
|
uiBackground: { color: Color45.create(0, 0, 0, 0) },
|
|
2781
|
-
value: `thestatic.tv/scene/${this.config.sceneId}
|
|
2781
|
+
value: `thestatic.tv/scene/${this.config.sceneId}`,
|
|
2782
2782
|
fontSize: t.labelSmall,
|
|
2783
2783
|
color: C.cyan,
|
|
2784
2784
|
onMouseDown: () => openExternalUrl3({ url: this.config.footerLink || `https://thestatic.tv/scene/${this.config.sceneId}` })
|
|
@@ -3492,6 +3492,7 @@ var StaticTVClient = class {
|
|
|
3492
3492
|
this._standardFeaturesEnabled = false;
|
|
3493
3493
|
this._proFeaturesEnabled = false;
|
|
3494
3494
|
this._pendingProConfig = null;
|
|
3495
|
+
this._featuresResolved = false;
|
|
3495
3496
|
/** Guide module - fetch channel lineup (standard/pro tier) */
|
|
3496
3497
|
this.guide = null;
|
|
3497
3498
|
/** Session module - track visitor sessions (all tiers, null when disabled) */
|
|
@@ -3512,6 +3513,9 @@ var StaticTVClient = class {
|
|
|
3512
3513
|
// --- VIDEO PLAYBACK (Internal handler for videoScreen) ---
|
|
3513
3514
|
// =============================================================================
|
|
3514
3515
|
this._currentVideoUrl = "";
|
|
3516
|
+
this._featuresReadyPromise = new Promise((resolve) => {
|
|
3517
|
+
this._featuresReadyResolve = resolve;
|
|
3518
|
+
});
|
|
3515
3519
|
this.config = {
|
|
3516
3520
|
autoStartSession: true,
|
|
3517
3521
|
sessionHeartbeatInterval: 3e4,
|
|
@@ -3529,6 +3533,7 @@ var StaticTVClient = class {
|
|
|
3529
3533
|
this.interactions = null;
|
|
3530
3534
|
this.guideUI = null;
|
|
3531
3535
|
this.chatUI = null;
|
|
3536
|
+
this._resolveFeaturesReady("free");
|
|
3532
3537
|
return;
|
|
3533
3538
|
}
|
|
3534
3539
|
if (config.apiKey.startsWith("dclk_")) {
|
|
@@ -3539,12 +3544,14 @@ var StaticTVClient = class {
|
|
|
3539
3544
|
console.warn("[TheStatic] Invalid API key format - get your key at thestatic.tv/dashboard");
|
|
3540
3545
|
this._disabled = true;
|
|
3541
3546
|
this._keyType = null;
|
|
3547
|
+
this._resolveFeaturesReady("free");
|
|
3542
3548
|
return;
|
|
3543
3549
|
}
|
|
3544
3550
|
this.session = new SessionModule(this);
|
|
3545
3551
|
if (this._keyType === KEY_TYPE_CHANNEL) {
|
|
3546
3552
|
this._tier = "standard";
|
|
3547
3553
|
this._initStandardModules();
|
|
3554
|
+
this._resolveFeaturesReady("standard");
|
|
3548
3555
|
}
|
|
3549
3556
|
if (this.config.autoStartSession) {
|
|
3550
3557
|
fetchUserData().then(() => {
|
|
@@ -3595,6 +3602,35 @@ var StaticTVClient = class {
|
|
|
3595
3602
|
get isLite() {
|
|
3596
3603
|
return this.isFree;
|
|
3597
3604
|
}
|
|
3605
|
+
/**
|
|
3606
|
+
* Wait for tier confirmation from the server.
|
|
3607
|
+
* Resolves when the SDK knows which features are available.
|
|
3608
|
+
*
|
|
3609
|
+
* Use this instead of polling `isLite` or `isFree` in a while loop.
|
|
3610
|
+
*
|
|
3611
|
+
* @returns Promise that resolves with the confirmed tier ('free', 'standard', or 'pro')
|
|
3612
|
+
*
|
|
3613
|
+
* @example
|
|
3614
|
+
* ```typescript
|
|
3615
|
+
* const staticTV = new StaticTVClient({ apiKey: 'dcls_...' })
|
|
3616
|
+
*
|
|
3617
|
+
* // Wait for tier to be confirmed
|
|
3618
|
+
* const tier = await staticTV.onFeaturesReady()
|
|
3619
|
+
*
|
|
3620
|
+
* // Now modules are guaranteed to be initialized (or confirmed unavailable)
|
|
3621
|
+
* if (staticTV.guideUI) {
|
|
3622
|
+
* await staticTV.guideUI.init()
|
|
3623
|
+
* console.log('Guide ready!')
|
|
3624
|
+
* }
|
|
3625
|
+
*
|
|
3626
|
+
* if (tier === 'free') {
|
|
3627
|
+
* console.log('Upgrade at thestatic.tv for Guide & Chat!')
|
|
3628
|
+
* }
|
|
3629
|
+
* ```
|
|
3630
|
+
*/
|
|
3631
|
+
async onFeaturesReady() {
|
|
3632
|
+
return this._featuresReadyPromise;
|
|
3633
|
+
}
|
|
3598
3634
|
/**
|
|
3599
3635
|
* Make an authenticated API request
|
|
3600
3636
|
* @internal
|
|
@@ -3775,6 +3811,16 @@ var StaticTVClient = class {
|
|
|
3775
3811
|
});
|
|
3776
3812
|
this.log(`Pro features enabled (admin panel) - sceneId: ${sceneId}`);
|
|
3777
3813
|
}
|
|
3814
|
+
/**
|
|
3815
|
+
* Resolve the features ready promise (only once)
|
|
3816
|
+
* @internal
|
|
3817
|
+
*/
|
|
3818
|
+
_resolveFeaturesReady(tier) {
|
|
3819
|
+
if (!this._featuresResolved) {
|
|
3820
|
+
this._featuresResolved = true;
|
|
3821
|
+
this._featuresReadyResolve(tier);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3778
3824
|
/**
|
|
3779
3825
|
* Called by SessionModule when server returns the tier
|
|
3780
3826
|
* Enables modules based on tier level
|
|
@@ -3796,6 +3842,7 @@ var StaticTVClient = class {
|
|
|
3796
3842
|
this.log("Pro tier detected but no video config - call enableProFeatures() or set videoScreen to enable admin panel");
|
|
3797
3843
|
}
|
|
3798
3844
|
}
|
|
3845
|
+
this._resolveFeaturesReady(tier);
|
|
3799
3846
|
}
|
|
3800
3847
|
/**
|
|
3801
3848
|
* @deprecated Use `_enableFeaturesForTier` instead
|
package/package.json
CHANGED