@applicaster/zapp-react-dom-app 12.0.0-alpha.3964644693 → 12.0.0-alpha.4023252383

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.
@@ -1,6 +1,8 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
 
4
+ import { connectToStore } from "@applicaster/zapp-react-native-redux";
5
+
4
6
  import {
5
7
  QUICK_BRICK_EVENTS,
6
8
  sendQuickBrickEvent,
@@ -35,9 +37,9 @@ import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
35
37
  import { getXray } from "@applicaster/zapp-react-native-utils/logger";
36
38
  import { confirmationDialogStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
37
39
  import {
38
- isVizioPlatform,
39
- isSamsungPlatform,
40
40
  isLgPlatform,
41
+ isSamsungPlatform,
42
+ isVizioPlatform,
41
43
  } from "@applicaster/zapp-react-native-utils/reactUtils";
42
44
 
43
45
  import { OnScreenKeyboard } from "@applicaster/zapp-react-dom-ui-components/Components/OnScreenKeyboard";
@@ -65,597 +67,568 @@ type Props = {
65
67
  };
66
68
  };
67
69
 
68
- type State = { isKeyboardVisible: boolean; keyboardInput: string };
70
+ interface State {
71
+ isKeyboardVisible: boolean;
72
+ keyboardInput: string;
73
+ activeInputRef: HTMLInputElement | null;
74
+ }
69
75
 
70
76
  type KeyCode = { code: string; keyCode: number | string };
71
77
 
72
78
  export const alertConfirmationMesage =
73
79
  "Are you sure you want to exit application?";
74
80
 
75
- export function withInteractionManager(Component: React.FC) {
76
- class InteractionManager extends React.Component<Props, State> {
77
- onKeyDownListener: (keyCode: KeyCode) => void;
78
- onMouseDownListener: (event: MouseEvent) => void;
79
- onMouseMoveListener: (event: MouseEvent) => void;
80
- onScrollListener: (event: MouseEvent) => void;
81
-
82
- longKeyPressTimeout: any | null;
83
- longPressActive: boolean;
84
- timeout: any | null;
85
- sequenceTimeout: NodeJS.Timeout | null; // timeoutID
86
- userSequence: number[] = [];
87
- confirmDialog: any;
88
- isDismissing = false;
81
+ class InteractionManagerClass extends React.Component<Props, State> {
82
+ onKeyDownListener: (keyCode: KeyCode) => void;
83
+ onMouseDownListener: (event: MouseEvent) => void;
84
+ onMouseMoveListener: (event: MouseEvent) => void;
85
+ onScrollListener: (event: MouseEvent) => void;
86
+
87
+ longKeyPressTimeout: any | null;
88
+ longPressActive: boolean;
89
+ timeout: any | null;
90
+ sequenceTimeout: NodeJS.Timeout | null; // timeoutID
91
+ userSequence: number[] = [];
92
+ confirmDialog: any;
93
+ isDismissing = false;
94
+
95
+ constructor(props) {
96
+ super(props);
97
+
98
+ this.onKeyDownListener = this.onKeyDown.bind(this);
99
+
100
+ this.state = {
101
+ isKeyboardVisible: false,
102
+ keyboardInput: "",
103
+ activeInputRef: null,
104
+ };
89
105
 
90
- constructor(props) {
91
- super(props);
106
+ this.onMouseMoveListener = debounce({
107
+ fn: this.onMouseMove,
108
+ context: this,
109
+ });
110
+
111
+ this.onScrollListener = debounce({
112
+ fn: this.onScroll,
113
+ wait: 100,
114
+ context: this,
115
+ });
116
+
117
+ this.onMouseDownListener = debounce({
118
+ fn: this.onMouseDown,
119
+ context: this,
120
+ });
121
+
122
+ this.longKeyPressTimeout = null;
123
+ this.longPressActive = false;
124
+ this.confirmDialog = confirmationDialogStore.getState();
125
+ this.resetHudTimeout = this.resetHudTimeout.bind(this);
126
+ this.cursorVisibilityChange = this.cursorVisibilityChange.bind(this);
127
+ this.onKeyDown = this.onKeyDown.bind(this);
128
+ this.onKeyUp = this.onKeyUp.bind(this);
129
+ this.onMouseMove = this.onMouseMove.bind(this);
130
+ this.onScroll = this.onScroll.bind(this);
131
+ this.onMouseDown = this.onMouseDown.bind(this);
132
+ this.onKeyboardStateChange = this.onKeyboardStateChange.bind(this);
133
+ this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
134
+ this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
135
+ this.performExit = this.performExit.bind(this);
136
+ this.handleInputFocus = this.handleInputFocus.bind(this);
137
+ this.handleInputBlur = this.handleInputBlur.bind(this);
138
+ this.handleKeyboardInput = this.handleKeyboardInput.bind(this);
139
+ this.handleKeyboardDismiss = this.handleKeyboardDismiss.bind(this);
140
+
141
+ this.confirmDialog.setConfirmAction(this.performExit);
142
+ this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
143
+ }
92
144
 
93
- this.onKeyDownListener = this.onKeyDown.bind(this);
145
+ componentDidMount() {
146
+ window.addEventListener("keydown", this.onKeyDownListener);
147
+ window.addEventListener("keyup", this.onKeyUp);
148
+ window.addEventListener("mousemove", this.onMouseMoveListener);
94
149
 
95
- this.state = {
96
- isKeyboardVisible: false,
97
- keyboardInput: "",
98
- };
150
+ document.addEventListener("wheel", this.onScrollListener, {
151
+ passive: true,
152
+ });
99
153
 
100
- this.onMouseMoveListener = debounce({
101
- fn: this.onMouseMove,
102
- context: this,
103
- });
154
+ document.addEventListener("mousedown", this.onMouseDownListener);
104
155
 
105
- this.onScrollListener = debounce({
106
- fn: this.onScroll,
107
- wait: 100,
108
- context: this,
109
- });
156
+ document.addEventListener(
157
+ "keyboardStateChange",
158
+ this.onKeyboardStateChange,
159
+ false
160
+ );
110
161
 
111
- this.onMouseDownListener = debounce({
112
- fn: this.onMouseDown,
113
- context: this,
114
- });
162
+ document.addEventListener("focusin", this.handleInputFocus);
115
163
 
116
- this.longKeyPressTimeout = null;
117
- this.longPressActive = false;
118
- this.confirmDialog = confirmationDialogStore.getState();
119
- this.resetHudTimeout = this.resetHudTimeout.bind(this);
120
- this.cursorVisibilityChange = this.cursorVisibilityChange.bind(this);
121
- this.onKeyDown = this.onKeyDown.bind(this);
122
- this.onKeyUp = this.onKeyUp.bind(this);
123
- this.onMouseMove = this.onMouseMove.bind(this);
124
- this.onScroll = this.onScroll.bind(this);
125
- this.onMouseDown = this.onMouseDown.bind(this);
126
- this.onKeyboardStateChange = this.onKeyboardStateChange.bind(this);
127
- this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
128
- this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
129
- this.performExit = this.performExit.bind(this);
130
- this.handleInputFocus = this.handleInputFocus.bind(this);
131
- this.handleInputBlur = this.handleInputBlur.bind(this);
132
- this.handleKeyboardInput = this.handleKeyboardInput.bind(this);
133
- this.handleKeyboardDismiss = this.handleKeyboardDismiss.bind(this);
134
-
135
- this.confirmDialog.setConfirmAction(this.performExit);
136
- this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
137
- }
138
-
139
- componentDidMount() {
140
- window.addEventListener("keydown", this.onKeyDownListener);
141
- window.addEventListener("keyup", this.onKeyUp);
142
- window.addEventListener("mousemove", this.onMouseMoveListener);
164
+ document.addEventListener(
165
+ "cursorStateChange",
166
+ this.cursorVisibilityChange,
167
+ false
168
+ );
169
+ }
143
170
 
144
- document.addEventListener("wheel", this.onScrollListener, {
145
- passive: true,
146
- });
171
+ componentWillUnmount() {
172
+ window.removeEventListener("keydown", this.onKeyDownListener);
173
+ window.removeEventListener("keyup", this.onKeyUp);
174
+ document.removeEventListener("wheel", this.onScrollListener);
147
175
 
148
- document.addEventListener("mousedown", this.onMouseDownListener);
176
+ document.removeEventListener(
177
+ "cursorStateChange",
178
+ this.cursorVisibilityChange
179
+ );
149
180
 
150
- document.addEventListener(
151
- "keyboardStateChange",
152
- this.onKeyboardStateChange,
153
- false
154
- );
181
+ document.removeEventListener("focusin", this.handleInputFocus);
155
182
 
156
- document.addEventListener("focusin", this.handleInputFocus);
183
+ document.removeEventListener("mousedown", this.onMouseDownListener);
157
184
 
158
- document.addEventListener(
159
- "cursorStateChange",
160
- this.cursorVisibilityChange,
161
- false
162
- );
185
+ if (this.timeout) {
186
+ this.clearTimeout();
163
187
  }
164
188
 
165
- componentWillUnmount() {
166
- window.removeEventListener("keydown", this.onKeyDownListener);
167
- window.removeEventListener("keyup", this.onKeyUp);
168
- document.removeEventListener("wheel", this.onScrollListener);
169
-
170
- document.removeEventListener(
171
- "cursorStateChange",
172
- this.cursorVisibilityChange
173
- );
174
-
175
- document.removeEventListener("focusin", this.handleInputFocus);
176
-
177
- document.removeEventListener("mousedown", this.onMouseDownListener);
178
-
179
- if (this.timeout) {
180
- this.clearTimeout();
181
- }
189
+ this.isDismissing = false;
190
+ }
182
191
 
183
- this.isDismissing = false;
192
+ performExit() {
193
+ if (isSamsungPlatform()) {
194
+ this.onConfirmDialogClose();
195
+ sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND);
184
196
  }
185
197
 
186
- performExit() {
187
- if (isSamsungPlatform()) {
188
- this.onConfirmDialogClose();
189
- sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND);
190
- }
191
-
192
- if (isLgPlatform()) {
193
- // webOS recommend to use this method when implemention a custom exit
194
- globalAny.close();
195
- }
198
+ if (isLgPlatform()) {
199
+ // webOS recommend to use this method when implemention a custom exit
200
+ globalAny.close();
201
+ }
196
202
 
197
- if (isVizioPlatform()) {
198
- window?.VIZIO?.exitApplication();
199
- }
203
+ if (isVizioPlatform()) {
204
+ window?.VIZIO?.exitApplication();
200
205
  }
206
+ }
201
207
 
202
- handleWebOsBack() {
203
- const systemBack = globalAny?.webOS?.platformBack;
208
+ handleWebOsBack() {
209
+ const systemBack = globalAny?.webOS?.platformBack;
204
210
 
205
- if (systemBack) {
206
- systemBack();
207
- } else {
208
- /**
209
- * In case webOS failed to show the exit pop-up, we show a regular promtp as fallback
210
- */
211
- showConfirmationDialog({
212
- title: "",
213
- message: alertConfirmationMesage,
214
- confirmCompletion: this.performExit,
215
- });
216
- }
211
+ if (systemBack) {
212
+ systemBack();
213
+ } else {
214
+ /**
215
+ * In case webOS failed to show the exit pop-up, we show a regular promtp as fallback
216
+ */
217
+ showConfirmationDialog({
218
+ title: "",
219
+ message: alertConfirmationMesage,
220
+ confirmCompletion: this.performExit,
221
+ });
217
222
  }
223
+ }
218
224
 
219
- /**
220
- * This methods sends the app to background in whatever way is appropriate for each platform
221
- * In the case of Tizen it shows a custom dialog on confirm it exits the app
222
- * WebOS behavior depends on the SDK version, WebOS 4 would pull up the launcher menu
223
- * following versions would show a native exit prompt
224
- */
225
- platformBack() {
226
- try {
227
- // TODO: temporary hack until this code is refactored
228
- if (isSamsungPlatform() || isVizioPlatform()) {
229
- const { isDialogVisible } = confirmationDialogStore.getState();
230
-
231
- if (isDialogVisible) {
232
- this.onConfirmDialogClose();
233
- } else {
234
- showConfirmationDialog({
235
- title: "",
236
- message: alertConfirmationMesage,
237
- confirmCompletion: this.performExit,
238
- cancelCompletion: this.onConfirmDialogClose,
239
- });
240
- }
241
- } else if (isLgPlatform()) {
242
- this.handleWebOsBack();
225
+ /**
226
+ * This methods sends the app to background in whatever way is appropriate for each platform
227
+ * In the case of Tizen it shows a custom dialog on confirm it exits the app
228
+ * WebOS behavior depends on the SDK version, WebOS 4 would pull up the launcher menu
229
+ * following versions would show a native exit prompt
230
+ */
231
+ platformBack() {
232
+ try {
233
+ // TODO: temporary hack until this code is refactored
234
+ if (isSamsungPlatform() || isVizioPlatform()) {
235
+ const { isDialogVisible } = confirmationDialogStore.getState();
236
+
237
+ if (isDialogVisible) {
238
+ this.onConfirmDialogClose();
239
+ } else {
240
+ showConfirmationDialog({
241
+ title: "",
242
+ message: alertConfirmationMesage,
243
+ confirmCompletion: this.performExit,
244
+ cancelCompletion: this.onConfirmDialogClose,
245
+ });
243
246
  }
244
- } catch (e) {
245
- // eslint-disable-next-line no-console
246
- console.warn("Failed to execute platformBack", e);
247
+ } else if (isLgPlatform()) {
248
+ this.handleWebOsBack();
247
249
  }
250
+ } catch (e) {
251
+ // eslint-disable-next-line no-console
252
+ console.warn("Failed to execute platformBack", e);
248
253
  }
254
+ }
249
255
 
250
- cursorVisibilityChange(event) {
251
- const visible = event.detail.visibility;
252
- const { setDisplayState, displayState } = this.props;
256
+ cursorVisibilityChange(event) {
257
+ const visible = event.detail.visibility;
258
+ const { setDisplayState, displayState } = this.props;
253
259
 
254
- if (visible && displayState === DISPLAY_STATES.PLAYER) {
255
- setDisplayState(DISPLAY_STATES.HUD);
256
- this.resetHudTimeout();
257
- }
260
+ if (visible && displayState === DISPLAY_STATES.PLAYER) {
261
+ setDisplayState(DISPLAY_STATES.HUD);
262
+ this.resetHudTimeout();
258
263
  }
264
+ }
259
265
 
260
- onMouseMove() {
261
- const { setDisplayState, displayState } = this.props;
266
+ onMouseMove() {
267
+ const { setDisplayState, displayState } = this.props;
262
268
 
263
- if (displayState === DISPLAY_STATES.PLAYER) {
264
- setDisplayState(DISPLAY_STATES.HUD);
265
- this.resetHudTimeout();
266
- }
269
+ if (displayState === DISPLAY_STATES.PLAYER) {
270
+ setDisplayState(DISPLAY_STATES.HUD);
271
+ this.resetHudTimeout();
267
272
  }
273
+ }
268
274
 
269
- isHomeScreen() {
270
- const { navigator } = this.props;
271
- const { rivers } = this.props;
275
+ isHomeScreen() {
276
+ const { navigator } = this.props;
277
+ const { rivers } = this.props;
272
278
 
273
- const homeId = R.compose(
274
- R.prop("id"),
275
- R.find(R.prop("home")),
276
- R.values
277
- )(rivers);
279
+ const homeId = R.compose(
280
+ R.prop("id"),
281
+ R.find(R.prop("home")),
282
+ R.values
283
+ )(rivers);
278
284
 
279
- const homePath = `/river/${homeId}`;
285
+ const homePath = `/river/${homeId}`;
280
286
 
281
- return homePath === navigator.currentRoute;
282
- }
287
+ return homePath === navigator.currentRoute;
288
+ }
283
289
 
284
- goToHome() {
285
- const { navigator } = this.props;
286
- const { rivers } = this.props;
287
- const homeRiver = R.find(R.propEq("home", true), R.values(rivers));
290
+ goToHome() {
291
+ const { navigator } = this.props;
292
+ const { rivers } = this.props;
293
+ const homeRiver = R.find(R.propEq("home", true), R.values(rivers));
288
294
 
289
- navigator.replace(homeRiver);
290
- }
295
+ navigator.replace(homeRiver);
296
+ }
291
297
 
292
- resetHudTimeout() {
293
- const { resetHudTimer = noop } = this.props;
298
+ resetHudTimeout() {
299
+ const { resetHudTimer = noop } = this.props;
294
300
 
295
- resetHudTimer?.();
296
- }
301
+ resetHudTimer?.();
302
+ }
297
303
 
298
- clearTimeout() {
299
- if (this.timeout) {
300
- clearTimeout(this.timeout);
301
- this.timeout = null;
302
- }
304
+ clearTimeout() {
305
+ if (this.timeout) {
306
+ clearTimeout(this.timeout);
307
+ this.timeout = null;
303
308
  }
309
+ }
304
310
 
305
- clearLongPressTimeout() {
306
- if (this.longKeyPressTimeout) {
307
- clearTimeout(this.longKeyPressTimeout);
308
- this.longKeyPressTimeout = null;
309
- }
311
+ clearLongPressTimeout() {
312
+ if (this.longKeyPressTimeout) {
313
+ clearTimeout(this.longKeyPressTimeout);
314
+ this.longKeyPressTimeout = null;
310
315
  }
316
+ }
311
317
 
312
- /**
313
- * If the keySupportsLongPress this method is called on longpress of key
314
- * Register your longpress handlers here, and make sure to add it to list of supported keys
315
- */
316
- onLongPress(event) {
317
- this.longPressActive = true;
318
- // Legacy way of determining whether controls are being displayed
319
- // Or if the player is in a playback state.
320
- const { displayState } = this.props;
321
- const playerState = displayState === DISPLAY_STATES.PLAYER;
322
- const hudState = displayState === DISPLAY_STATES.HUD;
323
- const playbackState = playerState || hudState;
324
-
325
- switch (event.code) {
326
- case KEYS.Back.code:
327
- // If you longpress the back button we probably want to exit
328
- this.platformBack();
318
+ /**
319
+ * If the keySupportsLongPress this method is called on longpress of key
320
+ * Register your longpress handlers here, and make sure to add it to list of supported keys
321
+ */
322
+ onLongPress(event) {
323
+ this.longPressActive = true;
324
+ // Legacy way of determining whether controls are being displayed
325
+ // Or if the player is in a playback state.
326
+ const { displayState } = this.props;
327
+ const playerState = displayState === DISPLAY_STATES.PLAYER;
328
+ const hudState = displayState === DISPLAY_STATES.HUD;
329
+ const playbackState = playerState || hudState;
330
+
331
+ switch (event.code) {
332
+ case KEYS.Back.code:
333
+ // If you longpress the back button we probably want to exit
334
+ this.platformBack();
329
335
 
330
- break;
336
+ break;
331
337
 
332
- case KEYS.Enter.code:
333
- focusManager.longPress();
338
+ case KEYS.Enter.code:
339
+ focusManager.longPress();
334
340
 
335
- break;
341
+ break;
336
342
 
337
- case KEYS.Forward.code:
338
- playbackState && this.props.seekMethods.onForwardLongPress();
343
+ case KEYS.Forward.code:
344
+ playbackState && this.props.seekMethods.onForwardLongPress();
339
345
 
340
- break;
346
+ break;
341
347
 
342
- case KEYS.Rewind.code:
343
- playbackState && this.props.seekMethods.onRewindLongPress();
348
+ case KEYS.Rewind.code:
349
+ playbackState && this.props.seekMethods.onRewindLongPress();
344
350
 
345
- break;
351
+ break;
346
352
 
347
- default:
348
- break;
349
- }
353
+ default:
354
+ break;
350
355
  }
356
+ }
351
357
 
352
- /**
353
- * If the keySupportsLongPress this method is called on keyup of a longpress key
354
- * If you need to handle the release or keyup of a longpress key register the handler here
355
- */
356
- onLongPressRelease(event) {
357
- this.longPressActive = false;
358
- this.resetHudTimeout();
358
+ /**
359
+ * If the keySupportsLongPress this method is called on keyup of a longpress key
360
+ * If you need to handle the release or keyup of a longpress key register the handler here
361
+ */
362
+ onLongPressRelease(event) {
363
+ this.longPressActive = false;
364
+ this.resetHudTimeout();
359
365
 
360
- focusManager.pressOut();
366
+ focusManager.pressOut();
361
367
 
362
- switch (event.code) {
363
- case KEYS.Forward.code:
364
- this.props.seekMethods.onForwardPressOut();
368
+ switch (event.code) {
369
+ case KEYS.Forward.code:
370
+ this.props.seekMethods.onForwardPressOut();
365
371
 
366
- break;
372
+ break;
367
373
 
368
- case KEYS.Rewind.code:
369
- this.props.seekMethods.onForwardPressOut();
374
+ case KEYS.Rewind.code:
375
+ this.props.seekMethods.onForwardPressOut();
370
376
 
371
- break;
377
+ break;
372
378
 
373
- default:
374
- break;
375
- }
379
+ default:
380
+ break;
376
381
  }
382
+ }
377
383
 
378
- /**
379
- * Resets the sequence of keys we captured.
380
- * Clear ensures that we don't interpret regular interactions
381
- * as a sequence used to unlock certain features
382
- */
383
- resetSequenceTimeout() {
384
- const SEQUENCE_ENTRY_TIME = 1900;
385
-
386
- if (this.sequenceTimeout) {
387
- // Start timer over
388
- clearTimeout(this.sequenceTimeout);
389
- }
390
-
391
- this.sequenceTimeout = setTimeout(() => {
392
- // User needs to complete sequence before SEQUENCE_ENTRY_TIME
393
- // or we set userSequence, and prev sequenceTimeout to empty values
394
- this.sequenceTimeout = null;
395
- this.userSequence = [];
396
- }, SEQUENCE_ENTRY_TIME);
384
+ /**
385
+ * Resets the sequence of keys we captured.
386
+ * Clear ensures that we don't interpret regular interactions
387
+ * as a sequence used to unlock certain features
388
+ */
389
+ resetSequenceTimeout() {
390
+ const SEQUENCE_ENTRY_TIME = 1900;
391
+
392
+ if (this.sequenceTimeout) {
393
+ // Start timer over
394
+ clearTimeout(this.sequenceTimeout);
397
395
  }
398
396
 
399
- /**
400
- * If a certain sequence of keys are pressed we enable special menus or settings
401
- * Each supported sequence can be stored as a constant
402
- */
403
- checkSequence(key) {
404
- const SINK_SEQUENCE = [
405
- KEYS.ArrowUp.keyCode,
406
- KEYS.ArrowUp.keyCode,
407
- KEYS.ArrowDown.keyCode,
408
- KEYS.ArrowDown.keyCode,
409
- KEYS.ArrowLeft.keyCode,
410
- KEYS.ArrowRight.keyCode,
411
- KEYS.ArrowLeft.keyCode,
412
- KEYS.ArrowRight.keyCode,
413
- ];
414
-
415
- this.resetSequenceTimeout();
416
- this.userSequence.push(key);
417
-
418
- // Remember [1,2,3] !== [1,2,3] in js, so string comparison is better
419
- const shouldAddSink =
420
- this.userSequence.toString() === SINK_SEQUENCE.toString();
421
-
422
- if (shouldAddSink) {
423
- this.props.xray.addRemoteSink({});
424
- }
397
+ this.sequenceTimeout = setTimeout(() => {
398
+ // User needs to complete sequence before SEQUENCE_ENTRY_TIME
399
+ // or we set userSequence, and prev sequenceTimeout to empty values
400
+ this.sequenceTimeout = null;
401
+ this.userSequence = [];
402
+ }, SEQUENCE_ENTRY_TIME);
403
+ }
404
+
405
+ /**
406
+ * If a certain sequence of keys are pressed we enable special menus or settings
407
+ * Each supported sequence can be stored as a constant
408
+ */
409
+ checkSequence(key) {
410
+ const SINK_SEQUENCE = [
411
+ KEYS.ArrowUp.keyCode,
412
+ KEYS.ArrowUp.keyCode,
413
+ KEYS.ArrowDown.keyCode,
414
+ KEYS.ArrowDown.keyCode,
415
+ KEYS.ArrowLeft.keyCode,
416
+ KEYS.ArrowRight.keyCode,
417
+ KEYS.ArrowLeft.keyCode,
418
+ KEYS.ArrowRight.keyCode,
419
+ ];
420
+
421
+ this.resetSequenceTimeout();
422
+ this.userSequence.push(key);
423
+
424
+ // Remember [1,2,3] !== [1,2,3] in js, so string comparison is better
425
+ const shouldAddSink =
426
+ this.userSequence.toString() === SINK_SEQUENCE.toString();
427
+
428
+ if (shouldAddSink) {
429
+ this.props.xray.addRemoteSink({});
425
430
  }
431
+ }
426
432
 
427
- /**
428
- * Determines whether the key supports or handles longpress
429
- * At the moment the only keys that support are the ones defined in
430
- * supportedEvents array. This logic just prevents us from registering timeouts for every key
431
- */
432
- keySupportsLongPress(event): boolean {
433
- const supportedEvents = [
434
- KEYS.Back,
435
- KEYS.Enter,
436
- KEYS.Rewind,
437
- KEYS.Forward,
438
- ];
433
+ /**
434
+ * Determines whether the key supports or handles longpress
435
+ * At the moment the only keys that support are the ones defined in
436
+ * supportedEvents array. This logic just prevents us from registering timeouts for every key
437
+ */
438
+ keySupportsLongPress(event): boolean {
439
+ const supportedEvents = [KEYS.Back, KEYS.Enter, KEYS.Rewind, KEYS.Forward];
439
440
 
440
- const eventMatcher = R.compose(R.prop("matches"), keyCode)(event);
441
+ const eventMatcher = R.compose(R.prop("matches"), keyCode)(event);
441
442
 
442
- const supportsLongPress = R.compose(
443
- R.any(R.equals(true)),
444
- R.map(eventMatcher)
445
- )(supportedEvents);
443
+ const supportsLongPress = R.compose(
444
+ R.any(R.equals(true)),
445
+ R.map(eventMatcher)
446
+ )(supportedEvents);
446
447
 
447
- return supportsLongPress;
448
- }
448
+ return supportsLongPress;
449
+ }
449
450
 
450
- onKeyUp(event: KeyboardEvent) {
451
- // If this is a supported longpress key we need to clear the timeout and handle release of key
452
- // This logic is handled by onLongPressRelease
453
- if (this.keySupportsLongPress(event)) {
454
- if (this.longPressActive) {
455
- this.onLongPressRelease(event);
456
- } else {
457
- this.clearLongPressTimeout();
458
- }
451
+ onKeyUp(event: KeyboardEvent) {
452
+ // If this is a supported longpress key we need to clear the timeout and handle release of key
453
+ // This logic is handled by onLongPressRelease
454
+ if (this.keySupportsLongPress(event)) {
455
+ if (this.longPressActive) {
456
+ this.onLongPressRelease(event);
457
+ } else {
458
+ this.clearLongPressTimeout();
459
459
  }
460
460
  }
461
+ }
461
462
 
462
- onKeyDown(event) {
463
- const { displayState, setDisplayState, navigator } = this.props;
464
- const { isKeyboardVisible } = this.state;
463
+ onKeyDown(event) {
464
+ const { displayState, setDisplayState, navigator } = this.props;
465
+ const { isKeyboardVisible } = this.state;
465
466
 
466
- const { isDialogVisible } = confirmationDialogStore.getState();
467
+ const { isDialogVisible } = confirmationDialogStore.getState();
467
468
 
468
- // If this is a supported longpress key we need to set the timeout and handle the longpress
469
- if (this.keySupportsLongPress(event)) {
470
- this.longKeyPressTimeout = setTimeout(() => {
471
- this.onLongPress(event);
472
- }, LONG_KEY_PRESS_TIMEOUT);
469
+ if (isKeyboardVisible && shouldUseOnScreenKeyboard()) {
470
+ if (keyCode(event).matchesAny("Escape", "Back")) {
471
+ this.handleKeyboardDismiss();
473
472
  }
474
473
 
475
- if (isKeyboardVisible && shouldUseOnScreenKeyboard()) {
476
- if (keyCode(event).matchesAny("Escape", "Back")) {
477
- event.preventDefault();
478
- event.stopPropagation();
479
- this.handleKeyboardDismiss();
474
+ event.preventDefault();
475
+ event.stopPropagation();
480
476
 
481
- return;
482
- }
483
- }
477
+ return;
478
+ }
484
479
 
480
+ // If this is a supported longpress key we need to set the timeout and handle the longpress
481
+ if (this.keySupportsLongPress(event)) {
482
+ this.longKeyPressTimeout = setTimeout(() => {
483
+ this.onLongPress(event);
484
+ }, LONG_KEY_PRESS_TIMEOUT);
485
+ }
486
+
487
+ /**
488
+ When the player is displayed, we listen to Native Play / Pause / Stop
489
+ buttons. Any other key press will display the transport controls
490
+ */
491
+ if (
492
+ displayState === DISPLAY_STATES.PLAYER ||
493
+ displayState === DISPLAY_STATES.HUD
494
+ ) {
485
495
  /**
486
- When the player is displayed, we listen to Native Play / Pause / Stop
487
- buttons. Any other key press will display the transport controls
496
+ registering native keys for playback
488
497
  */
489
- if (
490
- displayState === DISPLAY_STATES.PLAYER ||
491
- displayState === DISPLAY_STATES.HUD
492
- ) {
493
- /**
494
- registering native keys for playback
495
- */
496
- if (keyCode(event).matches(KEYS.TogglePlayPause)) {
497
- const { playing } = playerManager.getState() || {};
498
-
499
- if (!playing) {
500
- return playerManager.togglePlayPause();
501
- }
502
- }
498
+ if (keyCode(event).matches(KEYS.TogglePlayPause)) {
499
+ const { playing } = playerManager.getState() || {};
503
500
 
504
- if (keyCode(event).matches(KEYS.Play)) {
505
- return playerManager.play();
501
+ if (!playing) {
502
+ return playerManager.togglePlayPause();
506
503
  }
504
+ }
507
505
 
508
- if (keyCode(event).matches(KEYS.Pause)) {
509
- setDisplayState(DISPLAY_STATES.HUD);
506
+ if (keyCode(event).matches(KEYS.Play)) {
507
+ return playerManager.play();
508
+ }
510
509
 
511
- return playerManager.pause();
512
- }
510
+ if (keyCode(event).matches(KEYS.Pause)) {
511
+ setDisplayState(DISPLAY_STATES.HUD);
513
512
 
514
- if (keyCode(event).matches(KEYS.Stop)) {
515
- setDisplayState(DISPLAY_STATES.DEFAULT);
516
- navigator.goBack();
513
+ return playerManager.pause();
514
+ }
517
515
 
518
- return;
519
- }
516
+ if (keyCode(event).matches(KEYS.Stop)) {
517
+ setDisplayState(DISPLAY_STATES.DEFAULT);
518
+ navigator.goBack();
520
519
 
521
- if (keyCode(event).matches(KEYS.Forward)) {
522
- setDisplayState(DISPLAY_STATES.HUD);
523
- this.props.seekMethods.onForwardPress();
520
+ return;
521
+ }
524
522
 
525
- return;
526
- }
523
+ if (keyCode(event).matches(KEYS.Forward)) {
524
+ setDisplayState(DISPLAY_STATES.HUD);
525
+ this.props.seekMethods.onForwardPress();
527
526
 
528
- if (keyCode(event).matches(KEYS.Rewind)) {
529
- setDisplayState(DISPLAY_STATES.HUD);
530
- this.props.seekMethods.onRewindPress();
527
+ return;
528
+ }
531
529
 
532
- return;
533
- }
530
+ if (keyCode(event).matches(KEYS.Rewind)) {
531
+ setDisplayState(DISPLAY_STATES.HUD);
532
+ this.props.seekMethods.onRewindPress();
533
+
534
+ return;
534
535
  }
536
+ }
535
537
 
536
- /**
537
- When pressing backspace or the Native "Back" button:
538
- if on the transport controls: we show the player
539
- if on the player : we show the UI
540
- if on the UI : we exit the app
541
- */
538
+ /**
539
+ When pressing backspace or the Native "Back" button:
540
+ if on the transport controls: we show the player
541
+ if on the player : we show the UI
542
+ if on the UI : we exit the app
543
+ */
542
544
 
543
- if (
544
- keyCode(event).matchesAny(
545
- KEYS.Back,
546
- KEYS.Escape,
547
- KEYS.EscapeDevice,
548
- KEYS.Backspace
549
- )
550
- ) {
551
- switch (displayState) {
552
- case DISPLAY_STATES.DEFAULT:
553
- case DISPLAY_STATES.PLAYER:
554
- if (isDialogVisible) {
555
- this.onConfirmDialogClose();
556
- } else if (navigator.canGoBack()) {
557
- navigator.goBack();
558
- } else if (this.isHomeScreen()) {
545
+ if (
546
+ keyCode(event).matchesAny(
547
+ KEYS.Back,
548
+ KEYS.Escape,
549
+ KEYS.EscapeDevice,
550
+ KEYS.Backspace
551
+ )
552
+ ) {
553
+ switch (displayState) {
554
+ case DISPLAY_STATES.DEFAULT:
555
+ case DISPLAY_STATES.PLAYER:
556
+ if (isDialogVisible) {
557
+ this.onConfirmDialogClose();
558
+ } else if (navigator.canGoBack()) {
559
+ navigator.goBack();
560
+ } else if (this.isHomeScreen()) {
561
+ this.platformBack();
562
+ } else if (navigator.currentRoute.includes("hook")) {
563
+ // If code reaches this block navigator cant go back, it is not home,
564
+ // and this is a login plugin that was pushed on top of the home
565
+ // Treat it like a home screen and run platformBack()
566
+ const entryData = navigator?.data?.entry;
567
+ const isHookInHomescreen = entryData?.payload?.home;
568
+
569
+ const isHookFlowBlocker =
570
+ entryData?.hookPlugin?.module?.isFlowBlocker();
571
+
572
+ if (isHookInHomescreen && isHookFlowBlocker) {
559
573
  this.platformBack();
560
- } else if (navigator.currentRoute.includes("hook")) {
561
- // If code reaches this block navigator cant go back, it is not home,
562
- // and this is a login plugin that was pushed on top of the home
563
- // Treat it like a home screen and run platformBack()
564
- const entryData = navigator?.data?.entry;
565
- const isHookInHomescreen = entryData?.payload?.home;
566
-
567
- const isHookFlowBlocker =
568
- entryData?.hookPlugin?.module?.isFlowBlocker();
569
-
570
- if (isHookInHomescreen && isHookFlowBlocker) {
571
- this.platformBack();
572
- } else {
573
- navigator.goBack(false, true);
574
- }
575
574
  } else {
576
- this.goToHome();
575
+ navigator.goBack(false, true);
577
576
  }
577
+ } else {
578
+ this.goToHome();
579
+ }
578
580
 
579
- break;
580
- case DISPLAY_STATES.HUD:
581
- // IF transport controls are open, close them instead of closing player
582
- setDisplayState(DISPLAY_STATES.PLAYER);
583
- break;
584
- default:
585
- setDisplayState(DISPLAY_STATES.PLAYER);
586
- break;
587
- }
581
+ break;
582
+ case DISPLAY_STATES.HUD:
583
+ // IF transport controls are open, close them instead of closing player
584
+ setDisplayState(DISPLAY_STATES.PLAYER);
585
+ break;
586
+ default:
587
+ setDisplayState(DISPLAY_STATES.PLAYER);
588
+ break;
588
589
  }
590
+ }
589
591
 
590
- if (keyCode(event).matches(KEYS.Enter)) {
591
- if (displayState === DISPLAY_STATES.PLAYER) {
592
- setDisplayState(DISPLAY_STATES.HUD);
593
- } else {
594
- focusManager.press();
595
- }
596
-
597
- return true;
592
+ if (keyCode(event).matches(KEYS.Enter)) {
593
+ if (displayState === DISPLAY_STATES.PLAYER) {
594
+ setDisplayState(DISPLAY_STATES.HUD);
595
+ } else {
596
+ focusManager.press();
598
597
  }
599
598
 
600
- if (keyCode(event).matches(KEYS.Exit)) {
601
- if (isSamsungPlatform() || shouldUseOnScreenKeyboard()) {
602
- this.performExit();
599
+ return true;
600
+ }
603
601
 
604
- return;
605
- }
602
+ if (keyCode(event).matches(KEYS.Exit)) {
603
+ if (isSamsungPlatform() || shouldUseOnScreenKeyboard()) {
604
+ this.performExit();
606
605
 
607
- this.platformBack();
606
+ return;
608
607
  }
609
608
 
610
- if (
611
- keyCode(event).matchesAny(...ARROW_KEYS) &&
612
- !this.state.isKeyboardVisible
613
- ) {
614
- if (displayState === DISPLAY_STATES.PLAYER) {
615
- setDisplayState(DISPLAY_STATES.HUD);
616
- }
617
-
618
- this.checkSequence(event.keyCode);
619
-
620
- const direction = keyCode(event).direction();
621
-
622
- if (!isDialogVisible || !["up", "down"].includes(direction.value)) {
623
- focusManager.moveFocus(direction);
624
- }
625
-
626
- if (!isDialogVisible) {
627
- // run focus recovery on user interaction attempt
628
- // probably we failed to set initial focus for some reason
629
- focusManager.recoverFocus();
630
- }
631
-
632
- if (
633
- displayState === DISPLAY_STATES.PLAYER ||
634
- displayState === DISPLAY_STATES.HUD
635
- ) {
636
- this.resetHudTimeout();
637
- }
609
+ this.platformBack();
610
+ }
638
611
 
639
- return true;
612
+ if (
613
+ keyCode(event).matchesAny(...ARROW_KEYS) &&
614
+ !this.state.isKeyboardVisible
615
+ ) {
616
+ if (displayState === DISPLAY_STATES.PLAYER) {
617
+ setDisplayState(DISPLAY_STATES.HUD);
640
618
  }
641
- }
642
619
 
643
- onScroll(event) {
644
- const { displayState } = this.props;
620
+ this.checkSequence(event.keyCode);
645
621
 
646
- const deltaY = event?.deltaY;
622
+ const direction = keyCode(event).direction();
647
623
 
648
- const DIRECTION = {
649
- axis: "y",
650
- isHorizontal: false,
651
- isVertical: true,
652
- value: "",
653
- };
624
+ if (!isDialogVisible || !["up", "down"].includes(direction.value)) {
625
+ focusManager.moveFocus(direction);
626
+ }
654
627
 
655
- if (deltaY > 0) {
656
- DIRECTION.value = isLgPlatform() ? "down" : "up";
657
- } else if (deltaY < 0) {
658
- DIRECTION.value = isLgPlatform() ? "up" : "down";
628
+ if (!isDialogVisible) {
629
+ // run focus recovery on user interaction attempt
630
+ // probably we failed to set initial focus for some reason
631
+ focusManager.recoverFocus();
659
632
  }
660
633
 
661
634
  if (
@@ -663,112 +636,156 @@ export function withInteractionManager(Component: React.FC) {
663
636
  displayState === DISPLAY_STATES.HUD
664
637
  ) {
665
638
  this.resetHudTimeout();
666
- } else {
667
- focusManager.moveFocus(DIRECTION);
668
639
  }
640
+
641
+ return true;
669
642
  }
643
+ }
670
644
 
671
- onMouseDown(event) {
672
- const { button } = event;
673
- const { displayState } = this.props;
645
+ onScroll(event) {
646
+ const { displayState } = this.props;
674
647
 
675
- if (
676
- button === 1 && // number 1 represents press on the scroll wheel
677
- (displayState === DISPLAY_STATES.PLAYER ||
678
- displayState === DISPLAY_STATES.HUD)
679
- ) {
680
- this.resetHudTimeout();
681
- }
682
- }
648
+ const deltaY = event?.deltaY;
683
649
 
684
- onKeyboardStateChange(event) {
685
- this.setState({ isKeyboardVisible: event.visibility });
650
+ const DIRECTION = {
651
+ axis: "y",
652
+ isHorizontal: false,
653
+ isVertical: true,
654
+ value: "",
655
+ };
656
+
657
+ if (deltaY > 0) {
658
+ DIRECTION.value = isLgPlatform() ? "down" : "up";
659
+ } else if (deltaY < 0) {
660
+ DIRECTION.value = isLgPlatform() ? "up" : "down";
686
661
  }
687
662
 
688
- onConfirmDialogOpen() {
689
- this.confirmDialog.showDialog();
663
+ if (
664
+ displayState === DISPLAY_STATES.PLAYER ||
665
+ displayState === DISPLAY_STATES.HUD
666
+ ) {
667
+ this.resetHudTimeout();
668
+ } else {
669
+ focusManager.moveFocus(DIRECTION);
690
670
  }
671
+ }
691
672
 
692
- onConfirmDialogClose() {
693
- this.confirmDialog.hideDialog();
673
+ onMouseDown(event) {
674
+ const { button } = event;
675
+ const { displayState } = this.props;
694
676
 
695
- // restore initial focus after closing dialog
696
- focusManager.setInitialFocus();
677
+ if (
678
+ button === 1 && // number 1 represents press on the scroll wheel
679
+ (displayState === DISPLAY_STATES.PLAYER ||
680
+ displayState === DISPLAY_STATES.HUD)
681
+ ) {
682
+ this.resetHudTimeout();
697
683
  }
684
+ }
698
685
 
699
- handleInputFocus = (event: FocusEvent) => {
700
- if (!shouldUseOnScreenKeyboard()) return;
701
- const target = event.target as HTMLElement;
686
+ onKeyboardStateChange(event) {
687
+ this.setState({ isKeyboardVisible: event.visibility });
688
+ }
702
689
 
703
- if (this.isDismissing) {
704
- return;
705
- }
690
+ onConfirmDialogOpen() {
691
+ this.confirmDialog.showDialog();
692
+ }
706
693
 
707
- if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
708
- this.setState({
709
- isKeyboardVisible: true,
710
- keyboardInput: (target as HTMLInputElement).value || "",
711
- });
712
- }
713
- };
694
+ onConfirmDialogClose() {
695
+ this.confirmDialog.hideDialog();
714
696
 
715
- handleInputBlur(event: FocusEvent) {
716
- if (!shouldUseOnScreenKeyboard()) return;
697
+ // restore initial focus after closing dialog
698
+ focusManager.setInitialFocus();
699
+ }
717
700
 
718
- const target = event.target as HTMLElement;
701
+ handleInputFocus = (event: FocusEvent) => {
702
+ if (!shouldUseOnScreenKeyboard()) return;
703
+ const target = event.target as HTMLInputElement;
719
704
 
720
- if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
721
- const relatedTarget = event.relatedTarget as HTMLElement;
705
+ if (this.isDismissing) return;
722
706
 
723
- if (
724
- !relatedTarget ||
725
- (relatedTarget.tagName !== "INPUT" &&
726
- relatedTarget.tagName !== "TEXTAREA")
727
- ) {
728
- this.setState({ isKeyboardVisible: false });
729
- }
730
- }
707
+ if (
708
+ (target.tagName === "INPUT" && target.type === "text") ||
709
+ target.tagName === "TEXTAREA"
710
+ ) {
711
+ this.setState({
712
+ isKeyboardVisible: true,
713
+ keyboardInput: target.value || "",
714
+ activeInputRef: target,
715
+ });
731
716
  }
717
+ };
732
718
 
733
- handleKeyboardDismiss = () => {
734
- this.isDismissing = true;
719
+ handleInputBlur(event: FocusEvent) {
720
+ if (!shouldUseOnScreenKeyboard()) return;
735
721
 
736
- this.setState(
737
- {
738
- isKeyboardVisible: false,
739
- keyboardInput: "",
740
- },
741
- () => {
742
- this.isDismissing = false;
743
- }
744
- );
745
- };
722
+ const target = event.target as HTMLElement;
746
723
 
747
- handleKeyboardInput(value: string) {
748
- this.setState({ keyboardInput: value });
749
- }
724
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
725
+ const relatedTarget = event.relatedTarget as HTMLElement;
750
726
 
751
- render() {
752
- return (
753
- <>
754
- <Component {...this.props} />
755
- {shouldUseOnScreenKeyboard() && this.state.isKeyboardVisible && (
756
- <OnScreenKeyboard
757
- initialValue={this.state.keyboardInput}
758
- onInput={this.handleKeyboardInput}
759
- onDismiss={this.handleKeyboardDismiss}
760
- />
761
- )}
762
- </>
763
- );
727
+ if (
728
+ !relatedTarget ||
729
+ (relatedTarget.tagName !== "INPUT" &&
730
+ relatedTarget.tagName !== "TEXTAREA")
731
+ ) {
732
+ this.setState({ isKeyboardVisible: false });
733
+ }
764
734
  }
765
735
  }
766
736
 
767
- return R.compose(
768
- withNavigator,
769
- withUseSeek,
770
- withXray,
771
- PlayerContentContext.withConsumer,
772
- DisplayStateContext.withConsumer
773
- )(InteractionManager);
737
+ handleKeyboardDismiss = () => {
738
+ this.isDismissing = true;
739
+
740
+ this.setState(
741
+ {
742
+ isKeyboardVisible: false,
743
+ keyboardInput: "",
744
+ activeInputRef: null,
745
+ },
746
+ () => {
747
+ this.isDismissing = false;
748
+ }
749
+ );
750
+ };
751
+
752
+ handleKeyboardInput = (value: string) => {
753
+ const { activeInputRef } = this.state;
754
+
755
+ if (activeInputRef) {
756
+ // Update the input's value
757
+ activeInputRef.value = value;
758
+
759
+ // Dispatch keyboard update event
760
+ const keyboardEvent = new CustomEvent("keyboardUpdate", {
761
+ detail: { value },
762
+ });
763
+
764
+ document.dispatchEvent(keyboardEvent);
765
+ }
766
+
767
+ this.setState({ keyboardInput: value });
768
+ };
769
+
770
+ render() {
771
+ return (
772
+ (shouldUseOnScreenKeyboard() && this.state.isKeyboardVisible && (
773
+ <OnScreenKeyboard
774
+ value={this.state.keyboardInput}
775
+ onInput={this.handleKeyboardInput}
776
+ onDismiss={this.handleKeyboardDismiss}
777
+ />
778
+ )) ||
779
+ null
780
+ );
781
+ }
774
782
  }
783
+
784
+ export const InteractionManager = R.compose(
785
+ withNavigator,
786
+ withUseSeek,
787
+ withXray,
788
+ connectToStore(R.pick(["rivers"])),
789
+ PlayerContentContext.withConsumer,
790
+ DisplayStateContext.withConsumer
791
+ )(InteractionManagerClass);