@monterosa/sdk-interact-kit 2.0.0-rc.2 → 2.0.0-rc.3
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/dist/index.cjs +2095 -0
- package/dist/index.cjs.map +1 -0
- package/package.json +10 -9
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2095 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var sdkUtil = require('@monterosa/sdk-util');
|
|
6
|
+
var sdkConnectKit = require('@monterosa/sdk-connect-kit');
|
|
7
|
+
var sdkCore = require('@monterosa/sdk-core');
|
|
8
|
+
var sdkInteractInterop = require('@monterosa/sdk-interact-interop');
|
|
9
|
+
var sdkStorageKit = require('@monterosa/sdk-storage-kit');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @license
|
|
13
|
+
* @monterosa/sdk-interact-kit
|
|
14
|
+
*
|
|
15
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
16
|
+
*
|
|
17
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
exports.State = void 0;
|
|
23
|
+
(function (State) {
|
|
24
|
+
State["Ok"] = "ok";
|
|
25
|
+
State["Error"] = "error";
|
|
26
|
+
})(exports.State || (exports.State = {}));
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
exports.Klass = void 0;
|
|
31
|
+
(function (Klass) {
|
|
32
|
+
Klass["Listings"] = "listings";
|
|
33
|
+
Klass["History"] = "history";
|
|
34
|
+
Klass["Create"] = "create";
|
|
35
|
+
Klass["Revoke"] = "revoke";
|
|
36
|
+
Klass["Stop"] = "stop";
|
|
37
|
+
Klass["Reveal"] = "reveal";
|
|
38
|
+
Klass["Poll"] = "p";
|
|
39
|
+
Klass["Feedback"] = "fb";
|
|
40
|
+
Klass["Eoc"] = "eoc";
|
|
41
|
+
Klass["Vote"] = "v";
|
|
42
|
+
Klass["SubsCounter"] = "subscounter";
|
|
43
|
+
})(exports.Klass || (exports.Klass = {}));
|
|
44
|
+
/**
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
exports.Channel = void 0;
|
|
48
|
+
(function (Channel) {
|
|
49
|
+
Channel["Poll"] = "p";
|
|
50
|
+
Channel["Live"] = "l";
|
|
51
|
+
})(exports.Channel || (exports.Channel = {}));
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @license
|
|
55
|
+
* @monterosa/sdk-interact-kit
|
|
56
|
+
*
|
|
57
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
58
|
+
*
|
|
59
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
60
|
+
*/
|
|
61
|
+
class ConnectImpl extends sdkUtil.Emitter {
|
|
62
|
+
constructor(host) {
|
|
63
|
+
super();
|
|
64
|
+
this.host = host;
|
|
65
|
+
this._state = exports.State.Ok;
|
|
66
|
+
this._enmasse = null;
|
|
67
|
+
}
|
|
68
|
+
get state() {
|
|
69
|
+
return this._state;
|
|
70
|
+
}
|
|
71
|
+
set state(state) {
|
|
72
|
+
if (state === this._state) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this._state = state;
|
|
76
|
+
this.emit('state', this._state);
|
|
77
|
+
}
|
|
78
|
+
get enmasse() {
|
|
79
|
+
if (this._enmasse === null) {
|
|
80
|
+
throw new Error('Connect is not initialised, init() method should be called first.');
|
|
81
|
+
}
|
|
82
|
+
return this._enmasse;
|
|
83
|
+
}
|
|
84
|
+
set enmasse(enmasse) {
|
|
85
|
+
this._enmasse = enmasse;
|
|
86
|
+
}
|
|
87
|
+
async init() {
|
|
88
|
+
this.enmasse = await sdkConnectKit.getConnect(this.host);
|
|
89
|
+
this.enmasse.on('message', this.handleMessage.bind(this));
|
|
90
|
+
this.enmasse.on('state', this.handleState.bind(this));
|
|
91
|
+
}
|
|
92
|
+
async connect() {
|
|
93
|
+
await sdkConnectKit.connect(this.enmasse);
|
|
94
|
+
}
|
|
95
|
+
disconnect() {
|
|
96
|
+
sdkConnectKit.disconnect(this.enmasse);
|
|
97
|
+
}
|
|
98
|
+
async subscribeProject(id) {
|
|
99
|
+
await sdkConnectKit.subscribe(this.enmasse, `${exports.Channel.Live}-${id}`);
|
|
100
|
+
}
|
|
101
|
+
unsubscribeProject(id) {
|
|
102
|
+
sdkConnectKit.unsubscribe(this.enmasse, `${exports.Channel.Live}-${id}`);
|
|
103
|
+
}
|
|
104
|
+
async subscribeEvent(id) {
|
|
105
|
+
await Promise.all([
|
|
106
|
+
sdkConnectKit.subscribe(this.enmasse, `${exports.Channel.Live}-${id}`),
|
|
107
|
+
sdkConnectKit.subscribe(this.enmasse, `${exports.Channel.Poll}-${id}`),
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
unsubscribeEvent(id) {
|
|
111
|
+
sdkConnectKit.unsubscribe(this.enmasse, `${exports.Channel.Live}-${id}`);
|
|
112
|
+
sdkConnectKit.unsubscribe(this.enmasse, `${exports.Channel.Poll}-${id}`);
|
|
113
|
+
}
|
|
114
|
+
sendVote(eventId, pollId, answer) {
|
|
115
|
+
sdkConnectKit.send(this.enmasse, `${exports.Channel.Poll}-${eventId}`, exports.Klass.Vote, [
|
|
116
|
+
pollId,
|
|
117
|
+
answer,
|
|
118
|
+
'',
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
handleMessage(message) {
|
|
122
|
+
const { klass, body: data, channel } = message;
|
|
123
|
+
const id = channel.substring(channel.indexOf('-') + 1);
|
|
124
|
+
this.emit('message', {
|
|
125
|
+
id,
|
|
126
|
+
klass,
|
|
127
|
+
data,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
handleState(state, ...args) {
|
|
131
|
+
switch (state) {
|
|
132
|
+
case sdkConnectKit.ConnState.Disconnected:
|
|
133
|
+
this.state = exports.State.Error;
|
|
134
|
+
break;
|
|
135
|
+
case sdkConnectKit.ConnState.Connecting: {
|
|
136
|
+
const [attempt] = args;
|
|
137
|
+
if (attempt > 1) {
|
|
138
|
+
this.state = exports.State.Error;
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case sdkConnectKit.ConnState.Connected:
|
|
143
|
+
this.state = exports.State.Ok;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @license
|
|
151
|
+
* @monterosa/sdk-interact-kit
|
|
152
|
+
*
|
|
153
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
154
|
+
*
|
|
155
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
156
|
+
*/
|
|
157
|
+
const getConnectMemoized = sdkUtil.memoizePromise(async (host) => {
|
|
158
|
+
const connect = new ConnectImpl(host);
|
|
159
|
+
await connect.init();
|
|
160
|
+
await connect.connect();
|
|
161
|
+
return connect;
|
|
162
|
+
}, (host) => host);
|
|
163
|
+
/**
|
|
164
|
+
* @internal
|
|
165
|
+
*/
|
|
166
|
+
function getConnect(host) {
|
|
167
|
+
return getConnectMemoized(host);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @license
|
|
172
|
+
* @monterosa/sdk-interact-kit
|
|
173
|
+
*
|
|
174
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
175
|
+
*
|
|
176
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* Provides states of the connection health
|
|
180
|
+
*/
|
|
181
|
+
exports.ConnectionHealthState = void 0;
|
|
182
|
+
(function (ConnectionHealthState) {
|
|
183
|
+
/**
|
|
184
|
+
* The client is connected and the connection is healthy
|
|
185
|
+
*/
|
|
186
|
+
ConnectionHealthState["Ok"] = "ok";
|
|
187
|
+
/**
|
|
188
|
+
* The client is either trying to establish a connection but failing, or
|
|
189
|
+
* the client has been explicitly {@link @monterosa/sdk-connect-kit#disconnect() | disconnected}
|
|
190
|
+
*/
|
|
191
|
+
ConnectionHealthState["Error"] = "error";
|
|
192
|
+
})(exports.ConnectionHealthState || (exports.ConnectionHealthState = {}));
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @license
|
|
196
|
+
* @monterosa/sdk-interact-kit
|
|
197
|
+
*
|
|
198
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
199
|
+
*
|
|
200
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
201
|
+
*/
|
|
202
|
+
class ConnectionHealthImpl extends sdkUtil.Emitter {
|
|
203
|
+
constructor(connect) {
|
|
204
|
+
super();
|
|
205
|
+
this.connect = connect;
|
|
206
|
+
this._state = exports.ConnectionHealthState.Ok;
|
|
207
|
+
this.connect.on('state', this.handleState.bind(this));
|
|
208
|
+
}
|
|
209
|
+
get state() {
|
|
210
|
+
return this._state;
|
|
211
|
+
}
|
|
212
|
+
set state(state) {
|
|
213
|
+
this._state = state;
|
|
214
|
+
this.emit('state', state);
|
|
215
|
+
}
|
|
216
|
+
handleState() {
|
|
217
|
+
switch (this.connect.state) {
|
|
218
|
+
default:
|
|
219
|
+
case exports.State.Ok:
|
|
220
|
+
this.state = exports.ConnectionHealthState.Ok;
|
|
221
|
+
break;
|
|
222
|
+
case exports.State.Error:
|
|
223
|
+
this.state = exports.ConnectionHealthState.Error;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @license
|
|
231
|
+
* @monterosa/sdk-interact-kit
|
|
232
|
+
*
|
|
233
|
+
* Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
|
|
234
|
+
*
|
|
235
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
236
|
+
*/
|
|
237
|
+
const getConnectionHealthMemoized = sdkUtil.memoizePromise(async (sdk) => {
|
|
238
|
+
const connect = await getConnect(sdk.options.host);
|
|
239
|
+
const connectionHealth = new ConnectionHealthImpl(connect);
|
|
240
|
+
return connectionHealth;
|
|
241
|
+
}, (sdk) => sdk.options.host);
|
|
242
|
+
/**
|
|
243
|
+
* Returns {@link ConnectionHealth | connection health} instance
|
|
244
|
+
*
|
|
245
|
+
* @param sdk - The SDK instance to monitor
|
|
246
|
+
* @returns The connection health instance.
|
|
247
|
+
*/
|
|
248
|
+
function getConnectionHealth(sdk = sdkCore.getSdk()) {
|
|
249
|
+
return getConnectionHealthMemoized(sdk);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Adds an observer for when
|
|
253
|
+
* {@link ConnectionHealth | connection health state} changed
|
|
254
|
+
*
|
|
255
|
+
* @param connectionHealth - The instance to observe
|
|
256
|
+
* @param callback - Called with the new state
|
|
257
|
+
*/
|
|
258
|
+
function onConnectionHealthState(connectionHealth, callback) {
|
|
259
|
+
return sdkUtil.subscribe(connectionHealth, 'state', callback);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @license
|
|
264
|
+
* @monterosa/sdk-interact-kit
|
|
265
|
+
*
|
|
266
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
267
|
+
*
|
|
268
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
269
|
+
*/
|
|
270
|
+
const DEFAULT_IGNORE_TIME = 30;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @license
|
|
274
|
+
* @monterosa/sdk-interact-kit
|
|
275
|
+
*
|
|
276
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
277
|
+
*
|
|
278
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
279
|
+
*/
|
|
280
|
+
/**
|
|
281
|
+
* Converts tag for identifying languages (rfc5646) into ISO language code
|
|
282
|
+
*/
|
|
283
|
+
function tagToLanguage(tag) {
|
|
284
|
+
// The tag is split not only by `-` as its stated in the rfc
|
|
285
|
+
// but also by `_` as it is returned by Studio for some locales
|
|
286
|
+
const chunks = tag.split(/[-_]/);
|
|
287
|
+
return chunks[0];
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Returns either:
|
|
291
|
+
* * a locale from the list of provided locales that correspond OS locale preferences
|
|
292
|
+
* * or default locale if OS locale doesn't correspond to any from the provided list
|
|
293
|
+
*
|
|
294
|
+
* * @internal
|
|
295
|
+
*/
|
|
296
|
+
function getLocale(locales) {
|
|
297
|
+
// The list of user's preferred language tags,
|
|
298
|
+
const languageTags = [
|
|
299
|
+
// where the first item is the user's default language
|
|
300
|
+
navigator.language,
|
|
301
|
+
// and others are the user's preferred languages
|
|
302
|
+
...navigator.languages,
|
|
303
|
+
];
|
|
304
|
+
// firstly, trying to check if one of languages tag
|
|
305
|
+
// has a full match with passed locales
|
|
306
|
+
for (const tag of languageTags) {
|
|
307
|
+
if (locales.includes(tag)) {
|
|
308
|
+
// if language tag has a match return it right away
|
|
309
|
+
return tag;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// secondly, checking if user's language (ISO string)
|
|
313
|
+
// has occurrences in the list of locales
|
|
314
|
+
for (const tag of languageTags) {
|
|
315
|
+
const userLang = tagToLanguage(tag);
|
|
316
|
+
for (const locale of locales) {
|
|
317
|
+
const studioLang = tagToLanguage(locale);
|
|
318
|
+
if (studioLang === userLang) {
|
|
319
|
+
// return the first locale that has the same
|
|
320
|
+
// language ISO string as the user language tag
|
|
321
|
+
return locale;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// if none locales matches users preferred languages tags
|
|
326
|
+
// return default locale
|
|
327
|
+
return locales[0];
|
|
328
|
+
}
|
|
329
|
+
function localise(value, locale) {
|
|
330
|
+
if (Object.prototype.hasOwnProperty.call(value, locale)) {
|
|
331
|
+
return value[locale];
|
|
332
|
+
}
|
|
333
|
+
return value.all;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* @license
|
|
338
|
+
* @monterosa/sdk-interact-kit
|
|
339
|
+
*
|
|
340
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
341
|
+
*
|
|
342
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
343
|
+
*/
|
|
344
|
+
/**
|
|
345
|
+
* @internal
|
|
346
|
+
*/
|
|
347
|
+
class ProjectImpl extends sdkUtil.Emitter {
|
|
348
|
+
constructor(options, context) {
|
|
349
|
+
super();
|
|
350
|
+
this.eventsChecksum = null;
|
|
351
|
+
this.id = options.id;
|
|
352
|
+
this.host = options.host;
|
|
353
|
+
this._context = context;
|
|
354
|
+
}
|
|
355
|
+
get context() {
|
|
356
|
+
return this._context;
|
|
357
|
+
}
|
|
358
|
+
get embedUrl() {
|
|
359
|
+
var _a;
|
|
360
|
+
return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.embed;
|
|
361
|
+
}
|
|
362
|
+
get uuid() {
|
|
363
|
+
var _a;
|
|
364
|
+
return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.uuid;
|
|
365
|
+
}
|
|
366
|
+
get events() {
|
|
367
|
+
var _a;
|
|
368
|
+
return ((_a = this._listings) === null || _a === void 0 ? void 0 : _a.events) || [];
|
|
369
|
+
}
|
|
370
|
+
get listings() {
|
|
371
|
+
return this._listings;
|
|
372
|
+
}
|
|
373
|
+
set listings(newListings) {
|
|
374
|
+
const createdEvents = this._calculateCreatedEvents(newListings);
|
|
375
|
+
const updatedEvents = this._calculateUpdatedEvents(newListings);
|
|
376
|
+
const deletedEvents = this._calculateDeletedEvents(newListings);
|
|
377
|
+
this._listings = Object.assign({}, newListings);
|
|
378
|
+
this._updateLocales(newListings);
|
|
379
|
+
this._updateEventsChecksum(newListings);
|
|
380
|
+
this._emitListingsEvents(createdEvents, updatedEvents, deletedEvents);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Calculates events that were created (present in new listings but not in old)
|
|
384
|
+
*/
|
|
385
|
+
_calculateCreatedEvents(newListings) {
|
|
386
|
+
if (!this._listings) {
|
|
387
|
+
return [];
|
|
388
|
+
}
|
|
389
|
+
return newListings.events.filter(({ id }) => !this._listings.events.some((event) => id === event.id));
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Calculates events that were updated (present in old listings and new,
|
|
393
|
+
* but with different digest)
|
|
394
|
+
*/
|
|
395
|
+
_calculateUpdatedEvents(newListings) {
|
|
396
|
+
if (!this._listings) {
|
|
397
|
+
return [];
|
|
398
|
+
}
|
|
399
|
+
return newListings.events.filter((event) => {
|
|
400
|
+
// find event data in new listings
|
|
401
|
+
const existingEvent = this._listings.events.find(({ id }) => event.id === id);
|
|
402
|
+
// return true if event data is found and digest is different
|
|
403
|
+
return existingEvent && event.digest !== existingEvent.digest;
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Calculates events that were deleted (present in old listings but not in new)
|
|
408
|
+
*/
|
|
409
|
+
_calculateDeletedEvents(newListings) {
|
|
410
|
+
if (!this._listings) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
return this._listings.events.filter(({ id }) => !newListings.events.some((event) => id === event.id));
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Processes and updates locales from project listings
|
|
417
|
+
*/
|
|
418
|
+
_updateLocales(newListings) {
|
|
419
|
+
this._locales = newListings.project.locales
|
|
420
|
+
// clone the array first
|
|
421
|
+
.map((locale) => (Object.assign({}, locale)))
|
|
422
|
+
// sort an array so that the default locale is the first
|
|
423
|
+
.sort((locale) => (locale.default ? -1 : 0))
|
|
424
|
+
// return only keys of the locales
|
|
425
|
+
.map((locale) => locale.key);
|
|
426
|
+
this._locale = getLocale(this._locales);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Updates the events checksum based on new listings
|
|
430
|
+
*/
|
|
431
|
+
_updateEventsChecksum(newListings) {
|
|
432
|
+
this.eventsChecksum = sdkUtil.checksum(newListings.events.map((event) => event.digest).join(''));
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Emits events based on created and deleted events
|
|
436
|
+
*/
|
|
437
|
+
_emitListingsEvents(createdEvents, updatedEvents, deletedEvents) {
|
|
438
|
+
const hasCreatedEvents = createdEvents.length > 0;
|
|
439
|
+
const hasDeletedEvents = deletedEvents.length > 0;
|
|
440
|
+
const hasUpdatedEvents = updatedEvents.length > 0;
|
|
441
|
+
const hasChanges = hasCreatedEvents || hasDeletedEvents || hasUpdatedEvents;
|
|
442
|
+
if (hasCreatedEvents) {
|
|
443
|
+
this.emit('listings_events_created', createdEvents);
|
|
444
|
+
}
|
|
445
|
+
if (hasDeletedEvents) {
|
|
446
|
+
this.emit('listings_events_deleted', deletedEvents);
|
|
447
|
+
}
|
|
448
|
+
if (hasUpdatedEvents) {
|
|
449
|
+
this.emit('listings_events_updated', updatedEvents);
|
|
450
|
+
}
|
|
451
|
+
if (hasChanges) {
|
|
452
|
+
this.emit('listings');
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
get isLocalisationSupported() {
|
|
456
|
+
var _a;
|
|
457
|
+
return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.is_localisation_supported;
|
|
458
|
+
}
|
|
459
|
+
get locale() {
|
|
460
|
+
return this._locale;
|
|
461
|
+
}
|
|
462
|
+
set locale(locale) {
|
|
463
|
+
if (!this.locales.includes(locale)) {
|
|
464
|
+
throw new Error(`Unknown locale name "${locale}"`);
|
|
465
|
+
}
|
|
466
|
+
this._locale = locale;
|
|
467
|
+
}
|
|
468
|
+
get locales() {
|
|
469
|
+
return this._locales;
|
|
470
|
+
}
|
|
471
|
+
get fields() {
|
|
472
|
+
if (this._fields !== undefined) {
|
|
473
|
+
return localise(this._fields, this.locale);
|
|
474
|
+
}
|
|
475
|
+
return {};
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Sets custom fields
|
|
479
|
+
*
|
|
480
|
+
* It accepts both Localised<Record<string, unknown>> and Record<string, unknown> to
|
|
481
|
+
* avoid mistyping between setter and getter
|
|
482
|
+
*
|
|
483
|
+
* @internal
|
|
484
|
+
*/
|
|
485
|
+
set fields(value) {
|
|
486
|
+
const isUpdate = typeof this._fields !== 'undefined';
|
|
487
|
+
this._fields = Object.assign({}, value);
|
|
488
|
+
if (isUpdate) {
|
|
489
|
+
this.emit('updated');
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
get extensions() {
|
|
493
|
+
var _a;
|
|
494
|
+
return (_a = this._listings) === null || _a === void 0 ? void 0 : _a.assets;
|
|
495
|
+
}
|
|
496
|
+
get historyIgnore() {
|
|
497
|
+
var _a;
|
|
498
|
+
const value = (_a = this._listings) === null || _a === void 0 ? void 0 : _a.project.history_ignore;
|
|
499
|
+
if (value && value > 0) {
|
|
500
|
+
return value;
|
|
501
|
+
}
|
|
502
|
+
return DEFAULT_IGNORE_TIME;
|
|
503
|
+
}
|
|
504
|
+
// TODO: must be implemented
|
|
505
|
+
// eslint-disable-next-line
|
|
506
|
+
delete() { }
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @license
|
|
511
|
+
* @monterosa/sdk-interact-kit
|
|
512
|
+
*
|
|
513
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
514
|
+
*
|
|
515
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
516
|
+
*/
|
|
517
|
+
const projects = new Map();
|
|
518
|
+
const connects = new Map();
|
|
519
|
+
async function buildProject(options, context) {
|
|
520
|
+
const { id, host } = options;
|
|
521
|
+
if (projects.has(id)) {
|
|
522
|
+
return projects.get(id);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Create "bare" instance of the project. Until listings and settings are
|
|
526
|
+
* loaded it can't be used
|
|
527
|
+
*/
|
|
528
|
+
const project = new ProjectImpl(options, context);
|
|
529
|
+
/**
|
|
530
|
+
* Add it to the cache of projects, so that we could find it to populate it
|
|
531
|
+
* with the data when /listings/ message is coming from the EnMasse.
|
|
532
|
+
*/
|
|
533
|
+
projects.set(id, project);
|
|
534
|
+
/**
|
|
535
|
+
* Waiting when the listings and the project settings come and we would be
|
|
536
|
+
* able to populate the project instance
|
|
537
|
+
*/
|
|
538
|
+
try {
|
|
539
|
+
const setup = await setupConnect(host);
|
|
540
|
+
await setup.subscribeProject(id);
|
|
541
|
+
/**
|
|
542
|
+
* By the time goLive successfully resolved listings must be present
|
|
543
|
+
* in project. If they are not, then there is no /listings/ in the channel's cache.
|
|
544
|
+
* This might be due to either studio is malfunctioning or project id is incorrect.
|
|
545
|
+
*/
|
|
546
|
+
if (!project.listings) {
|
|
547
|
+
throw new Error(`Unable to fetch project ${id} listings. Possibly wrong project id.`);
|
|
548
|
+
}
|
|
549
|
+
project.fields = await sdkInteractInterop.fetchSettings(project.host, project.id);
|
|
550
|
+
return project;
|
|
551
|
+
}
|
|
552
|
+
catch (err) {
|
|
553
|
+
/**
|
|
554
|
+
* If anything goes wrong we need to unsubscribe from the EnMasse project's
|
|
555
|
+
* channel by switching it to the offline mode.
|
|
556
|
+
*/
|
|
557
|
+
if (connects.has(id)) {
|
|
558
|
+
const connect = connects.get(id);
|
|
559
|
+
connect.unsubscribeProject(id);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* And project should be removed from the list of cached projects so that
|
|
563
|
+
* it won't be automatically returned upon consequent buildProject call
|
|
564
|
+
*/
|
|
565
|
+
projects.delete(id);
|
|
566
|
+
throw err;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async function setupConnect(host) {
|
|
570
|
+
if (connects.has(host)) {
|
|
571
|
+
return connects.get(host);
|
|
572
|
+
}
|
|
573
|
+
const connect = await getConnect(host);
|
|
574
|
+
connect.on('message', handleConnectMessage$1);
|
|
575
|
+
return connect;
|
|
576
|
+
}
|
|
577
|
+
async function handleConnectMessage$1(message) {
|
|
578
|
+
const { id, klass, data } = message;
|
|
579
|
+
const project = projects.get(id);
|
|
580
|
+
if (!project) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
switch (klass) {
|
|
584
|
+
case exports.Klass.Listings:
|
|
585
|
+
await handleListings(project, data[0]);
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function handleListings(project, data) {
|
|
590
|
+
var _a;
|
|
591
|
+
const listings = JSON.parse(data);
|
|
592
|
+
const newDigest = listings.project.app_settings_digest;
|
|
593
|
+
const oldDigest = (_a = project.listings) === null || _a === void 0 ? void 0 : _a.project.app_settings_digest;
|
|
594
|
+
project.listings = listings;
|
|
595
|
+
try {
|
|
596
|
+
if (oldDigest !== undefined && newDigest !== oldDigest) {
|
|
597
|
+
project.fields = await sdkInteractInterop.fetchSettings(project.host, project.id);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
catch (_b) {
|
|
601
|
+
console.warn(`Unable to fetch project ${project.id} settings`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* @license
|
|
607
|
+
* @monterosa/sdk-interact-kit
|
|
608
|
+
*
|
|
609
|
+
* Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
|
|
610
|
+
*
|
|
611
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
612
|
+
*/
|
|
613
|
+
/**
|
|
614
|
+
* @internal
|
|
615
|
+
*/
|
|
616
|
+
const getProjectMemoized = sdkUtil.memoizePromise(async (sdk) => {
|
|
617
|
+
const project = await buildProject({
|
|
618
|
+
id: sdk.options.projectId,
|
|
619
|
+
host: sdk.options.host,
|
|
620
|
+
}, {
|
|
621
|
+
sdk,
|
|
622
|
+
});
|
|
623
|
+
return project;
|
|
624
|
+
}, (sdk) => sdk.options.projectId);
|
|
625
|
+
/**
|
|
626
|
+
* Returns {@link InteractProject | Project instance} associated
|
|
627
|
+
* with the
|
|
628
|
+
* {@link @monterosa/sdk-core#MonterosaSdk | configured SDK}
|
|
629
|
+
*
|
|
630
|
+
* @param sdk - The SDK instance. Defaults to the default SDK.
|
|
631
|
+
* @returns The Project instance.
|
|
632
|
+
*/
|
|
633
|
+
function getProject(sdk = sdkCore.getSdk()) {
|
|
634
|
+
return getProjectMemoized(sdk);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Adds an observer for when
|
|
638
|
+
* {@link InteractProject | Project fields} are updated
|
|
639
|
+
*
|
|
640
|
+
* @param project - The Project to observe
|
|
641
|
+
* @param callback - Called when Project fields change
|
|
642
|
+
*/
|
|
643
|
+
function onProjectFieldsUpdated(project, callback) {
|
|
644
|
+
return sdkUtil.subscribe(project, 'updated', callback);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/******************************************************************************
|
|
648
|
+
Copyright (c) Microsoft Corporation.
|
|
649
|
+
|
|
650
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
651
|
+
purpose with or without fee is hereby granted.
|
|
652
|
+
|
|
653
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
654
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
655
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
656
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
657
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
658
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
659
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
660
|
+
***************************************************************************** */
|
|
661
|
+
|
|
662
|
+
function __values(o) {
|
|
663
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
664
|
+
if (m) return m.call(o);
|
|
665
|
+
if (o && typeof o.length === "number") return {
|
|
666
|
+
next: function () {
|
|
667
|
+
if (o && i >= o.length) o = void 0;
|
|
668
|
+
return { value: o && o[i++], done: !o };
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function __asyncValues(o) {
|
|
675
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
676
|
+
var m = o[Symbol.asyncIterator], i;
|
|
677
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
678
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
679
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
683
|
+
var e = new Error(message);
|
|
684
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* @license
|
|
689
|
+
* @monterosa/sdk-interact-kit
|
|
690
|
+
*
|
|
691
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
692
|
+
*
|
|
693
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
694
|
+
*/
|
|
695
|
+
async function fetchHistory(host, id) {
|
|
696
|
+
const response = await fetch(`https://${host}/events/${id.substring(0, 2)}/${id}/history.json`);
|
|
697
|
+
if (!response.ok) {
|
|
698
|
+
throw new Error(`Failed to fetch event ${id} history`);
|
|
699
|
+
}
|
|
700
|
+
const data = await response.json();
|
|
701
|
+
return data;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* @license
|
|
706
|
+
* @monterosa/sdk-interact-kit
|
|
707
|
+
*
|
|
708
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
709
|
+
*
|
|
710
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
711
|
+
*/
|
|
712
|
+
/**
|
|
713
|
+
* Describes the Event state.
|
|
714
|
+
*/
|
|
715
|
+
exports.EventState = void 0;
|
|
716
|
+
(function (EventState) {
|
|
717
|
+
/**
|
|
718
|
+
* The Event is in the `upcoming` state when its {@link InteractEvent | startAt}
|
|
719
|
+
* less than the {@link @monterosa/sdk-util#now | current time}.
|
|
720
|
+
*/
|
|
721
|
+
EventState["Upcoming"] = "upcoming";
|
|
722
|
+
/**
|
|
723
|
+
* The Event is in the `active` state when its {@link InteractEvent | startAt}
|
|
724
|
+
* equal or more than the {@link @monterosa/sdk-util#now | current time} and less than the
|
|
725
|
+
* {@link InteractEvent | endAt}.
|
|
726
|
+
*/
|
|
727
|
+
EventState["Active"] = "active";
|
|
728
|
+
/**
|
|
729
|
+
* @internal
|
|
730
|
+
*/
|
|
731
|
+
EventState["Prolonged"] = "prolonged";
|
|
732
|
+
/**
|
|
733
|
+
* The Event is in the `finished` state when its {@link InteractEvent | endAt}
|
|
734
|
+
* equal or more than the {@link @monterosa/sdk-util#now | current time}.
|
|
735
|
+
*/
|
|
736
|
+
EventState["Finished"] = "finished";
|
|
737
|
+
})(exports.EventState || (exports.EventState = {}));
|
|
738
|
+
var InitState;
|
|
739
|
+
(function (InitState) {
|
|
740
|
+
InitState["Uninitialised"] = "uninitialised";
|
|
741
|
+
InitState["Initialising"] = "initialising";
|
|
742
|
+
InitState["Initialised"] = "initialised";
|
|
743
|
+
})(InitState || (InitState = {}));
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* @license
|
|
747
|
+
* @monterosa/sdk-interact-kit
|
|
748
|
+
*
|
|
749
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
750
|
+
*
|
|
751
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
752
|
+
*/
|
|
753
|
+
/**
|
|
754
|
+
* @internal
|
|
755
|
+
*/
|
|
756
|
+
class EventImpl extends sdkUtil.Emitter {
|
|
757
|
+
constructor(data, context) {
|
|
758
|
+
super();
|
|
759
|
+
this._initState = InitState.Uninitialised;
|
|
760
|
+
this._history = null;
|
|
761
|
+
this._data = data;
|
|
762
|
+
this._context = context;
|
|
763
|
+
this._state = this.calculateState();
|
|
764
|
+
this._internalState = this.calculateInternalState();
|
|
765
|
+
this.boundHandleState = this.handleState.bind(this);
|
|
766
|
+
this.boundHandleInternalState = this.handleInternalState.bind(this);
|
|
767
|
+
this.boundHandleEventsUpdated = this.handleEventsUpdated.bind(this);
|
|
768
|
+
this.unsubscribeStateHandler = sdkUtil.onTick(this.boundHandleState);
|
|
769
|
+
this.unsubscribeInternalStateHandler = sdkUtil.onTick(this.boundHandleInternalState);
|
|
770
|
+
this.unsubscribeListingsHandler = sdkUtil.subscribe(this.context.project, 'listings_events_updated', this.boundHandleEventsUpdated);
|
|
771
|
+
}
|
|
772
|
+
calculateState() {
|
|
773
|
+
if (!this._data.started || sdkUtil.now() < this.startAt) {
|
|
774
|
+
return exports.EventState.Upcoming;
|
|
775
|
+
}
|
|
776
|
+
if (sdkUtil.now() < this.endAt) {
|
|
777
|
+
return exports.EventState.Active;
|
|
778
|
+
}
|
|
779
|
+
return exports.EventState.Finished;
|
|
780
|
+
}
|
|
781
|
+
calculateInternalState() {
|
|
782
|
+
if (!this._data.started || sdkUtil.now() < this.startAt) {
|
|
783
|
+
return exports.EventState.Upcoming;
|
|
784
|
+
}
|
|
785
|
+
if (sdkUtil.now() < this.endAt) {
|
|
786
|
+
return exports.EventState.Active;
|
|
787
|
+
}
|
|
788
|
+
if (sdkUtil.now() < this.endAt + this._data.extra_time) {
|
|
789
|
+
return exports.EventState.Prolonged;
|
|
790
|
+
}
|
|
791
|
+
return exports.EventState.Finished;
|
|
792
|
+
}
|
|
793
|
+
handleState() {
|
|
794
|
+
const state = this.calculateState();
|
|
795
|
+
if (state === exports.EventState.Finished) {
|
|
796
|
+
this.unsubscribeStateHandler();
|
|
797
|
+
}
|
|
798
|
+
if (state !== this._state) {
|
|
799
|
+
this._state = state;
|
|
800
|
+
this.emit('state', state);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
handleInternalState() {
|
|
804
|
+
const internalState = this.calculateInternalState();
|
|
805
|
+
if (internalState === exports.EventState.Finished) {
|
|
806
|
+
this.unsubscribeInternalStateHandler();
|
|
807
|
+
}
|
|
808
|
+
if (internalState !== this._internalState) {
|
|
809
|
+
this._internalState = internalState;
|
|
810
|
+
this.emit('internal_state', internalState);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
handleEventsUpdated(events) {
|
|
814
|
+
const data = events.find(({ id }) => id === this.id);
|
|
815
|
+
if (data && this._data.digest !== data.digest) {
|
|
816
|
+
this._data = data;
|
|
817
|
+
// Forcing state recalculation to ensure the event is in the correct state
|
|
818
|
+
this.handleState();
|
|
819
|
+
this.handleInternalState();
|
|
820
|
+
this.emit('updated');
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
/** @internal */
|
|
824
|
+
destroy() {
|
|
825
|
+
this.unsubscribeStateHandler();
|
|
826
|
+
this.unsubscribeInternalStateHandler();
|
|
827
|
+
this.unsubscribeListingsHandler();
|
|
828
|
+
}
|
|
829
|
+
get context() {
|
|
830
|
+
return this._context;
|
|
831
|
+
}
|
|
832
|
+
get id() {
|
|
833
|
+
return this._data.id;
|
|
834
|
+
}
|
|
835
|
+
get name() {
|
|
836
|
+
return this._data.name;
|
|
837
|
+
}
|
|
838
|
+
get startAt() {
|
|
839
|
+
return this._data.start_at;
|
|
840
|
+
}
|
|
841
|
+
get startAtIso() {
|
|
842
|
+
return this._data.start_at_iso;
|
|
843
|
+
}
|
|
844
|
+
get endAt() {
|
|
845
|
+
return this._data.end_at;
|
|
846
|
+
}
|
|
847
|
+
get endAtIso() {
|
|
848
|
+
return this._data.end_at_iso;
|
|
849
|
+
}
|
|
850
|
+
get duration() {
|
|
851
|
+
return this._data.duration;
|
|
852
|
+
}
|
|
853
|
+
get fields() {
|
|
854
|
+
const { locale } = this.context.project;
|
|
855
|
+
return localise(this._data.custom_fields, locale);
|
|
856
|
+
}
|
|
857
|
+
get state() {
|
|
858
|
+
return this._state;
|
|
859
|
+
}
|
|
860
|
+
get internalState() {
|
|
861
|
+
return this._internalState;
|
|
862
|
+
}
|
|
863
|
+
get initState() {
|
|
864
|
+
return this._initState;
|
|
865
|
+
}
|
|
866
|
+
set initState(state) {
|
|
867
|
+
this._initState = state;
|
|
868
|
+
}
|
|
869
|
+
get history() {
|
|
870
|
+
return this._history;
|
|
871
|
+
}
|
|
872
|
+
set history(value) {
|
|
873
|
+
this._history = value;
|
|
874
|
+
this.emit('history', value);
|
|
875
|
+
}
|
|
876
|
+
get canSubscribe() {
|
|
877
|
+
return (this._internalState === exports.EventState.Active ||
|
|
878
|
+
this._internalState === exports.EventState.Prolonged);
|
|
879
|
+
}
|
|
880
|
+
get canLoadHistory() {
|
|
881
|
+
return sdkUtil.now() >= this.startAt + this.context.project.historyIgnore;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* @license
|
|
887
|
+
* @monterosa/sdk-interact-kit
|
|
888
|
+
*
|
|
889
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
890
|
+
*
|
|
891
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
892
|
+
*/
|
|
893
|
+
const events = new Map();
|
|
894
|
+
const healthListeners = new Map();
|
|
895
|
+
function buildEvent(options, context) {
|
|
896
|
+
if (events.has(options.id)) {
|
|
897
|
+
return events.get(options.id);
|
|
898
|
+
}
|
|
899
|
+
const event = new EventImpl(options, context);
|
|
900
|
+
event.on('internal_state', handleEventInternalState.bind(event));
|
|
901
|
+
events.set(options.id, event);
|
|
902
|
+
return event;
|
|
903
|
+
}
|
|
904
|
+
async function init(event) {
|
|
905
|
+
if (event.initState === InitState.Initialising) {
|
|
906
|
+
return Promise.reject(new Error("can't initialise event, event is already being initialised"));
|
|
907
|
+
}
|
|
908
|
+
if (event.initState === InitState.Initialised) {
|
|
909
|
+
return Promise.reject(new Error("can't initialise event, event is already initialised"));
|
|
910
|
+
}
|
|
911
|
+
event.initState = InitState.Initialising;
|
|
912
|
+
if (event.canLoadHistory && event.history === null) {
|
|
913
|
+
const history = await fetchHistory(event.context.project.host, event.id);
|
|
914
|
+
event.history = history;
|
|
915
|
+
}
|
|
916
|
+
if (event.canSubscribe) {
|
|
917
|
+
await subscribe(event);
|
|
918
|
+
}
|
|
919
|
+
await listenConnHealth(event);
|
|
920
|
+
event.initState = InitState.Initialised;
|
|
921
|
+
return Promise.resolve();
|
|
922
|
+
}
|
|
923
|
+
async function subscribe(event) {
|
|
924
|
+
if (!event.canSubscribe) {
|
|
925
|
+
return Promise.reject(new Error("Event can't be subscribed"));
|
|
926
|
+
}
|
|
927
|
+
const connect = await getConnect(event.context.project.host);
|
|
928
|
+
await connect.subscribeEvent(event.id);
|
|
929
|
+
return Promise.resolve();
|
|
930
|
+
}
|
|
931
|
+
async function unsubscribe(event) {
|
|
932
|
+
const connect = await getConnect(event.context.project.host);
|
|
933
|
+
await connect.unsubscribeEvent(event.id);
|
|
934
|
+
return Promise.resolve();
|
|
935
|
+
}
|
|
936
|
+
async function listenConnHealth(event) {
|
|
937
|
+
const { project } = event.context;
|
|
938
|
+
if (healthListeners.has(project.id)) {
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
const connect = await getConnect(project.host);
|
|
942
|
+
if (connect.enmasse !== null) {
|
|
943
|
+
const unsub = sdkConnectKit.onConnected(connect.enmasse, handleEnmasseConnected);
|
|
944
|
+
healthListeners.set(project.id, unsub);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
async function handleEventInternalState(state) {
|
|
948
|
+
if (state === exports.EventState.Finished) {
|
|
949
|
+
await unsubscribe(this);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
async function handleEnmasseConnected() {
|
|
953
|
+
var _a, e_1, _b, _c;
|
|
954
|
+
try {
|
|
955
|
+
for (var _d = true, _e = __asyncValues(Array.from(events.values())), _f; _f = await _e.next(), _a = _f.done, !_a;) {
|
|
956
|
+
_c = _f.value;
|
|
957
|
+
_d = false;
|
|
958
|
+
try {
|
|
959
|
+
const event = _c;
|
|
960
|
+
if (event.initState === InitState.Initialised) {
|
|
961
|
+
const history = await fetchHistory(event.context.project.host, event.id);
|
|
962
|
+
event.history = history;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
finally {
|
|
966
|
+
_d = true;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
971
|
+
finally {
|
|
972
|
+
try {
|
|
973
|
+
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
|
|
974
|
+
}
|
|
975
|
+
finally { if (e_1) throw e_1.error; }
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* @internal
|
|
981
|
+
*/
|
|
982
|
+
const getEventsMemoized = sdkUtil.memoizePromise(async (project) => {
|
|
983
|
+
if (project === undefined) {
|
|
984
|
+
project = await getProject();
|
|
985
|
+
}
|
|
986
|
+
const context = Object.assign(Object.assign({}, project.context), { project });
|
|
987
|
+
return (project.events
|
|
988
|
+
// Calling builder function that creates an Event instance.
|
|
989
|
+
// Existing Event will be returned if it already exists in the cache
|
|
990
|
+
.map((data) => buildEvent(data, context))
|
|
991
|
+
// Sort Events so the oldest Event is first, and the most recent is last
|
|
992
|
+
.sort((a, b) => a.startAt - b.startAt));
|
|
993
|
+
},
|
|
994
|
+
/**
|
|
995
|
+
* Resolver function that determines the cache key for memoization.
|
|
996
|
+
*
|
|
997
|
+
* The cache key is used to determine whether a cached result can be reused.
|
|
998
|
+
* It prioritizes `eventsChecksum` over `id` to ensure cache invalidation when
|
|
999
|
+
* the events list changes, even if the project ID remains the same.
|
|
1000
|
+
*
|
|
1001
|
+
* @param project - Optional project instance. If not provided, attempts to
|
|
1002
|
+
* resolve using the default project from the SDK.
|
|
1003
|
+
* @returns A cache key string derived from the project's checksum/id or SDK projectId
|
|
1004
|
+
*/
|
|
1005
|
+
(project) => {
|
|
1006
|
+
var _a, _b;
|
|
1007
|
+
// If project is provided, use its checksum (if available) or id as cache key
|
|
1008
|
+
if (project !== undefined) {
|
|
1009
|
+
return (_a = project.eventsChecksum) !== null && _a !== void 0 ? _a : project.id;
|
|
1010
|
+
}
|
|
1011
|
+
// If no project provided, try to get default project from SDK
|
|
1012
|
+
const sdk = sdkCore.getSdk();
|
|
1013
|
+
const defaultProject = projects.get(sdk.options.projectId);
|
|
1014
|
+
// Use default project's checksum/id if available
|
|
1015
|
+
if (defaultProject) {
|
|
1016
|
+
return (_b = defaultProject.eventsChecksum) !== null && _b !== void 0 ? _b : defaultProject.id;
|
|
1017
|
+
}
|
|
1018
|
+
// Fallback to SDK's projectId option
|
|
1019
|
+
return sdk.options.projectId;
|
|
1020
|
+
});
|
|
1021
|
+
/**
|
|
1022
|
+
* Returns all Events in a Project, including all active Events and
|
|
1023
|
+
* past/upcoming Events, according to Listings settings in Project Setup
|
|
1024
|
+
* in Studio.
|
|
1025
|
+
*
|
|
1026
|
+
* @param project - A Project instance. If not provided,
|
|
1027
|
+
* the one from the default SDK will be fetched.
|
|
1028
|
+
* @returns A promise that resolves to an array of Events.
|
|
1029
|
+
*/
|
|
1030
|
+
function getEvents(project) {
|
|
1031
|
+
return getEventsMemoized(project);
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* @internal
|
|
1035
|
+
*/
|
|
1036
|
+
const getEventMemoized = sdkUtil.memoizePromise(async (id, project) => {
|
|
1037
|
+
if (events.has(id)) {
|
|
1038
|
+
return events.get(id);
|
|
1039
|
+
}
|
|
1040
|
+
if (project === undefined) {
|
|
1041
|
+
project = await getProject();
|
|
1042
|
+
}
|
|
1043
|
+
const context = Object.assign(Object.assign({}, project.context), { project });
|
|
1044
|
+
const data = project.events.find((eventData) => eventData.id === id);
|
|
1045
|
+
if (data !== undefined) {
|
|
1046
|
+
const event = buildEvent(data, context);
|
|
1047
|
+
if (event.canLoadHistory) {
|
|
1048
|
+
const history = await fetchHistory(project.host, id);
|
|
1049
|
+
event.history = history;
|
|
1050
|
+
}
|
|
1051
|
+
return event;
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
const history = await fetchHistory(project.host, id);
|
|
1055
|
+
const event = buildEvent(history.config, context);
|
|
1056
|
+
event.history = history;
|
|
1057
|
+
return event;
|
|
1058
|
+
}
|
|
1059
|
+
catch (err) {
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
}, (id) => id);
|
|
1063
|
+
/**
|
|
1064
|
+
* Returns an Event by its id
|
|
1065
|
+
*
|
|
1066
|
+
* @param id - Id of the Event
|
|
1067
|
+
* @param project - A Project instance. If not provided,
|
|
1068
|
+
* the one from the default SDK will be fetched.
|
|
1069
|
+
*
|
|
1070
|
+
* @returns The Event associated with the provided id,
|
|
1071
|
+
* or null if no such Event exists in the Project.
|
|
1072
|
+
*/
|
|
1073
|
+
function getEvent(id, project) {
|
|
1074
|
+
return getEventMemoized(id, project);
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Adds an observer for when
|
|
1078
|
+
* {@link InteractEvent | Event state} changed
|
|
1079
|
+
*
|
|
1080
|
+
* @param event - The Event to observe
|
|
1081
|
+
* @param callback - Called with the new state
|
|
1082
|
+
*/
|
|
1083
|
+
function onEventState(event, callback) {
|
|
1084
|
+
return sdkUtil.subscribe(event, 'state', callback);
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Adds an observer for when Event's data changed
|
|
1088
|
+
*
|
|
1089
|
+
* @param event - The Event to observe
|
|
1090
|
+
* @param callback - Called when the Event data changes
|
|
1091
|
+
*/
|
|
1092
|
+
function onEventUpdated(event, callback) {
|
|
1093
|
+
return sdkUtil.subscribe(event, 'updated', callback);
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Adds an observer that is called when an Event is added to listings.
|
|
1097
|
+
*
|
|
1098
|
+
* @remarks
|
|
1099
|
+
* The following actions will result in adding an Event to listings:
|
|
1100
|
+
*
|
|
1101
|
+
* - The start of the Event in Studio
|
|
1102
|
+
*
|
|
1103
|
+
* - The scheduling of a future Event in Studio, when listings are configured
|
|
1104
|
+
* to include future Events in Project Settings.
|
|
1105
|
+
*
|
|
1106
|
+
* - When a future Event starts, a new future Event may be added to the list,
|
|
1107
|
+
* depending on the Project configuration, to ensure a minimum number of
|
|
1108
|
+
* upcoming Events are always available.
|
|
1109
|
+
*
|
|
1110
|
+
* @param project - The Project to observe
|
|
1111
|
+
* @param callback - Called with the added Event
|
|
1112
|
+
*/
|
|
1113
|
+
function onEventAdded(project, callback) {
|
|
1114
|
+
return sdkUtil.subscribe(project, 'listings_events_created', async (data) => {
|
|
1115
|
+
var _a, e_1, _b, _c;
|
|
1116
|
+
try {
|
|
1117
|
+
for (var _d = true, data_1 = __asyncValues(data), data_1_1; data_1_1 = await data_1.next(), _a = data_1_1.done, !_a;) {
|
|
1118
|
+
_c = data_1_1.value;
|
|
1119
|
+
_d = false;
|
|
1120
|
+
try {
|
|
1121
|
+
const options = _c;
|
|
1122
|
+
const event = await getEvent(options.id, project);
|
|
1123
|
+
if (event !== null) {
|
|
1124
|
+
callback(event);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
finally {
|
|
1128
|
+
_d = true;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1133
|
+
finally {
|
|
1134
|
+
try {
|
|
1135
|
+
if (!_d && !_a && (_b = data_1.return)) await _b.call(data_1);
|
|
1136
|
+
}
|
|
1137
|
+
finally { if (e_1) throw e_1.error; }
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Adds an observer that is called when an Event is removed from listings.
|
|
1143
|
+
*
|
|
1144
|
+
* @remarks
|
|
1145
|
+
* The following actions will result in removing an Event from listings:
|
|
1146
|
+
*
|
|
1147
|
+
* - The deletion of the Event from Studio
|
|
1148
|
+
*
|
|
1149
|
+
* - The change of listings configuration in Project Settings to exclude future
|
|
1150
|
+
* or past Events.
|
|
1151
|
+
*
|
|
1152
|
+
* - The Event is removed after a period of time since its completion. This period
|
|
1153
|
+
* is calculated as 45 seconds plus the Maximum allowed delay (configured in
|
|
1154
|
+
* Project Settings in Studio).
|
|
1155
|
+
*
|
|
1156
|
+
* @param project - The Project to observe
|
|
1157
|
+
* @param callback - Called with the removed Event
|
|
1158
|
+
*/
|
|
1159
|
+
function onEventRemoved(project, callback) {
|
|
1160
|
+
return sdkUtil.subscribe(project, 'listings_events_deleted', async (data) => {
|
|
1161
|
+
var _a, e_2, _b, _c;
|
|
1162
|
+
try {
|
|
1163
|
+
for (var _d = true, data_2 = __asyncValues(data), data_2_1; data_2_1 = await data_2.next(), _a = data_2_1.done, !_a;) {
|
|
1164
|
+
_c = data_2_1.value;
|
|
1165
|
+
_d = false;
|
|
1166
|
+
try {
|
|
1167
|
+
const options = _c;
|
|
1168
|
+
const event = await getEvent(options.id, project);
|
|
1169
|
+
if (event !== null) {
|
|
1170
|
+
callback(event);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
finally {
|
|
1174
|
+
_d = true;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
1179
|
+
finally {
|
|
1180
|
+
try {
|
|
1181
|
+
if (!_d && !_a && (_b = data_2.return)) await _b.call(data_2);
|
|
1182
|
+
}
|
|
1183
|
+
finally { if (e_2) throw e_2.error; }
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* @license
|
|
1190
|
+
* @monterosa/sdk-interact-kit
|
|
1191
|
+
*
|
|
1192
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
1193
|
+
*
|
|
1194
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1195
|
+
*/
|
|
1196
|
+
/**
|
|
1197
|
+
* A map of potential error codes to compare with errors thrown by
|
|
1198
|
+
* vote() or validateAnswer() functions.
|
|
1199
|
+
*/
|
|
1200
|
+
exports.AnswerError = void 0;
|
|
1201
|
+
(function (AnswerError) {
|
|
1202
|
+
/** Selected option index is out of range. */
|
|
1203
|
+
AnswerError["OptionIndexOutOfRange"] = "out_of_range";
|
|
1204
|
+
/** Fewer options selected than the minimum required. */
|
|
1205
|
+
AnswerError["BelowMinVoteOptions"] = "below_min_vote_options";
|
|
1206
|
+
/** More options selected than the maximum allowed. */
|
|
1207
|
+
AnswerError["AboveMaxVoteOptions"] = "above_max_vote_options";
|
|
1208
|
+
/** User has exceeded the maximum votes allowed. */
|
|
1209
|
+
AnswerError["AboveMaxVotesPerUser"] = "above_max_per_user";
|
|
1210
|
+
/** A single option received more votes than allowed. */
|
|
1211
|
+
AnswerError["AboveMaxVotesPerOption"] = "above_max_per_option";
|
|
1212
|
+
/** Attempted to vote on a non-interactive element. */
|
|
1213
|
+
AnswerError["VotedOnNonInteractiveElement"] = "non_interactive_element";
|
|
1214
|
+
/** Attempted to vote on a closed element. */
|
|
1215
|
+
AnswerError["VotedOnClosedElement"] = "closed_element";
|
|
1216
|
+
/** No vote value was provided. */
|
|
1217
|
+
AnswerError["EmptyVote"] = "empty_vote";
|
|
1218
|
+
})(exports.AnswerError || (exports.AnswerError = {}));
|
|
1219
|
+
const AnswerErrorMessages = {
|
|
1220
|
+
[exports.AnswerError.OptionIndexOutOfRange]: (max, idx) => `Options index ${idx} is out of range [0..${max}]`,
|
|
1221
|
+
[exports.AnswerError.BelowMinVoteOptions]: (min, total) => `Below min (${min}) vote options: ${total}`,
|
|
1222
|
+
[exports.AnswerError.AboveMaxVoteOptions]: (max, total) => `Above max (${max}) vote options: ${total}`,
|
|
1223
|
+
[exports.AnswerError.AboveMaxVotesPerUser]: (max, total) => `Above max (${max}) votes per user: ${total}`,
|
|
1224
|
+
[exports.AnswerError.AboveMaxVotesPerOption]: (max, total) => `Above max (${max}) votes per option: ${total}`,
|
|
1225
|
+
[exports.AnswerError.VotedOnNonInteractiveElement]: (id) => `Non interact element ${id}`,
|
|
1226
|
+
[exports.AnswerError.VotedOnClosedElement]: (id) => `Element ${id} is closed`,
|
|
1227
|
+
[exports.AnswerError.EmptyVote]: (indices) => `Empty value for options indices: ${indices}`,
|
|
1228
|
+
};
|
|
1229
|
+
/**
|
|
1230
|
+
* The ElementState represents the potential states an Element is in.
|
|
1231
|
+
*/
|
|
1232
|
+
exports.ElementState = void 0;
|
|
1233
|
+
(function (ElementState) {
|
|
1234
|
+
/**
|
|
1235
|
+
* Element is opened and active. The user can vote if
|
|
1236
|
+
* the Element is `interactive`
|
|
1237
|
+
*/
|
|
1238
|
+
ElementState["Opened"] = "opened";
|
|
1239
|
+
/**
|
|
1240
|
+
* Element is closed and the user can no longer vote for
|
|
1241
|
+
* `interactive` Elements
|
|
1242
|
+
*/
|
|
1243
|
+
ElementState["Closed"] = "closed";
|
|
1244
|
+
})(exports.ElementState || (exports.ElementState = {}));
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* @license
|
|
1248
|
+
* @monterosa/sdk-interact-kit
|
|
1249
|
+
*
|
|
1250
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
1251
|
+
*
|
|
1252
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1253
|
+
*/
|
|
1254
|
+
/**
|
|
1255
|
+
* An `Answer` contains data of the user answer. Can be used to manage number of
|
|
1256
|
+
* votes that the user placed for a specific option.
|
|
1257
|
+
*/
|
|
1258
|
+
class Answer {
|
|
1259
|
+
constructor(answers = []) {
|
|
1260
|
+
this._answers = new Map();
|
|
1261
|
+
for (const { option, value } of answers) {
|
|
1262
|
+
this._answers.set(option, value);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* The array of user answers
|
|
1267
|
+
*/
|
|
1268
|
+
get answers() {
|
|
1269
|
+
return Array.from(this._answers.entries()).map(([option, value]) => ({
|
|
1270
|
+
option,
|
|
1271
|
+
value,
|
|
1272
|
+
}));
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Instantiate Answer class with the provided options indices. Number
|
|
1276
|
+
* of votes will be set to 1 for all options
|
|
1277
|
+
*
|
|
1278
|
+
* @example
|
|
1279
|
+
* ```javascript
|
|
1280
|
+
* // user votes for the first and fourth options
|
|
1281
|
+
* const answer = Answer.fromIndices([0, 3]);
|
|
1282
|
+
* ```
|
|
1283
|
+
*
|
|
1284
|
+
* @param indices - The array of option indices
|
|
1285
|
+
*/
|
|
1286
|
+
static fromIndices(indices) {
|
|
1287
|
+
return new Answer(indices.map((option) => ({
|
|
1288
|
+
option,
|
|
1289
|
+
value: 1,
|
|
1290
|
+
})));
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Adds a vote for the provided option index
|
|
1294
|
+
*
|
|
1295
|
+
* @param option - The index of the option, starting with 0
|
|
1296
|
+
* @param value - The number of votes that will be placed against the option
|
|
1297
|
+
*/
|
|
1298
|
+
set(option, value) {
|
|
1299
|
+
this._answers.set(option, value);
|
|
1300
|
+
return this;
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Removes the vote for the provided option index
|
|
1304
|
+
*
|
|
1305
|
+
* @param option - The index of the option, starting with 0
|
|
1306
|
+
*/
|
|
1307
|
+
remove(option) {
|
|
1308
|
+
this._answers.delete(option);
|
|
1309
|
+
return this;
|
|
1310
|
+
}
|
|
1311
|
+
/** @internal */
|
|
1312
|
+
toJSON() {
|
|
1313
|
+
return this.answers;
|
|
1314
|
+
}
|
|
1315
|
+
/** @internal */
|
|
1316
|
+
static fromJSON(value) {
|
|
1317
|
+
return new Answer(JSON.parse(value));
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* @license
|
|
1323
|
+
* @monterosa/sdk-interact-kit
|
|
1324
|
+
*
|
|
1325
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
1326
|
+
*
|
|
1327
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1328
|
+
*/
|
|
1329
|
+
/**
|
|
1330
|
+
* Describes Element types
|
|
1331
|
+
*/
|
|
1332
|
+
exports.ElementType = void 0;
|
|
1333
|
+
(function (ElementType) {
|
|
1334
|
+
/**
|
|
1335
|
+
* Data Element
|
|
1336
|
+
*/
|
|
1337
|
+
ElementType["Data"] = "data";
|
|
1338
|
+
/**
|
|
1339
|
+
* Poll Element
|
|
1340
|
+
*/
|
|
1341
|
+
ElementType["Poll"] = "poll";
|
|
1342
|
+
/**
|
|
1343
|
+
* Regular poll Element
|
|
1344
|
+
*/
|
|
1345
|
+
ElementType["RegularPoll"] = "rpoll";
|
|
1346
|
+
/**
|
|
1347
|
+
* Diametric poll Element
|
|
1348
|
+
*/
|
|
1349
|
+
ElementType["DiametricPoll"] = "dpoll";
|
|
1350
|
+
/**
|
|
1351
|
+
* Emoting poll Element
|
|
1352
|
+
*/
|
|
1353
|
+
ElementType["EmotingPoll"] = "emo";
|
|
1354
|
+
/**
|
|
1355
|
+
* Powerbar Element
|
|
1356
|
+
*/
|
|
1357
|
+
ElementType["Powerbar"] = "powerbar";
|
|
1358
|
+
/**
|
|
1359
|
+
* Prediction Element
|
|
1360
|
+
*/
|
|
1361
|
+
ElementType["Prediction"] = "prediction";
|
|
1362
|
+
/**
|
|
1363
|
+
* Trivia Element
|
|
1364
|
+
*/
|
|
1365
|
+
ElementType["Trivia"] = "trivia";
|
|
1366
|
+
})(exports.ElementType || (exports.ElementType = {}));
|
|
1367
|
+
var ShowResultsMode;
|
|
1368
|
+
(function (ShowResultsMode) {
|
|
1369
|
+
ShowResultsMode["OnVote"] = "vote";
|
|
1370
|
+
ShowResultsMode["OnClose"] = "close";
|
|
1371
|
+
ShowResultsMode["OnEventEnd"] = "event_end";
|
|
1372
|
+
ShowResultsMode["Manual"] = "manual";
|
|
1373
|
+
ShowResultsMode["Never"] = "never";
|
|
1374
|
+
ShowResultsMode["Always"] = "always";
|
|
1375
|
+
})(ShowResultsMode || (ShowResultsMode = {}));
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* @license
|
|
1379
|
+
* @monterosa/sdk-interact-kit
|
|
1380
|
+
*
|
|
1381
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
1382
|
+
*
|
|
1383
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1384
|
+
*/
|
|
1385
|
+
/**
|
|
1386
|
+
* @internal
|
|
1387
|
+
*/
|
|
1388
|
+
class ElementImpl extends sdkUtil.Emitter {
|
|
1389
|
+
constructor(data, context) {
|
|
1390
|
+
var _a, _b;
|
|
1391
|
+
super();
|
|
1392
|
+
this._results = null;
|
|
1393
|
+
this._userAnswer = null;
|
|
1394
|
+
this._correctOption = null;
|
|
1395
|
+
this._data = data;
|
|
1396
|
+
this._context = context;
|
|
1397
|
+
this._results = ((_a = data.data) === null || _a === void 0 ? void 0 : _a.results) || null;
|
|
1398
|
+
this._correctOption = ((_b = data.data) === null || _b === void 0 ? void 0 : _b.correct_option)
|
|
1399
|
+
? data.data.correct_option - 1
|
|
1400
|
+
: null;
|
|
1401
|
+
this._state = this.calculateState();
|
|
1402
|
+
this.unsubscribeStateHandler = sdkUtil.onTick(() => this.handleState());
|
|
1403
|
+
}
|
|
1404
|
+
calculateState() {
|
|
1405
|
+
if (sdkUtil.now() < this.publishedAt + this.duration) {
|
|
1406
|
+
return exports.ElementState.Opened;
|
|
1407
|
+
}
|
|
1408
|
+
return exports.ElementState.Closed;
|
|
1409
|
+
}
|
|
1410
|
+
handleState() {
|
|
1411
|
+
const state = this.calculateState();
|
|
1412
|
+
if (state === exports.ElementState.Closed) {
|
|
1413
|
+
this.unsubscribeStateHandler();
|
|
1414
|
+
}
|
|
1415
|
+
if (state !== this._state) {
|
|
1416
|
+
this._state = state;
|
|
1417
|
+
this.emit('state', state);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
update(data) {
|
|
1421
|
+
this._data = data;
|
|
1422
|
+
this.handleState();
|
|
1423
|
+
}
|
|
1424
|
+
destroy() {
|
|
1425
|
+
this.unsubscribeStateHandler();
|
|
1426
|
+
}
|
|
1427
|
+
get context() {
|
|
1428
|
+
return this._context;
|
|
1429
|
+
}
|
|
1430
|
+
get id() {
|
|
1431
|
+
return this._data.id;
|
|
1432
|
+
}
|
|
1433
|
+
get eventId() {
|
|
1434
|
+
return this.context.event.id;
|
|
1435
|
+
}
|
|
1436
|
+
get state() {
|
|
1437
|
+
return this._state;
|
|
1438
|
+
}
|
|
1439
|
+
get type() {
|
|
1440
|
+
return this._data.type;
|
|
1441
|
+
}
|
|
1442
|
+
get contentType() {
|
|
1443
|
+
return this._data.content_type;
|
|
1444
|
+
}
|
|
1445
|
+
get publishedAt() {
|
|
1446
|
+
return this._data.published_at;
|
|
1447
|
+
}
|
|
1448
|
+
get publishedAtIso() {
|
|
1449
|
+
return this._data.published_at_iso;
|
|
1450
|
+
}
|
|
1451
|
+
get updatedAt() {
|
|
1452
|
+
return this._data.updated_at;
|
|
1453
|
+
}
|
|
1454
|
+
get updatedAtIso() {
|
|
1455
|
+
return this._data.updated_at_iso;
|
|
1456
|
+
}
|
|
1457
|
+
get duration() {
|
|
1458
|
+
return this._data.duration;
|
|
1459
|
+
}
|
|
1460
|
+
get fields() {
|
|
1461
|
+
const { locale } = this.context.project;
|
|
1462
|
+
return localise(this._data.custom_fields, locale);
|
|
1463
|
+
}
|
|
1464
|
+
get interactive() {
|
|
1465
|
+
return this.type !== exports.ElementType.Data;
|
|
1466
|
+
}
|
|
1467
|
+
get pollId() {
|
|
1468
|
+
return this._data.data.id || null;
|
|
1469
|
+
}
|
|
1470
|
+
get question() {
|
|
1471
|
+
const { locale } = this.context.project;
|
|
1472
|
+
if (this._data.data.question === undefined) {
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
return localise(this._data.data.question, locale);
|
|
1476
|
+
}
|
|
1477
|
+
get answerOptions() {
|
|
1478
|
+
const { locale } = this.context.project;
|
|
1479
|
+
if (this._data.data.options === undefined) {
|
|
1480
|
+
return null;
|
|
1481
|
+
}
|
|
1482
|
+
return localise(this._data.data.options, locale);
|
|
1483
|
+
}
|
|
1484
|
+
get minOptionsPerVote() {
|
|
1485
|
+
return this._data.data.min_options_per_vote || 1;
|
|
1486
|
+
}
|
|
1487
|
+
get maxOptionsPerVote() {
|
|
1488
|
+
return this._data.data.max_options_per_vote || 1;
|
|
1489
|
+
}
|
|
1490
|
+
get maxVotesPerOption() {
|
|
1491
|
+
return this._data.data.max_votes_per_option || 1;
|
|
1492
|
+
}
|
|
1493
|
+
get maxVotesPerUser() {
|
|
1494
|
+
return this._data.data.max_votes_per_user || null;
|
|
1495
|
+
}
|
|
1496
|
+
get userAnswer() {
|
|
1497
|
+
return this._userAnswer;
|
|
1498
|
+
}
|
|
1499
|
+
set userAnswer(value) {
|
|
1500
|
+
this._userAnswer = value;
|
|
1501
|
+
}
|
|
1502
|
+
get hasBeenAnswered() {
|
|
1503
|
+
return this._userAnswer !== null;
|
|
1504
|
+
}
|
|
1505
|
+
get showResultsMode() {
|
|
1506
|
+
return this._data.data.reveal_results_mode || null;
|
|
1507
|
+
}
|
|
1508
|
+
get canShowResults() {
|
|
1509
|
+
switch (this.showResultsMode) {
|
|
1510
|
+
case ShowResultsMode.OnVote:
|
|
1511
|
+
return this.hasBeenAnswered || this.state === exports.ElementState.Closed;
|
|
1512
|
+
case ShowResultsMode.OnClose:
|
|
1513
|
+
return this.state === exports.ElementState.Closed;
|
|
1514
|
+
case ShowResultsMode.Manual:
|
|
1515
|
+
return this._results !== null;
|
|
1516
|
+
case ShowResultsMode.OnEventEnd:
|
|
1517
|
+
return this.context.event.state === exports.EventState.Finished;
|
|
1518
|
+
case ShowResultsMode.Never:
|
|
1519
|
+
return false;
|
|
1520
|
+
case ShowResultsMode.Always:
|
|
1521
|
+
default:
|
|
1522
|
+
return true;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
get results() {
|
|
1526
|
+
if (!this.canShowResults) {
|
|
1527
|
+
return null;
|
|
1528
|
+
}
|
|
1529
|
+
return this._results;
|
|
1530
|
+
}
|
|
1531
|
+
set results(results) {
|
|
1532
|
+
// skip setting results if they weren't changed
|
|
1533
|
+
if (JSON.stringify(results) === JSON.stringify(this.results)) {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
this._results = results;
|
|
1537
|
+
if (this.canShowResults) {
|
|
1538
|
+
this.emit('results');
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
get correctOption() {
|
|
1542
|
+
return this._correctOption;
|
|
1543
|
+
}
|
|
1544
|
+
set correctOption(value) {
|
|
1545
|
+
this._correctOption = value;
|
|
1546
|
+
}
|
|
1547
|
+
get isCorrectOptionRevealed() {
|
|
1548
|
+
return this._correctOption !== null;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
/**
|
|
1553
|
+
* @license
|
|
1554
|
+
* @monterosa/sdk-interact-kit
|
|
1555
|
+
*
|
|
1556
|
+
* Copyright © 2022-2024 Monterosa Productions Limited. All rights reserved.
|
|
1557
|
+
*
|
|
1558
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1559
|
+
*/
|
|
1560
|
+
const elements = new Map();
|
|
1561
|
+
async function buildElement(options, context) {
|
|
1562
|
+
if (elements.has(options.id)) {
|
|
1563
|
+
return elements.get(options.id);
|
|
1564
|
+
}
|
|
1565
|
+
const element = new ElementImpl(options, context);
|
|
1566
|
+
await restoreAnswer(element);
|
|
1567
|
+
elements.set(element.id, element);
|
|
1568
|
+
return element;
|
|
1569
|
+
}
|
|
1570
|
+
function getElementByPolld(pollId) {
|
|
1571
|
+
return Array.from(elements.values()).find((element) => element.pollId === pollId);
|
|
1572
|
+
}
|
|
1573
|
+
const configureEventMemoized = sdkUtil.memoizePromise(async (event) => {
|
|
1574
|
+
event.on('history', handleEventHistory);
|
|
1575
|
+
if (event.history !== null) {
|
|
1576
|
+
handleEventHistory(event.history);
|
|
1577
|
+
}
|
|
1578
|
+
await configureConnect(event.context.sdk.options.host);
|
|
1579
|
+
await init(event);
|
|
1580
|
+
}, (event) => event.id);
|
|
1581
|
+
async function configureEvent(event) {
|
|
1582
|
+
return configureEventMemoized(event);
|
|
1583
|
+
}
|
|
1584
|
+
const configureConnectMemoized = sdkUtil.memoizePromise(async (host) => {
|
|
1585
|
+
const connect = await getConnect(host);
|
|
1586
|
+
connect.on('message', handleConnectMessage);
|
|
1587
|
+
}, (host) => host);
|
|
1588
|
+
async function configureConnect(host) {
|
|
1589
|
+
return configureConnectMemoized(host);
|
|
1590
|
+
}
|
|
1591
|
+
async function handleConnectMessage(message) {
|
|
1592
|
+
const { klass } = message;
|
|
1593
|
+
switch (klass) {
|
|
1594
|
+
case exports.Klass.Create:
|
|
1595
|
+
await handleCreateMessage(message);
|
|
1596
|
+
break;
|
|
1597
|
+
case exports.Klass.Revoke:
|
|
1598
|
+
await handleRevokeMessage(message);
|
|
1599
|
+
break;
|
|
1600
|
+
case exports.Klass.Feedback:
|
|
1601
|
+
await handleFeedbackMessage(message);
|
|
1602
|
+
break;
|
|
1603
|
+
case exports.Klass.Reveal:
|
|
1604
|
+
await handleRevealMessage(message);
|
|
1605
|
+
break;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
async function handleCreateMessage(message) {
|
|
1609
|
+
const { id, data } = message;
|
|
1610
|
+
const options = JSON.parse(data[0]);
|
|
1611
|
+
const event = await getEvent(id);
|
|
1612
|
+
if (event === null) {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
if (elements.has(options.id)) {
|
|
1616
|
+
const element = elements.get(options.id);
|
|
1617
|
+
if (options.updated_at > element.updatedAt ||
|
|
1618
|
+
// This is a workaround to handle a Studio issue when two /create/ messages
|
|
1619
|
+
// are sent upon correct option reveal. The first /create/ message contains
|
|
1620
|
+
// the same duration but a different updated_at timestamp. The second /create/
|
|
1621
|
+
// message contains the same updated_at timestamp but a different duration.
|
|
1622
|
+
// Therefore, we need to check if the duration is different to ensure that
|
|
1623
|
+
// the element is updated correctly.
|
|
1624
|
+
options.duration !== element.duration) {
|
|
1625
|
+
element.update(options);
|
|
1626
|
+
event.emit('update', element);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
else {
|
|
1630
|
+
const context = Object.assign(Object.assign({}, event.context), { event });
|
|
1631
|
+
const element = await buildElement(options, context);
|
|
1632
|
+
event.emit('publish', element);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
function handleRevokeMessage(message) {
|
|
1636
|
+
const { data } = message;
|
|
1637
|
+
const { id } = JSON.parse(data[0]);
|
|
1638
|
+
if (!elements.has(id)) {
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
const element = elements.get(id);
|
|
1642
|
+
elements.delete(id);
|
|
1643
|
+
element.destroy();
|
|
1644
|
+
element.context.event.emit('revoke', element);
|
|
1645
|
+
}
|
|
1646
|
+
async function handleFeedbackMessage(message) {
|
|
1647
|
+
const { data } = message;
|
|
1648
|
+
// Feedback message format:
|
|
1649
|
+
//
|
|
1650
|
+
// [pollId, isFinal, sources, counters, timestamp]
|
|
1651
|
+
const pollId = +data[0];
|
|
1652
|
+
const counters = data[3];
|
|
1653
|
+
const element = getElementByPolld(pollId);
|
|
1654
|
+
if (!element) {
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
const votes = counters.split(':').map((value) => +value);
|
|
1658
|
+
const percentages = sdkUtil.calculatePercentage(votes);
|
|
1659
|
+
const results = votes.map((item, idx) => ({
|
|
1660
|
+
percentage: percentages[idx],
|
|
1661
|
+
votes: item,
|
|
1662
|
+
}));
|
|
1663
|
+
element.results = results;
|
|
1664
|
+
}
|
|
1665
|
+
function handleRevealMessage(message) {
|
|
1666
|
+
const { data } = message;
|
|
1667
|
+
// Reveal message format:
|
|
1668
|
+
//
|
|
1669
|
+
// [pollId, index (1-based), timestamp]
|
|
1670
|
+
const pollId = +data[0];
|
|
1671
|
+
const correctOption = +data[1] - 1;
|
|
1672
|
+
const element = getElementByPolld(pollId);
|
|
1673
|
+
if (!element) {
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
if (element.correctOption !== correctOption) {
|
|
1677
|
+
element.correctOption = correctOption;
|
|
1678
|
+
element.context.event.emit('update', element);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
async function handleEventHistory(history) {
|
|
1682
|
+
var _a, e_1, _b, _c;
|
|
1683
|
+
if (history.timeline.length === 0) {
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
const event = await getEvent(history.config.id);
|
|
1687
|
+
if (event === null) {
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
const context = Object.assign(Object.assign({}, event.context), { event });
|
|
1691
|
+
try {
|
|
1692
|
+
for (var _d = true, _e = __asyncValues(history.timeline), _f; _f = await _e.next(), _a = _f.done, !_a;) {
|
|
1693
|
+
_c = _f.value;
|
|
1694
|
+
_d = false;
|
|
1695
|
+
try {
|
|
1696
|
+
const snapshot = _c;
|
|
1697
|
+
if (!elements.has(snapshot.id)) {
|
|
1698
|
+
await buildElement(snapshot, context);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
finally {
|
|
1702
|
+
_d = true;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1707
|
+
finally {
|
|
1708
|
+
try {
|
|
1709
|
+
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
|
|
1710
|
+
}
|
|
1711
|
+
finally { if (e_1) throw e_1.error; }
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
async function sendAnswer(element, userAnswer) {
|
|
1715
|
+
const { project, event } = element.context;
|
|
1716
|
+
const connect = await getConnect(project.host);
|
|
1717
|
+
const answer = userAnswer.answers
|
|
1718
|
+
.map(({ option, value }) => [option + 1, value])
|
|
1719
|
+
.flat()
|
|
1720
|
+
.join(':');
|
|
1721
|
+
connect.sendVote(event.id, element.pollId, answer);
|
|
1722
|
+
}
|
|
1723
|
+
async function storeAnswer(element, userAnswer) {
|
|
1724
|
+
try {
|
|
1725
|
+
element.userAnswer = userAnswer;
|
|
1726
|
+
await sdkStorageKit.storageWrite(`element_${element.id}_vote`, JSON.stringify(userAnswer));
|
|
1727
|
+
}
|
|
1728
|
+
catch (err) {
|
|
1729
|
+
console.warn(`Failed to store user answer: ${sdkUtil.getErrorMessage(err)}`);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
async function restoreAnswer(element) {
|
|
1733
|
+
const userAnswer = await sdkStorageKit.storageRead(`element_${element.id}_vote`);
|
|
1734
|
+
if (userAnswer !== null) {
|
|
1735
|
+
element.userAnswer = Answer.fromJSON(userAnswer);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
/**
|
|
1740
|
+
* @license
|
|
1741
|
+
* @monterosa/sdk-interact-kit
|
|
1742
|
+
*
|
|
1743
|
+
* Copyright © 2022 Monterosa Productions Limited. All rights reserved.
|
|
1744
|
+
*
|
|
1745
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1746
|
+
*/
|
|
1747
|
+
/**
|
|
1748
|
+
* @internal
|
|
1749
|
+
*/
|
|
1750
|
+
const getElementMemoized = sdkUtil.memoizePromise(async (event, id) => {
|
|
1751
|
+
await configureEvent(event);
|
|
1752
|
+
const element = Array.from(elements.values()).find((el) => el.id === id);
|
|
1753
|
+
return element || null;
|
|
1754
|
+
}, (event, id) => id);
|
|
1755
|
+
/**
|
|
1756
|
+
* Returns an Element of a specific Event by its id.
|
|
1757
|
+
*
|
|
1758
|
+
* @param event - The Event that owns the Element
|
|
1759
|
+
* @param id - The Element identifier
|
|
1760
|
+
*/
|
|
1761
|
+
function getElement(event, id) {
|
|
1762
|
+
return getElementMemoized(event, id);
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* @internal
|
|
1766
|
+
*/
|
|
1767
|
+
const getElementsMemoized = sdkUtil.memoizePromise(async (event) => {
|
|
1768
|
+
await configureEvent(event);
|
|
1769
|
+
return (Array.from(elements.values())
|
|
1770
|
+
// Get Elements which belong only to the provided Event
|
|
1771
|
+
.filter((element) => element.eventId === event.id)
|
|
1772
|
+
// Sort Elements so the oldest Element is first, and the most recent is last
|
|
1773
|
+
.sort((a, b) => a.publishedAt - b.publishedAt));
|
|
1774
|
+
}, (event) => event.id, {
|
|
1775
|
+
clearOnResolve: true,
|
|
1776
|
+
});
|
|
1777
|
+
/**
|
|
1778
|
+
* Returns the list of Elements published in a specific Event
|
|
1779
|
+
*
|
|
1780
|
+
* @param event - The Event to fetch Elements for
|
|
1781
|
+
* @returns The published Elements, sorted oldest first.
|
|
1782
|
+
*/
|
|
1783
|
+
function getElements(event) {
|
|
1784
|
+
return getElementsMemoized(event);
|
|
1785
|
+
}
|
|
1786
|
+
function answer(element, ...value) {
|
|
1787
|
+
const answerObj = answerParametersToAnswer(...value);
|
|
1788
|
+
validateAnswer(element, ...value);
|
|
1789
|
+
sendAnswer(element, answerObj);
|
|
1790
|
+
storeAnswer(element, answerObj);
|
|
1791
|
+
}
|
|
1792
|
+
function validateAnswer(element, ...value) {
|
|
1793
|
+
const answerObj = answerParametersToAnswer(...value);
|
|
1794
|
+
const { answers } = answerObj;
|
|
1795
|
+
const indices = answers.map(({ option }) => option);
|
|
1796
|
+
const values = answers.map(({ value: v }) => v);
|
|
1797
|
+
const maxIndex = Math.max(...indices);
|
|
1798
|
+
const maxValue = Math.max(...values);
|
|
1799
|
+
const sumValues = values.reduce((memo, val) => memo + val, 0);
|
|
1800
|
+
const emptyIndices = values.reduce((memo, val, idx) => {
|
|
1801
|
+
if (val === 0) {
|
|
1802
|
+
memo.push(indices[idx]);
|
|
1803
|
+
}
|
|
1804
|
+
return memo;
|
|
1805
|
+
}, []);
|
|
1806
|
+
if (!element.interactive) {
|
|
1807
|
+
throw sdkUtil.createError(exports.AnswerError.VotedOnNonInteractiveElement, AnswerErrorMessages, element.id);
|
|
1808
|
+
}
|
|
1809
|
+
if (element.state === exports.ElementState.Closed) {
|
|
1810
|
+
throw sdkUtil.createError(exports.AnswerError.VotedOnClosedElement, AnswerErrorMessages, element.id);
|
|
1811
|
+
}
|
|
1812
|
+
if (maxIndex > element.answerOptions.length - 1) {
|
|
1813
|
+
throw sdkUtil.createError(exports.AnswerError.OptionIndexOutOfRange, AnswerErrorMessages, element.answerOptions.length - 1, maxIndex);
|
|
1814
|
+
}
|
|
1815
|
+
if (element.minOptionsPerVote !== null &&
|
|
1816
|
+
indices.length < element.minOptionsPerVote) {
|
|
1817
|
+
throw sdkUtil.createError(exports.AnswerError.BelowMinVoteOptions, AnswerErrorMessages, element.minOptionsPerVote, indices.length);
|
|
1818
|
+
}
|
|
1819
|
+
if (element.maxOptionsPerVote !== null &&
|
|
1820
|
+
indices.length > element.maxOptionsPerVote) {
|
|
1821
|
+
throw sdkUtil.createError(exports.AnswerError.AboveMaxVoteOptions, AnswerErrorMessages, element.maxOptionsPerVote, indices.length);
|
|
1822
|
+
}
|
|
1823
|
+
if (sumValues > element.maxVotesPerUser) {
|
|
1824
|
+
throw sdkUtil.createError(exports.AnswerError.AboveMaxVotesPerUser, AnswerErrorMessages, element.maxVotesPerUser, sumValues);
|
|
1825
|
+
}
|
|
1826
|
+
if (maxValue > element.maxVotesPerOption) {
|
|
1827
|
+
throw sdkUtil.createError(exports.AnswerError.AboveMaxVotesPerOption, AnswerErrorMessages, element.maxVotesPerOption, maxValue);
|
|
1828
|
+
}
|
|
1829
|
+
if (emptyIndices.length > 0) {
|
|
1830
|
+
throw sdkUtil.createError(exports.AnswerError.EmptyVote, AnswerErrorMessages, emptyIndices.join(', '));
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function answerParametersToAnswer(...value) {
|
|
1834
|
+
const answerValues = value.flat();
|
|
1835
|
+
if (isAnswer(answerValues[0])) {
|
|
1836
|
+
return answerValues[0];
|
|
1837
|
+
}
|
|
1838
|
+
if (isNumbers(answerValues)) {
|
|
1839
|
+
return Answer.fromIndices(answerValues);
|
|
1840
|
+
}
|
|
1841
|
+
if (isAnswerValues(answerValues)) {
|
|
1842
|
+
return new Answer(answerValues);
|
|
1843
|
+
}
|
|
1844
|
+
return new Answer();
|
|
1845
|
+
}
|
|
1846
|
+
function isAnswer(value) {
|
|
1847
|
+
return value instanceof Answer;
|
|
1848
|
+
}
|
|
1849
|
+
function isAnswerValues(value) {
|
|
1850
|
+
return value.every((v) => Object.prototype.hasOwnProperty.call(v, 'option') &&
|
|
1851
|
+
Object.prototype.hasOwnProperty.call(v, 'value'));
|
|
1852
|
+
}
|
|
1853
|
+
function isNumbers(value) {
|
|
1854
|
+
return value.every((v) => typeof v === 'number');
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Adds an observer for when
|
|
1858
|
+
* {@link InteractElement | Element results} are updated
|
|
1859
|
+
*
|
|
1860
|
+
* @param element - The Element to observe
|
|
1861
|
+
* @param callback - Called when results are updated
|
|
1862
|
+
*/
|
|
1863
|
+
function onElementResults(element, callback) {
|
|
1864
|
+
return sdkUtil.subscribe(element, 'results', callback);
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Adds an observer for when
|
|
1868
|
+
* {@link InteractElement | Element state} changed
|
|
1869
|
+
*
|
|
1870
|
+
* @param element - The Element to observe
|
|
1871
|
+
* @param callback - Called when the state changes
|
|
1872
|
+
*/
|
|
1873
|
+
function onElementStateChanged(element, callback) {
|
|
1874
|
+
return sdkUtil.subscribe(element, 'state', callback);
|
|
1875
|
+
}
|
|
1876
|
+
/**
|
|
1877
|
+
* Adds an observer for when
|
|
1878
|
+
* {@link InteractElement | Element fields} are updated
|
|
1879
|
+
*
|
|
1880
|
+
* @param event - The Event containing the Elements
|
|
1881
|
+
* @param callback - Called with the updated Element
|
|
1882
|
+
*/
|
|
1883
|
+
function onElementUpdated(event, callback) {
|
|
1884
|
+
configureEvent(event);
|
|
1885
|
+
return sdkUtil.subscribe(event, 'update', callback);
|
|
1886
|
+
}
|
|
1887
|
+
/**
|
|
1888
|
+
* Adds an observer for when a new Element is published
|
|
1889
|
+
*
|
|
1890
|
+
* @param event - The Event to observe
|
|
1891
|
+
* @param callback - Called with the published Element
|
|
1892
|
+
*/
|
|
1893
|
+
function onElementPublished(event, callback) {
|
|
1894
|
+
configureEvent(event);
|
|
1895
|
+
return sdkUtil.subscribe(event, 'publish', (element) => {
|
|
1896
|
+
if (event.initState === InitState.Initialised) {
|
|
1897
|
+
callback(element);
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Adds an observer for when an Element is revoked
|
|
1903
|
+
*
|
|
1904
|
+
* @param event - The Event to observe
|
|
1905
|
+
* @param callback - Called with the revoked Element
|
|
1906
|
+
*/
|
|
1907
|
+
function onElementRevoked(event, callback) {
|
|
1908
|
+
configureEvent(event);
|
|
1909
|
+
return sdkUtil.subscribe(event, 'revoke', callback);
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* @license
|
|
1914
|
+
* @monterosa/sdk-interact-kit
|
|
1915
|
+
*
|
|
1916
|
+
* Copyright © 2025 Monterosa Productions Limited. All rights reserved.
|
|
1917
|
+
*
|
|
1918
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1919
|
+
*/
|
|
1920
|
+
exports.PresenceCounterState = void 0;
|
|
1921
|
+
(function (PresenceCounterState) {
|
|
1922
|
+
PresenceCounterState["Opened"] = "opened";
|
|
1923
|
+
PresenceCounterState["Closed"] = "closed";
|
|
1924
|
+
})(exports.PresenceCounterState || (exports.PresenceCounterState = {}));
|
|
1925
|
+
|
|
1926
|
+
/**
|
|
1927
|
+
* @license
|
|
1928
|
+
* @monterosa/sdk-interact-kit
|
|
1929
|
+
*
|
|
1930
|
+
* Copyright © 2023 Monterosa Productions Limited. All rights reserved.
|
|
1931
|
+
*
|
|
1932
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1933
|
+
*/
|
|
1934
|
+
const logger = new sdkCore.Logger('@monterosa/sdk-interact-kit');
|
|
1935
|
+
|
|
1936
|
+
/**
|
|
1937
|
+
* @license
|
|
1938
|
+
* @monterosa/sdk-interact-kit
|
|
1939
|
+
*
|
|
1940
|
+
* Copyright © 2025 Monterosa Productions Limited. All rights reserved.
|
|
1941
|
+
*
|
|
1942
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
1943
|
+
*/
|
|
1944
|
+
class PresenceCounterImpl extends sdkUtil.Emitter {
|
|
1945
|
+
constructor(host, id) {
|
|
1946
|
+
super();
|
|
1947
|
+
this.host = host;
|
|
1948
|
+
this.id = id;
|
|
1949
|
+
this.state = exports.PresenceCounterState.Closed;
|
|
1950
|
+
this.lastCount = 0;
|
|
1951
|
+
this.connect = null;
|
|
1952
|
+
this.boundHandleMessage = this.handleMessage.bind(this);
|
|
1953
|
+
this.boundHandleTimeout = this.handleTimeout.bind(this);
|
|
1954
|
+
}
|
|
1955
|
+
async init() {
|
|
1956
|
+
this.connect = await getConnect(this.host);
|
|
1957
|
+
this.connect.on('message', this.boundHandleMessage);
|
|
1958
|
+
}
|
|
1959
|
+
get isOpened() {
|
|
1960
|
+
return this.state === exports.PresenceCounterState.Opened;
|
|
1961
|
+
}
|
|
1962
|
+
async handleMessage(message) {
|
|
1963
|
+
const { id, klass, data } = message;
|
|
1964
|
+
if (id !== this.id || klass !== exports.Klass.SubsCounter) {
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
try {
|
|
1968
|
+
const subsData = JSON.parse(data[0]);
|
|
1969
|
+
this.handleCounterUpdate(subsData.counter);
|
|
1970
|
+
this.resetTimeout(subsData.next_update_at);
|
|
1971
|
+
}
|
|
1972
|
+
catch (e) {
|
|
1973
|
+
logger.warn("Couldn't parse subscounter message.");
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
handleCounterUpdate(count) {
|
|
1977
|
+
if (count < 0) {
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
let shouldEmitUpdated = false;
|
|
1981
|
+
let shouldEmitState = false;
|
|
1982
|
+
if (this.lastCount !== count) {
|
|
1983
|
+
shouldEmitUpdated = true;
|
|
1984
|
+
this.lastCount = count;
|
|
1985
|
+
}
|
|
1986
|
+
if (this.state === exports.PresenceCounterState.Closed) {
|
|
1987
|
+
shouldEmitState = true;
|
|
1988
|
+
this.state = exports.PresenceCounterState.Opened;
|
|
1989
|
+
}
|
|
1990
|
+
if (shouldEmitUpdated) {
|
|
1991
|
+
this.emit('updated', this.lastCount);
|
|
1992
|
+
}
|
|
1993
|
+
if (shouldEmitState) {
|
|
1994
|
+
this.emit('state', exports.PresenceCounterState.Opened);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
handleTimeout() {
|
|
1998
|
+
this.state = exports.PresenceCounterState.Closed;
|
|
1999
|
+
this.emit('state', exports.PresenceCounterState.Closed);
|
|
2000
|
+
}
|
|
2001
|
+
resetTimeout(nextUpdateAt) {
|
|
2002
|
+
clearTimeout(this.closeTimeout);
|
|
2003
|
+
this.closeTimeout = setTimeout(this.boundHandleTimeout,
|
|
2004
|
+
// because of the time difference between the server and the client,
|
|
2005
|
+
// we need to add 3 seconds to the timeout
|
|
2006
|
+
(nextUpdateAt - sdkUtil.now() + 3) * 1000);
|
|
2007
|
+
}
|
|
2008
|
+
destroy() {
|
|
2009
|
+
if (this.connect !== null) {
|
|
2010
|
+
this.connect.off('message', this.boundHandleMessage);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
/**
|
|
2016
|
+
* @license
|
|
2017
|
+
* @monterosa/sdk-interact-kit
|
|
2018
|
+
*
|
|
2019
|
+
* Copyright © 2025 Monterosa Productions Limited. All rights reserved.
|
|
2020
|
+
*
|
|
2021
|
+
* More details on the license can be found at https://www.monterosa.co/sdk/license
|
|
2022
|
+
*/
|
|
2023
|
+
const presenceCounters = new Map();
|
|
2024
|
+
const getPresenceCounterMemoized = sdkUtil.memoizePromise(async (host, id) => {
|
|
2025
|
+
if (presenceCounters.has(id)) {
|
|
2026
|
+
return presenceCounters.get(id);
|
|
2027
|
+
}
|
|
2028
|
+
const presenceCounter = new PresenceCounterImpl(host, id);
|
|
2029
|
+
await presenceCounter.init();
|
|
2030
|
+
presenceCounters.set(id, presenceCounter);
|
|
2031
|
+
return presenceCounter;
|
|
2032
|
+
}, (_, id) => id);
|
|
2033
|
+
async function getPresenceCounter(project) {
|
|
2034
|
+
if (project === undefined) {
|
|
2035
|
+
project = await getProject();
|
|
2036
|
+
}
|
|
2037
|
+
return getPresenceCounterMemoized(project.context.sdk.options.host, project.id);
|
|
2038
|
+
}
|
|
2039
|
+
function onPresenceCounterUpdate(presenceCounter, callback) {
|
|
2040
|
+
presenceCounter.on('updated', callback);
|
|
2041
|
+
return () => presenceCounter.off('updated', callback);
|
|
2042
|
+
}
|
|
2043
|
+
function onPresenceCounterOpen(presenceCounter, callback) {
|
|
2044
|
+
const handler = (state) => {
|
|
2045
|
+
if (state === exports.PresenceCounterState.Opened) {
|
|
2046
|
+
callback();
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
presenceCounter.on('state', handler);
|
|
2050
|
+
return () => presenceCounter.off('state', handler);
|
|
2051
|
+
}
|
|
2052
|
+
function onPresenceCounterClose(presenceCounter, callback) {
|
|
2053
|
+
const handler = (state) => {
|
|
2054
|
+
if (state === exports.PresenceCounterState.Closed) {
|
|
2055
|
+
callback();
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2058
|
+
presenceCounter.on('state', handler);
|
|
2059
|
+
return () => presenceCounter.off('state', handler);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
exports.Answer = Answer;
|
|
2063
|
+
exports.ElementImpl = ElementImpl;
|
|
2064
|
+
exports.EventImpl = EventImpl;
|
|
2065
|
+
exports.ProjectImpl = ProjectImpl;
|
|
2066
|
+
exports.answer = answer;
|
|
2067
|
+
exports.getConnect = getConnect;
|
|
2068
|
+
exports.getConnectionHealth = getConnectionHealth;
|
|
2069
|
+
exports.getElement = getElement;
|
|
2070
|
+
exports.getElementMemoized = getElementMemoized;
|
|
2071
|
+
exports.getElements = getElements;
|
|
2072
|
+
exports.getElementsMemoized = getElementsMemoized;
|
|
2073
|
+
exports.getEvent = getEvent;
|
|
2074
|
+
exports.getEventMemoized = getEventMemoized;
|
|
2075
|
+
exports.getEvents = getEvents;
|
|
2076
|
+
exports.getEventsMemoized = getEventsMemoized;
|
|
2077
|
+
exports.getPresenceCounter = getPresenceCounter;
|
|
2078
|
+
exports.getProject = getProject;
|
|
2079
|
+
exports.getProjectMemoized = getProjectMemoized;
|
|
2080
|
+
exports.onConnectionHealthState = onConnectionHealthState;
|
|
2081
|
+
exports.onElementPublished = onElementPublished;
|
|
2082
|
+
exports.onElementResults = onElementResults;
|
|
2083
|
+
exports.onElementRevoked = onElementRevoked;
|
|
2084
|
+
exports.onElementStateChanged = onElementStateChanged;
|
|
2085
|
+
exports.onElementUpdated = onElementUpdated;
|
|
2086
|
+
exports.onEventAdded = onEventAdded;
|
|
2087
|
+
exports.onEventRemoved = onEventRemoved;
|
|
2088
|
+
exports.onEventState = onEventState;
|
|
2089
|
+
exports.onEventUpdated = onEventUpdated;
|
|
2090
|
+
exports.onPresenceCounterClose = onPresenceCounterClose;
|
|
2091
|
+
exports.onPresenceCounterOpen = onPresenceCounterOpen;
|
|
2092
|
+
exports.onPresenceCounterUpdate = onPresenceCounterUpdate;
|
|
2093
|
+
exports.onProjectFieldsUpdated = onProjectFieldsUpdated;
|
|
2094
|
+
exports.validateAnswer = validateAnswer;
|
|
2095
|
+
//# sourceMappingURL=index.cjs.map
|