@applicaster/zapp-react-dom-app 12.0.0-alpha.4840257570 → 12.0.0-alpha.4876423277

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