@raydiant/playlist-window-manager 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -89,6 +89,7 @@ export default class PlaylistWindowManager extends EventEmitter {
89
89
  private evalRuleEngine;
90
90
  private handleEnableInterruptLock;
91
91
  private handleDisableInterruptLock;
92
+ private handleAnalyticsEvent;
92
93
  private handleCurrentPresentationComplete;
93
94
  private handleCurrentPresentationError;
94
95
  private handleNextPresentationError;
@@ -109,6 +109,11 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
109
109
  _this.endCurrentPresentation();
110
110
  }
111
111
  };
112
+ _this.handleAnalyticsEvent = function (event) {
113
+ // Emit the event on the current window. Only the current window should
114
+ // send analytics events since the next window is not visible.
115
+ _this.emitCurrentWindowEvent('window_analytics_event', event);
116
+ };
112
117
  _this.handleCurrentPresentationComplete = function () { return __awaiter(_this, void 0, void 0, function () {
113
118
  var logger, isThrottled, now, timeSinceLastComplete, delay, _a, next, preview, nextPreviewPresentationId;
114
119
  var _this = this;
@@ -693,6 +698,7 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
693
698
  this.currentWindow.onError(this.handleCurrentPresentationError);
694
699
  this.currentWindow.onEnableInterruptLock(this.handleEnableInterruptLock);
695
700
  this.currentWindow.onDisableInterruptLock(this.handleDisableInterruptLock);
701
+ this.currentWindow.onAnalyticsEvent(this.handleAnalyticsEvent);
696
702
  return [4 /*yield*/, this.currentWindow.load()];
697
703
  case 1:
698
704
  _a.sent();
@@ -896,6 +902,7 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
896
902
  this.currentWindow.onError(this.handleCurrentPresentationError);
897
903
  this.currentWindow.onEnableInterruptLock(this.handleEnableInterruptLock);
898
904
  this.currentWindow.onDisableInterruptLock(this.handleDisableInterruptLock);
905
+ this.currentWindow.onAnalyticsEvent(this.handleAnalyticsEvent);
899
906
  }
900
907
  // Play new current window.
901
908
  this.playCurrentWindow();
@@ -1,7 +1,7 @@
1
1
  import * as RE from '@raydiant/playlist-rule-engine';
2
2
  import PlaylistWindowManagerState from './PlaylistWindowManagerState';
3
3
  import PlaylistWindowManager from './PlaylistWindowManager';
4
- import { Presentation, ApplicationVersion, Theme, Device, PresentationWindowRenderer, WindowBounds, Logger, BuilderState, WorkerState } from './types';
4
+ import { Presentation, ApplicationVersion, Theme, Device, PresentationWindowRenderer, WindowBounds, Logger, BuilderState, WorkerState, AnalyticsEvent } from './types';
5
5
  export interface PresentationWindowOptions {
6
6
  windowManager?: PlaylistWindowManager;
7
7
  windowId: string;
@@ -49,6 +49,7 @@ export default class PresentationWindow {
49
49
  private isChildWindowLocked;
50
50
  private enableInterruptLockCallback;
51
51
  private disableInterruptLockCallback;
52
+ private analyticsEventCallback;
52
53
  private builderState;
53
54
  private presentation;
54
55
  private playlistWindowManagerState;
@@ -73,6 +74,7 @@ export default class PresentationWindow {
73
74
  onError(callback: ((err: Error) => void) | null): void;
74
75
  onEnableInterruptLock(callback: (() => void) | null): void;
75
76
  onDisableInterruptLock(callback: (() => void) | null): void;
77
+ onAnalyticsEvent(callback: ((event: AnalyticsEvent) => void) | null): void;
76
78
  isReady(): boolean;
77
79
  isComplete(): boolean;
78
80
  isPlaying(): boolean;
@@ -67,6 +67,15 @@ var defaultAppRendererUrl = 'https://apps.raydiant.com/3.0.0/index.html';
67
67
  var isValidNumber = function (value) {
68
68
  return typeof value === 'number' && !isNaN(value);
69
69
  };
70
+ var isValidAnalyticsEvent = function (value) {
71
+ return value &&
72
+ typeof value === 'object' &&
73
+ typeof value.type === 'string' &&
74
+ typeof value.source === 'string' &&
75
+ (typeof value.productCategory === 'string' ||
76
+ value.productCategory === undefined ||
77
+ value.productCategory === null);
78
+ };
70
79
  var PresentationWindow = /** @class */ (function () {
71
80
  function PresentationWindow(options) {
72
81
  var _this = this;
@@ -87,6 +96,7 @@ var PresentationWindow = /** @class */ (function () {
87
96
  this.isChildWindowLocked = {};
88
97
  this.enableInterruptLockCallback = null;
89
98
  this.disableInterruptLockCallback = null;
99
+ this.analyticsEventCallback = null;
90
100
  this.forceMute = false;
91
101
  this.error = null;
92
102
  this.workerState = {};
@@ -168,6 +178,12 @@ var PresentationWindow = /** @class */ (function () {
168
178
  }
169
179
  break;
170
180
  }
181
+ case 'analytics-event': {
182
+ if (this.analyticsEventCallback && isValidAnalyticsEvent(payload)) {
183
+ this.analyticsEventCallback(payload);
184
+ }
185
+ break;
186
+ }
171
187
  default: {
172
188
  this.logWarning("Received unknown embedded message type: " + type);
173
189
  }
@@ -400,6 +416,9 @@ var PresentationWindow = /** @class */ (function () {
400
416
  PresentationWindow.prototype.onDisableInterruptLock = function (callback) {
401
417
  this.disableInterruptLockCallback = callback;
402
418
  };
419
+ PresentationWindow.prototype.onAnalyticsEvent = function (callback) {
420
+ this.analyticsEventCallback = callback;
421
+ };
403
422
  PresentationWindow.prototype.isReady = function () {
404
423
  // TODO: To support the 'ready' event in embedded apps, they will need to tell the
405
424
  // runtime that it should wait for ready instead of DOMLoaded. This could be a "wait
@@ -523,7 +542,7 @@ var PresentationWindow = /** @class */ (function () {
523
542
  }
524
543
  url = this.getEmbeddedAppUrl();
525
544
  this.logInfo("Create window '" + windowId + "' for embedded app url '" + url + "'");
526
- return [4 /*yield*/, this.createAndLoadWindow(this.appendWindowIdToHash(url, windowId),
545
+ return [4 /*yield*/, this.createAndLoadWindow(url,
527
546
  // Embedded apps should not have a backgroundColor because some sites expect
528
547
  // the browser default of white will be applied.
529
548
  '#ffffff', this.handleEmbeddedMessage)];
@@ -766,6 +785,9 @@ var PresentationWindow = /** @class */ (function () {
766
785
  windowManager.on('window_end_error', function (metadata) {
767
786
  parentWindowManager.emit('window_end_error', metadata);
768
787
  });
788
+ windowManager.on('window_analytics_event', function (metadata) {
789
+ parentWindowManager.emit('window_analytics_event', metadata);
790
+ });
769
791
  }
770
792
  this.windowManagers[windowName] = windowManager;
771
793
  windowManager.setRenderer(renderer);
@@ -832,10 +854,12 @@ var PresentationWindow = /** @class */ (function () {
832
854
  }
833
855
  };
834
856
  PresentationWindow.prototype.getEmbeddedAppUrl = function () {
857
+ var windowId = this.options.windowId;
835
858
  var metadata = {
836
859
  renderMode: this.options.isPreview ? 'preview' : 'live'
837
860
  };
838
- return embeddedApps_1.default(this.presentation, this.options.applicationVersion, metadata);
861
+ var url = embeddedApps_1.default(this.presentation, this.options.applicationVersion, metadata);
862
+ return this.appendWindowIdToHash(url, windowId);
839
863
  };
840
864
  PresentationWindow.prototype.handleReady = function () {
841
865
  this.didReceiveReady = true;
package/build/types.d.ts CHANGED
@@ -130,3 +130,8 @@ export interface InputState {
130
130
  export interface EmbeddedAppMetadata {
131
131
  renderMode: 'preview' | 'live';
132
132
  }
133
+ export interface AnalyticsEvent {
134
+ type: string;
135
+ source: string;
136
+ productCategory?: string | null;
137
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raydiant/playlist-window-manager",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "Window manager for playlist playback.",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -7,7 +7,8 @@ import {
7
7
  WindowBounds,
8
8
  Theme,
9
9
  Logger,
10
- WorkerState
10
+ WorkerState,
11
+ AnalyticsEvent
11
12
  } from './types';
12
13
  import PresentationWindow from './PresentationWindow';
13
14
  import PlaylistWindowManagerState from './PlaylistWindowManagerState';
@@ -521,6 +522,8 @@ export default class PlaylistWindowManager extends EventEmitter {
521
522
  this.currentWindow.onError(this.handleCurrentPresentationError);
522
523
  this.currentWindow.onEnableInterruptLock(this.handleEnableInterruptLock);
523
524
  this.currentWindow.onDisableInterruptLock(this.handleDisableInterruptLock);
525
+ this.currentWindow.onAnalyticsEvent(this.handleAnalyticsEvent);
526
+
524
527
  await this.currentWindow.load();
525
528
  }
526
529
 
@@ -691,6 +694,7 @@ export default class PlaylistWindowManager extends EventEmitter {
691
694
  this.currentWindow.onDisableInterruptLock(
692
695
  this.handleDisableInterruptLock
693
696
  );
697
+ this.currentWindow.onAnalyticsEvent(this.handleAnalyticsEvent);
694
698
  }
695
699
 
696
700
  // Play new current window.
@@ -754,6 +758,12 @@ export default class PlaylistWindowManager extends EventEmitter {
754
758
  }
755
759
  };
756
760
 
761
+ private handleAnalyticsEvent = (event: AnalyticsEvent) => {
762
+ // Emit the event on the current window. Only the current window should
763
+ // send analytics events since the next window is not visible.
764
+ this.emitCurrentWindowEvent('window_analytics_event', event);
765
+ };
766
+
757
767
  private handleCurrentPresentationComplete = async () => {
758
768
  const { logger } = this.options;
759
769
 
@@ -16,7 +16,8 @@ import {
16
16
  OpenChildWindowOptions,
17
17
  BuilderState,
18
18
  WorkerState,
19
- EmbeddedAppMetadata
19
+ EmbeddedAppMetadata,
20
+ AnalyticsEvent
20
21
  } from './types';
21
22
 
22
23
  export interface PresentationWindowOptions {
@@ -57,6 +58,15 @@ const defaultAppRendererUrl = 'https://apps.raydiant.com/3.0.0/index.html';
57
58
  const isValidNumber = (value: any) =>
58
59
  typeof value === 'number' && !isNaN(value);
59
60
 
61
+ const isValidAnalyticsEvent = (value: any): value is AnalyticsEvent =>
62
+ value &&
63
+ typeof value === 'object' &&
64
+ typeof value.type === 'string' &&
65
+ typeof value.source === 'string' &&
66
+ (typeof value.productCategory === 'string' ||
67
+ value.productCategory === undefined ||
68
+ value.productCategory === null);
69
+
60
70
  export default class PresentationWindow {
61
71
  private didReceiveReady: boolean = false;
62
72
  private readyCallback: (() => void) | null = null;
@@ -76,6 +86,9 @@ export default class PresentationWindow {
76
86
  private isChildWindowLocked: { [windowId: string]: boolean } = {};
77
87
  private enableInterruptLockCallback: (() => void) | null = null;
78
88
  private disableInterruptLockCallback: (() => void) | null = null;
89
+ private analyticsEventCallback:
90
+ | ((event: AnalyticsEvent) => void)
91
+ | null = null;
79
92
 
80
93
  private builderState: BuilderState | undefined;
81
94
  private presentation: Presentation;
@@ -264,6 +277,10 @@ export default class PresentationWindow {
264
277
  this.disableInterruptLockCallback = callback;
265
278
  }
266
279
 
280
+ public onAnalyticsEvent(callback: ((event: AnalyticsEvent) => void) | null) {
281
+ this.analyticsEventCallback = callback;
282
+ }
283
+
267
284
  public isReady() {
268
285
  // TODO: To support the 'ready' event in embedded apps, they will need to tell the
269
286
  // runtime that it should wait for ready instead of DOMLoaded. This could be a "wait
@@ -408,7 +425,7 @@ export default class PresentationWindow {
408
425
  this.logInfo(`Create window '${windowId}' for embedded app url '${url}'`);
409
426
 
410
427
  await this.createAndLoadWindow(
411
- this.appendWindowIdToHash(url, windowId),
428
+ url,
412
429
  // Embedded apps should not have a backgroundColor because some sites expect
413
430
  // the browser default of white will be applied.
414
431
  '#ffffff',
@@ -671,6 +688,9 @@ export default class PresentationWindow {
671
688
  windowManager.on('window_end_error', metadata => {
672
689
  parentWindowManager.emit('window_end_error', metadata);
673
690
  });
691
+ windowManager.on('window_analytics_event', metadata => {
692
+ parentWindowManager.emit('window_analytics_event', metadata);
693
+ });
674
694
  }
675
695
 
676
696
  this.windowManagers[windowName] = windowManager;
@@ -741,15 +761,19 @@ export default class PresentationWindow {
741
761
  }
742
762
 
743
763
  private getEmbeddedAppUrl() {
764
+ const { windowId } = this.options;
765
+
744
766
  const metadata: EmbeddedAppMetadata = {
745
767
  renderMode: this.options.isPreview ? 'preview' : 'live'
746
768
  };
747
769
 
748
- return createEmbeddedApp(
770
+ const url = createEmbeddedApp(
749
771
  this.presentation,
750
772
  this.options.applicationVersion,
751
773
  metadata
752
774
  );
775
+
776
+ return this.appendWindowIdToHash(url, windowId);
753
777
  }
754
778
 
755
779
  private handleEmbeddedMessage = async (type: string, payload?: any) => {
@@ -850,6 +874,13 @@ export default class PresentationWindow {
850
874
  break;
851
875
  }
852
876
 
877
+ case 'analytics-event': {
878
+ if (this.analyticsEventCallback && isValidAnalyticsEvent(payload)) {
879
+ this.analyticsEventCallback(payload);
880
+ }
881
+ break;
882
+ }
883
+
853
884
  default: {
854
885
  this.logWarning(`Received unknown embedded message type: ${type}`);
855
886
  }
package/src/types.ts CHANGED
@@ -156,3 +156,9 @@ export interface InputState {
156
156
  export interface EmbeddedAppMetadata {
157
157
  renderMode: 'preview' | 'live';
158
158
  }
159
+
160
+ export interface AnalyticsEvent {
161
+ type: string;
162
+ source: string;
163
+ productCategory?: string | null;
164
+ }