@monterosa/sdk-interact-kit 0.19.0-rc.5 → 2.0.0-rc.1

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.esm.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { getConnect as getConnect$1, login as login$1, connect, disconnect, subscribe as subscribe$1, unsubscribe as unsubscribe$1, send, ConnState, onConnected } from '@monterosa/sdk-connect-kit';
2
- import { Emitter, memoizePromise, subscribe as subscribe$2, checksum, onTick, now, checkAvailability, calculatePercentage, getItem, setItem, createError } from '@monterosa/sdk-util';
2
+ import { Emitter, memoizePromise, subscribe as subscribe$2, checksum, onTick, now, calculatePercentage, getErrorMessage, createError } from '@monterosa/sdk-util';
3
3
  import { getSdk, Logger } from '@monterosa/sdk-core';
4
+ import { fetchSettings } from '@monterosa/sdk-interact-interop';
5
+ import { storageRead, storageWrite } from '@monterosa/sdk-storage-kit';
4
6
 
5
7
  /**
6
8
  * @license
@@ -286,20 +288,6 @@ function onConnectionHealthState(connectionHealth, callback) {
286
288
  return subscribe$2(connectionHealth, 'state', callback);
287
289
  }
288
290
 
289
- /**
290
- * @license
291
- * @monterosa/sdk-interact-kit
292
- *
293
- * Copyright © 2022 Monterosa Productions Limited. All rights reserved.
294
- *
295
- * More details on the license can be found at https://www.monterosa.co/sdk/license
296
- */
297
- async function fetchSettings(host, id) {
298
- const response = await fetch(`https://${host}/projects/${id.substring(0, 2)}/${id}/settings.json`);
299
- const data = await response.json();
300
- return data;
301
- }
302
-
303
291
  /**
304
292
  * @license
305
293
  * @monterosa/sdk-interact-kit
@@ -413,11 +401,12 @@ class ProjectImpl extends Emitter {
413
401
  }
414
402
  set listings(newListings) {
415
403
  const createdEvents = this._calculateCreatedEvents(newListings);
404
+ const updatedEvents = this._calculateUpdatedEvents(newListings);
416
405
  const deletedEvents = this._calculateDeletedEvents(newListings);
417
406
  this._listings = Object.assign({}, newListings);
418
407
  this._updateLocales(newListings);
419
408
  this._updateEventsChecksum(newListings);
420
- this._emitListingsEvents(createdEvents, deletedEvents);
409
+ this._emitListingsEvents(createdEvents, updatedEvents, deletedEvents);
421
410
  }
422
411
  /**
423
412
  * Calculates events that were created (present in new listings but not in old)
@@ -428,6 +417,21 @@ class ProjectImpl extends Emitter {
428
417
  }
429
418
  return newListings.events.filter(({ id }) => !this._listings.events.some((event) => id === event.id));
430
419
  }
420
+ /**
421
+ * Calculates events that were updated (present in old listings and new,
422
+ * but with different digest)
423
+ */
424
+ _calculateUpdatedEvents(newListings) {
425
+ if (!this._listings) {
426
+ return [];
427
+ }
428
+ return newListings.events.filter((event) => {
429
+ // find event data in new listings
430
+ const existingEvent = this._listings.events.find(({ id }) => event.id === id);
431
+ // return true if event data is found and digest is different
432
+ return existingEvent && event.digest !== existingEvent.digest;
433
+ });
434
+ }
431
435
  /**
432
436
  * Calculates events that were deleted (present in old listings but not in new)
433
437
  */
