@raydiant/playlist-window-manager 0.28.0 → 0.28.2-rc1

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.
@@ -4,6 +4,7 @@ export default class IFrameRenderer implements PresentationWindowRenderer {
4
4
  private windows;
5
5
  constructor(container: HTMLElement);
6
6
  createWindow({ windowId, bounds, backgroundColor, receiveMessageCallback, }: CreateWindowOptions): Promise<void>;
7
+ private loadWindowResolvers;
7
8
  loadWindow(windowId: string, url: string): Promise<void>;
8
9
  updateWindowBounds(windowId: string, bounds: WindowBounds): void;
9
10
  showWindow(windowId: string, isFocused: boolean): void;
@@ -42,6 +42,7 @@ var IFrameRenderer = /** @class */ (function () {
42
42
  var _this = this;
43
43
  this.container = container;
44
44
  this.windows = {};
45
+ this.loadWindowResolvers = [];
45
46
  this.globalMessageHandler = function (event) {
46
47
  // Ignore messages that don't start with the message prefix.
47
48
  if (typeof event.data === 'string' && event.data.startsWith(namespace)) {
@@ -106,18 +107,35 @@ var IFrameRenderer = /** @class */ (function () {
106
107
  });
107
108
  };
108
109
  IFrameRenderer.prototype.loadWindow = function (windowId, url) {
110
+ var _this = this;
109
111
  var window = this.getWindow(windowId);
110
112
  if (!window) {
111
113
  throw new Error("Failed to update window bounds, missing window id '".concat(windowId, "'"));
112
114
  }
113
115
  // Return a promise that resolves then the window has successfully loaded.
114
116
  return new Promise(function (resolve, reject) {
115
- window.frame.onload = function () {
116
- resolve();
117
- };
118
- window.frame.onerror = function () {
119
- reject(new Error("Error loading window for '".concat(url, "'")));
120
- };
117
+ _this.loadWindowResolvers.push([resolve, reject]);
118
+ // If we reassign onload, we may lost any pending promise called previously.
119
+ // Basically, if this function is called a first time, then it's called a
120
+ // second time without the first one completes load, the resolve callback
121
+ // will never be called, and the code that is awaiting for the promise will
122
+ // never be fulfilled.
123
+ if (!window.frame.onload) {
124
+ window.frame.onload = function () {
125
+ while (_this.loadWindowResolvers.length) {
126
+ var resolve_1 = (_this.loadWindowResolvers.shift() || [])[0];
127
+ resolve_1 === null || resolve_1 === void 0 ? void 0 : resolve_1();
128
+ }
129
+ };
130
+ }
131
+ if (!window.frame.onerror) {
132
+ window.frame.onerror = function () {
133
+ while (_this.loadWindowResolvers.length) {
134
+ var _a = _this.loadWindowResolvers.shift() || [], _ = _a[0], reject_1 = _a[1];
135
+ reject_1 === null || reject_1 === void 0 ? void 0 : reject_1(new Error("Error loading window for '".concat(url, "'")));
136
+ }
137
+ };
138
+ }
121
139
  window.frame.src = url;
122
140
  });
123
141
  };
@@ -17,6 +17,7 @@ export interface PlaylistWindowManagerOptions {
17
17
  parentWindowId?: string;
18
18
  windowName?: string;
19
19
  isPreview?: boolean;
20
+ closeWindowDelayMs?: number;
20
21
  onEnableInterruptLock?: () => void;
21
22
  onDisableInterruptLock?: () => void;
22
23
  getTerminal?: () => Terminal | null;
@@ -74,6 +74,7 @@ var WindowLoadError_1 = __importDefault(require("./WindowLoadError"));
74
74
  // Wait at least 3 seconds before restarting or moving onto the next window
75
75
  // to prevent log spam causing devices to become unresponsive.
76
76
  var minTimeBetweenCompletes = 3000;
77
+ var delay = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); };
77
78
  var PlaylistWindowManager = /** @class */ (function (_super) {
78
79
  __extends(PlaylistWindowManager, _super);
79
80
  function PlaylistWindowManager(options) {
@@ -123,7 +124,7 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
123
124
  _this.emitCurrentWindowEvent('window_charge_request', request);
124
125
  };
125
126
  _this.handleCurrentPresentationComplete = function () { return __awaiter(_this, void 0, void 0, function () {
126
- var logger, isThrottled, now, timeSinceLastComplete, delay, _a, next, preview, nextPreviewPresentationId;
127
+ var logger, isThrottled, now, timeSinceLastComplete, delay_1, _a, next, preview, nextPreviewPresentationId;
127
128
  var _this = this;
128
129
  return __generator(this, function (_b) {
129
130
  switch (_b.label) {
@@ -137,12 +138,12 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
137
138
  isThrottled = true;
138
139
  // Throttle presentation complete handler if we have not throttled already.
139
140
  if (this.currentPresentationCompleteTimeout === null) {
140
- delay = minTimeBetweenCompletes - timeSinceLastComplete + 1;
141
- logger.warn("Throttling presentation complete handler for ".concat(delay, "ms because it was called again in less than ").concat(minTimeBetweenCompletes, "ms"));
141
+ delay_1 = minTimeBetweenCompletes - timeSinceLastComplete + 1;
142
+ logger.warn("Throttling presentation complete handler for ".concat(delay_1, "ms because it was called again in less than ").concat(minTimeBetweenCompletes, "ms"));
142
143
  this.currentPresentationCompleteTimeout = setTimeout(function () {
143
144
  _this.currentPresentationCompleteTimeout = null;
144
145
  _this.handleCurrentPresentationComplete();
145
- }, delay);
146
+ }, delay_1);
146
147
  }
147
148
  else {
148
149
  logger.warn("Skipping presentation complete because it is currently throttled");
@@ -940,13 +941,19 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
940
941
  if (this.nextWindow) {
941
942
  this.nextWindow.show(this.isFocused);
942
943
  }
943
- if (!this.currentWindow) return [3 /*break*/, 2];
944
- this.currentWindow.hide();
945
- return [4 /*yield*/, this.removeCurrentWindow()];
944
+ if (!this.currentWindow) return [3 /*break*/, 4];
945
+ if (!(this.options.closeWindowDelayMs !== undefined)) return [3 /*break*/, 2];
946
+ return [4 /*yield*/, delay(this.options.closeWindowDelayMs)];
946
947
  case 1:
947
948
  _a.sent();
948
949
  _a.label = 2;
949
950
  case 2:
951
+ this.currentWindow.hide();
952
+ return [4 /*yield*/, this.removeCurrentWindow()];
953
+ case 3:
954
+ _a.sent();
955
+ _a.label = 4;
956
+ case 4:
950
957
  // Swap windows.
951
958
  this.currentWindow = this.nextWindow;
952
959
  if (this.currentWindow) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raydiant/playlist-window-manager",
3
- "version": "0.28.0",
3
+ "version": "0.28.2-rc1",
4
4
  "description": "Window manager for playlist playback.",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -69,6 +69,8 @@ export default class IFrameRenderer implements PresentationWindowRenderer {
69
69
  this.container.appendChild(frame);
70
70
  }
71
71
 
72
+ private loadWindowResolvers: Array<[() => void, (err: Error) => void]> = [];
73
+
72
74
  public loadWindow(windowId: string, url: string) {
73
75
  const window = this.getWindow(windowId);
74
76
  if (!window) {
@@ -79,13 +81,30 @@ export default class IFrameRenderer implements PresentationWindowRenderer {
79
81
 
80
82
  // Return a promise that resolves then the window has successfully loaded.
81
83
  return new Promise<void>((resolve, reject) => {
82
- window.frame.onload = () => {
83
- resolve();
84
- };
84
+ this.loadWindowResolvers.push([resolve, reject]);
85
+
86
+ // If we reassign onload, we may lost any pending promise called previously.
87
+ // Basically, if this function is called a first time, then it's called a
88
+ // second time without the first one completes load, the resolve callback
89
+ // will never be called, and the code that is awaiting for the promise will
90
+ // never be fulfilled.
91
+ if (!window.frame.onload) {
92
+ window.frame.onload = () => {
93
+ while (this.loadWindowResolvers.length) {
94
+ const [resolve] = this.loadWindowResolvers.shift() || [];
95
+ resolve?.();
96
+ }
97
+ };
98
+ }
85
99
 
86
- window.frame.onerror = () => {
87
- reject(new Error(`Error loading window for '${url}'`));
88
- };
100
+ if (!window.frame.onerror) {
101
+ window.frame.onerror = () => {
102
+ while (this.loadWindowResolvers.length) {
103
+ const [_, reject] = this.loadWindowResolvers.shift() || [];
104
+ reject?.(new Error(`Error loading window for '${url}'`));
105
+ }
106
+ };
107
+ }
89
108
 
90
109
  window.frame.src = url;
91
110
  });
@@ -70,6 +70,8 @@ export interface PlaylistWindowManagerOptions {
70
70
  // Set to true to render the presentation window as a preview.
71
71
  isPreview?: boolean;
72
72
 
73
+ closeWindowDelayMs?: number;
74
+
73
75
  // Called when the current window wants to enable the interrupt lock
74
76
  onEnableInterruptLock?: () => void;
75
77
 
@@ -87,6 +89,8 @@ export interface PlaylistWindowManagerOptions {
87
89
  // to prevent log spam causing devices to become unresponsive.
88
90
  const minTimeBetweenCompletes = 3000;
89
91
 
92
+ const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
93
+
90
94
  interface EvalRuleEngineResult {
91
95
  next: RE.NextPresentationID;
92
96
  preview: RE.NextPresentationID;
@@ -750,6 +754,9 @@ export default class PlaylistWindowManager extends EventEmitter {
750
754
  // If current window isn't defined we don't need to hide it but we still
751
755
  // want to make the next window the new current window.
752
756
  if (this.currentWindow) {
757
+ if (this.options.closeWindowDelayMs !== undefined) {
758
+ await delay(this.options.closeWindowDelayMs);
759
+ }
753
760
  this.currentWindow.hide();
754
761
  await this.removeCurrentWindow();
755
762
  }