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