@@ -459,16 +463,20 @@ class ProjectImpl extends Emitter {
459
463
  /**
460
464
  * Emits events based on created and deleted events
461
465
  */
462
- _emitListingsEvents(createdEvents, deletedEvents) {
466
+ _emitListingsEvents(createdEvents, updatedEvents, deletedEvents) {
463
467
  const hasCreatedEvents = createdEvents.length > 0;
464
468
  const hasDeletedEvents = deletedEvents.length > 0;
465
- const hasChanges = hasCreatedEvents || hasDeletedEvents;
469
+ const hasUpdatedEvents = updatedEvents.length > 0;
470
+ const hasChanges = hasCreatedEvents || hasDeletedEvents || hasUpdatedEvents;
466
471
  if (hasCreatedEvents) {
467
472
  this.emit('listings_events_created', createdEvents);
468
473
  }
469
474
  if (hasDeletedEvents) {
470
475
  this.emit('listings_events_deleted', deletedEvents);
471
476
  }
477
+ if (hasUpdatedEvents) {
478
+ this.emit('listings_events_updated', updatedEvents);
479
+ }
472
480
  if (hasChanges) {
473
481
  this.emit('listings');
474
482
  }
@@ -650,14 +658,6 @@ const getProjectMemoized = memoizePromise(async (sdk) => {
650
658
  function getProject(sdk = getSdk()) {
651
659
  return getProjectMemoized(sdk);
652
660
  }
653
- /**
654
- * @internal
655
- */
656
- async function fetchListings(host, projectId) {
657
- const response = await fetch(`https://${host}/projects/${projectId.substring(0, 2)}/${projectId}/listings.json`);
658
- const data = (await response.json());
659
- return data;
660
- }
661
661
  /**
662
662
  * Adds an observer for when {@link InteractProject.fields | project fields}
663
663
  * are updated
@@ -666,9 +666,13 @@ function onProjectFieldsUpdated(project, callback) {
666
666
  return subscribe$2(project, 'updated', callback);
667
667
  }
668
668
  /**
669
- * @internal
669
+ * Adds an observer that is called when the project listings are updated.
670
+ *
671
+ * @deprecated Use {@link onEventAdded()}, {@link onEventUpdated()} and
672
+ * {@link onEventRemoved()} instead
670
673
  */
671
674
  function onProjectListingsUpdated(project, callback) {
675
+ console.warn('onProjectListingsUpdated() is deprecated. Please use onEventAdded(), onEventUpdated() and onEventRemoved() instead');
672
676
  return subscribe$2(project, 'listings', callback);
673
677
  }
674
678
 
@@ -790,11 +794,12 @@ class EventImpl extends Emitter {
790
794
  this._context = context;
791
795
  this._state = this.calculateState();
792
796
  this._internalState = this.calculateInternalState();
793
- this.unsubscribeStateHandler = onTick(() => this.handleState());
794
- this.unsubscribeInternalStateHandler = onTick(() => {
795
- this.handleInternalState();
796
- });
797
- this.unsubscribeListingsHandler = onProjectListingsUpdated(context.project, () => this.handleListings());
797
+ this.boundHandleState = this.handleState.bind(this);
798
+ this.boundHandleInternalState = this.handleInternalState.bind(this);
799
+ this.boundHandleEventsUpdated = this.handleEventsUpdated.bind(this);
800
+ this.unsubscribeStateHandler = onTick(this.boundHandleState);
801
+ this.unsubscribeInternalStateHandler = onTick(this.boundHandleInternalState);
802
+ this.unsubscribeListingsHandler = subscribe$2(this.context.project, 'listings_events_updated', this.boundHandleEventsUpdated);
798
803
  }
799
804
  calculateState() {
800
805
  if (!this._data.started || now() < this.startAt) {
@@ -837,11 +842,13 @@ class EventImpl extends Emitter {
837
842
  this.emit('internal_state', internalState);
838
843
  }
839
844
  }
840
- handleListings() {
841
- const { project } = this.context;
842
- const data = project.events.find(({ id }) => id === this.id);
845
+ handleEventsUpdated(events) {
846
+ const data = events.find(({ id }) => id === this.id);
843
847
  if (data && this._data.digest !== data.digest) {
844
848
  this._data = data;
849
+ // Forcing state recalculation to ensure the event is in the correct state
850
+ this.handleState();
851
+ this.handleInternalState();
845
852
  this.emit('updated');
846
853
  }
847
854
  }
@@ -1431,6 +1438,7 @@ class ElementImpl extends Emitter {
1431
1438
  }
1432
1439
  update(data) {
1433
1440
  this._data = data;
1441
+ this.handleState();
1434
1442
  }
1435
1443
  destroy() {
1436
1444
  this.unsubscribeStateHandler();
@@ -1569,13 +1577,12 @@ class ElementImpl extends Emitter {
1569
1577
  * More details on the license can be found at https://www.monterosa.co/sdk/license
1570
1578
  */
1571
1579
  const elements = new Map();
1572
- const storageAvailable = checkAvailability();
1573
- function buildElement(options, context) {
1580
+ async function buildElement(options, context) {
1574
1581
  if (elements.has(options.id)) {
1575
1582
  return elements.get(options.id);
1576
1583
  }
1577
1584
  const element = new ElementImpl(options, context);
1578
- restoreAnswer(element);
1585
+ await restoreAnswer(element);
1579
1586
  elements.set(element.id, element);
1580
1587
  return element;
1581
1588
  }
@@ -1626,14 +1633,21 @@ async function handleCreateMessage(message) {
1626
1633
  }
1627
1634
  if (elements.has(options.id)) {
1628
1635
  const element = elements.get(options.id);
1629
- if (options.updated_at > element.updatedAt) {
1636
+ if (options.updated_at > element.updatedAt ||
1637
+ // This is a workaround to handle a Studio issue when two /create/ messages
1638
+ // are sent upon correct option reveal. The first /create/ message contains
1639
+ // the same duration but a different updated_at timestamp. The second /create/
1640
+ // message contains the same updated_at timestamp but a different duration.
1641
+ // Therefore, we need to check if the duration is different to ensure that
1642
+ // the element is updated correctly.
1643
+ options.duration !== element.duration) {
1630
1644
  element.update(options);
1631
1645
  event.emit('update', element);
1632
1646
  }
1633
1647
  }
1634
1648
  else {
1635
1649
  const context = Object.assign(Object.assign({}, event.context), { event });
1636
- const element = buildElement(options, context);
1650
+ const element = await buildElement(options, context);
1637
1651
  event.emit('publish', element);
1638
1652
  }
1639
1653
  }
@@ -1678,10 +1692,13 @@ function handleRevealMessage(message) {
1678
1692
  if (!element) {
1679
1693
  return;
1680
1694
  }
1681
- element.correctOption = correctOption;
1682
- element.context.event.emit('update', element);
1695
+ if (element.correctOption !== correctOption) {
1696
+ element.correctOption = correctOption;
1697
+ element.context.event.emit('update', element);
1698
+ }
1683
1699
  }
1684
1700
  async function handleEventHistory(history) {
1701
+ var _a, e_1, _b, _c;
1685
1702
  if (history.timeline.length === 0) {
1686
1703
  return;
1687
1704
  }
@@ -1690,11 +1707,28 @@ async function handleEventHistory(history) {
1690
1707
  return;
1691
1708
  }
1692
1709
  const context = Object.assign(Object.assign({}, event.context), { event });
1693
- for (const snapshot of history.timeline) {
1694
- if (!elements.has(snapshot.id)) {
1695
- buildElement(snapshot, context);
1710
+ try {
1711
+ for (var _d = true, _e = __asyncValues(history.timeline), _f; _f = await _e.next(), _a = _f.done, !_a;) {
1712
+ _c = _f.value;
1713
+ _d = false;
1714
+ try {
1715
+ const snapshot = _c;
1716
+ if (!elements.has(snapshot.id)) {
1717
+ await buildElement(snapshot, context);
1718
+ }
1719
+ }
1720
+ finally {
1721
+ _d = true;
1722
+ }
1696
1723
  }
1697
1724
  }
1725
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
1726
+ finally {
1727
+ try {
1728
+ if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
1729
+ }
1730
+ finally { if (e_1) throw e_1.error; }
1731
+ }
1698
1732
  }
1699
1733
  async function sendAnswer(element, userAnswer) {
1700
1734
  const { project, event } = element.context;
@@ -1705,27 +1739,19 @@ async function sendAnswer(element, userAnswer) {
1705
1739
  .join(':');
1706
1740
  connect.sendVote(event.id, element.pollId, answer);
1707
1741
  }
1708
- function storeAnswer(element, userAnswer) {
1742
+ async function storeAnswer(element, userAnswer) {
1709
1743
  try {
1710
1744
  element.userAnswer = userAnswer;
1711
- if (storageAvailable) {
1712
- // Can throw a "QuotaExceededError" DOMException exception
1713
- // if the quota has been exceeded.
1714
- setItem(`element_${element.id}_vote`, JSON.stringify(userAnswer));
1715
- }
1745
+ await storageWrite(`element_${element.id}_vote`, JSON.stringify(userAnswer));
1716
1746
  }
1717
1747
  catch (err) {
1718
- if (err instanceof Error) {
1719
- console.warn(`Can't store user answer: ${err.message}`);
1720
- }
1748
+ console.warn(`Failed to store user answer: ${getErrorMessage(err)}`);
1721
1749
  }
1722
1750
  }
1723
- function restoreAnswer(element) {
1724
- if (storageAvailable) {
1725
- const userAnswer = getItem(`element_${element.id}_vote`);
1726
- if (userAnswer !== null) {
1727
- element.userAnswer = Answer.fromJSON(userAnswer);
1728
- }
1751
+ async function restoreAnswer(element) {
1752
+ const userAnswer = await storageRead(`element_${element.id}_vote`);
1753
+ if (userAnswer !== null) {
1754
+ element.userAnswer = Answer.fromJSON(userAnswer);
1729
1755
  }
1730
1756
  }
1731
1757
 
@@ -2031,5 +2057,5 @@ function onPresenceCounterClose(presenceCounter, callback) {
2031
2057
  return () => presenceCounter.off('state', handler);
2032
2058
  }
2033
2059
 
2034
- export { Answer, AnswerError, Channel, ConnectionHealthState, ElementImpl, ElementState, ElementType, EventImpl, EventState, Klass, PresenceCounterState, ProjectImpl, State, answer, fetchListings, getConnect, getConnectionHealth, getElement, getElementMemoized, getElements, getElementsMemoized, getEvent, getEventMemoized, getEvents, getEventsMemoized, getPresenceCounter, getProject, getProjectMemoized, login, onConnectionHealthState, onElementPublished, onElementResults, onElementRevoked, onElementStateChanged, onElementUpdated, onEventAdded, onEventPublished, onEventRemoved, onEventState, onEventUpdated, onPresenceCounterClose, onPresenceCounterOpen, onPresenceCounterUpdate, onProjectFieldsUpdated, onProjectListingsUpdated, validateAnswer };
2060
+ export { Answer, AnswerError, Channel, ConnectionHealthState, ElementImpl, ElementState, ElementType, EventImpl, EventState, Klass, PresenceCounterState, ProjectImpl, State, answer, getConnect, getConnectionHealth, getElement, getElementMemoized, getElements, getElementsMemoized, getEvent, getEventMemoized, getEvents, getEventsMemoized, getPresenceCounter, getProject, getProjectMemoized, login, onConnectionHealthState, onElementPublished, onElementResults, onElementRevoked, onElementStateChanged, onElementUpdated, onEventAdded, onEventPublished, onEventRemoved, onEventState, onEventUpdated, onPresenceCounterClose, onPresenceCounterOpen, onPresenceCounterUpdate, onProjectFieldsUpdated, onProjectListingsUpdated, validateAnswer };
2035
2061
  //# sourceMappingURL=index.esm.js.map