@applicaster/zapp-react-dom-app 16.0.0-alpha.1054344748 → 16.0.0-alpha.1108851646

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,10 +1,26 @@
1
1
  import { DISPLAY_STATES } from "@applicaster/zapp-react-native-ui-components/Contexts/DisplayStateContext";
2
2
  import { confirmationDialogStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
3
- import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
3
+ import {
4
+ focusManager,
5
+ KEYS,
6
+ playerManager,
7
+ } from "@applicaster/zapp-react-native-utils/appUtils";
8
+ import { KeyInputHandler } from "@applicaster/zapp-react-native-utils/appUtils/keyInputHandler/KeyInputHandler";
9
+ import { TVSeekController } from "@applicaster/zapp-react-native-utils/reactHooks/player/TVSeekControlller/TVSeekController";
10
+ import { SKIP_TIME_BASE } from "@applicaster/zapp-react-native-utils/reactHooks/player/const";
4
11
  import { showConfirmationDialog } from "@applicaster/zapp-react-native-utils/alertUtils";
5
12
 
6
13
  import { InteractionManagerClass } from "..";
7
14
 
15
+ // The web app runs with the web key map
16
+ jest.mock(
17
+ "@applicaster/zapp-react-native-utils/appUtils/keyCodes/keys/keys",
18
+ () =>
19
+ jest.requireActual(
20
+ "@applicaster/zapp-react-native-utils/appUtils/keyCodes/keys/keys.web"
21
+ )
22
+ );
23
+
8
24
  jest.mock("@applicaster/zapp-react-native-utils/alertUtils", () => ({
9
25
  showConfirmationDialog: jest.fn(),
10
26
  }));
@@ -129,4 +145,128 @@ describe("InteractionManager (web)", () => {
129
145
  );
130
146
  });
131
147
  });
148
+
149
+ describe("Forward / Rewind keys", () => {
150
+ const POSITION = 100;
151
+
152
+ const createPlayer = (supportsNativeControls: boolean) => ({
153
+ supportsNativeControls: () => supportsNativeControls,
154
+ isPaused: () => false,
155
+ isLive: () => false,
156
+ getPosition: () => POSITION,
157
+ getSeekableDuration: () => 1000,
158
+ pause: jest.fn(),
159
+ play: jest.fn(),
160
+ seekTo: jest.fn(),
161
+ forward: jest.fn(),
162
+ rewind: jest.fn(),
163
+ });
164
+
165
+ let removeListener = () => {};
166
+
167
+ // Same wiring as usePlayerControllerSetup
168
+ const setup = ({ supportsNativeControls, ...props }: any) => {
169
+ const player = createPlayer(supportsNativeControls);
170
+
171
+ jest
172
+ .spyOn(playerManager, "getInstanceController")
173
+ .mockReturnValue(player as any);
174
+
175
+ removeListener = KeyInputHandler.getInstance().addListener(
176
+ new TVSeekController(player as any)
177
+ );
178
+
179
+ return { player, ...createManager(props) };
180
+ };
181
+
182
+ const press = (manager, key) => {
183
+ manager.onKeyDown(KEYS[key]);
184
+ manager.onKeyUp(KEYS[key]);
185
+ jest.runAllTimers();
186
+ };
187
+
188
+ beforeEach(() => jest.useFakeTimers());
189
+
190
+ afterEach(() => {
191
+ removeListener();
192
+ jest.useRealTimers();
193
+ jest.restoreAllMocks();
194
+ });
195
+
196
+ describe("with a player rendering its own controls", () => {
197
+ const expectUntouched = (player, props) => {
198
+ expect(player.seekTo).not.toHaveBeenCalled();
199
+ expect(player.forward).not.toHaveBeenCalled();
200
+ expect(player.rewind).not.toHaveBeenCalled();
201
+ expect(player.pause).not.toHaveBeenCalled();
202
+ expect(props.setDisplayState).not.toHaveBeenCalled();
203
+ };
204
+
205
+ // the deferral logs the focused element
206
+ beforeEach(() => {
207
+ globalAny.document = {};
208
+ });
209
+
210
+ afterEach(() => {
211
+ delete globalAny.document;
212
+ });
213
+
214
+ it.each(["Forward", "Rewind"])("%s is left to the player", (key) => {
215
+ const { player, manager, props } = setup({
216
+ supportsNativeControls: true,
217
+ });
218
+
219
+ const event = { ...KEYS[key], stopPropagation: jest.fn() };
220
+
221
+ manager.onKeyDown(event);
222
+ manager.onKeyUp(event);
223
+ jest.runAllTimers();
224
+
225
+ expectUntouched(player, props);
226
+ // the key has to reach the player's own handler
227
+ expect(event.stopPropagation).not.toHaveBeenCalled();
228
+ });
229
+
230
+ it("does not seek the player while the play-next overlay is visible", () => {
231
+ const { player, manager } = setup({ supportsNativeControls: true });
232
+
233
+ (manager.props.navigator as any).getPlayNextOverlay = () => ({});
234
+
235
+ press(manager, "Forward");
236
+
237
+ expect(player.seekTo).not.toHaveBeenCalled();
238
+ expect(player.forward).not.toHaveBeenCalled();
239
+ });
240
+
241
+ it("keeps the keys while a dialog is visible", () => {
242
+ const { player, manager } = setup({ supportsNativeControls: true });
243
+
244
+ confirmationDialogStore.getState().showDialog();
245
+
246
+ press(manager, "Forward");
247
+
248
+ expect(player.seekTo).toHaveBeenCalledWith(POSITION + SKIP_TIME_BASE);
249
+ });
250
+ });
251
+
252
+ describe("with QuickBrick's transport controls", () => {
253
+ it.each([
254
+ ["Forward", POSITION + SKIP_TIME_BASE],
255
+ ["Rewind", POSITION - SKIP_TIME_BASE],
256
+ ])("%s keeps the delayed seek", (key, expectedPosition) => {
257
+ const { player, manager, props } = setup({
258
+ supportsNativeControls: false,
259
+ displayState: DISPLAY_STATES.PLAYER,
260
+ });
261
+
262
+ press(manager, key);
263
+
264
+ expect(props.setDisplayState).toHaveBeenCalledWith(DISPLAY_STATES.HUD);
265
+ expect(player.seekTo).toHaveBeenCalledWith(expectedPosition);
266
+ expect(player.play).toHaveBeenCalled();
267
+ expect(player.forward).not.toHaveBeenCalled();
268
+ expect(player.rewind).not.toHaveBeenCalled();
269
+ });
270
+ });
271
+ });
132
272
  });
@@ -368,6 +368,10 @@ export class InteractionManagerClass extends React.Component<Props, State> {
368
368
  focusManager.pressOut();
369
369
  }
370
370
 
371
+ if (this.isDeferredToNativeControls(event)) {
372
+ return;
373
+ }
374
+
371
375
  if (
372
376
  keyCode(event).matches(KEYS.Forward) ||
373
377
  (__DEV__ && keyCode(event).matches(KEYS.C))
@@ -567,12 +571,22 @@ export class InteractionManagerClass extends React.Component<Props, State> {
567
571
  return (
568
572
  this.isNativePlayerControlsEnabled() &&
569
573
  !this.isPlayNextOverlayVisible() &&
570
- (keyCode(event).matchesAny(...ARROW_KEYS) ||
571
- keyCode(event).matches(KEYS.Enter) ||
572
- (__DEV__ && keyCode(event).matches(KEYS.X)))
574
+ (keyCode(event).matchesAny(
575
+ ...ARROW_KEYS,
576
+ KEYS.Enter,
577
+ // The player seeks on its own with these keys
578
+ KEYS.Forward,
579
+ KEYS.Rewind
580
+ ) ||
581
+ (__DEV__ && keyCode(event).matchesAny(KEYS.X, KEYS.C, KEYS.Z)))
573
582
  );
574
583
  };
575
584
 
585
+ // An open dialog owns the remote, whatever the player renders.
586
+ isDeferredToNativeControls = (event: KeyboardEvent) =>
587
+ !confirmationDialogStore.getState().isDialogVisible &&
588
+ this.shouldDeferPlaybackNavigationToNativeControls(event);
589
+
576
590
  onKeyDown(event) {
577
591
  const { setDisplayState, navigator, xray } = this.props;
578
592
  const displayState = this.getEffectiveDisplayState();
@@ -611,11 +625,8 @@ export class InteractionManagerClass extends React.Component<Props, State> {
611
625
  // Only handle repeat events for arrow keys
612
626
  if (event.repeat && !keyCode(event).matchesAny(...ARROW_KEYS)) return;
613
627
 
614
- if (
615
- !isDialogVisible &&
616
- this.shouldDeferPlaybackNavigationToNativeControls(event)
617
- ) {
618
- // The player's own spatial navigation handles these keys.
628
+ if (this.isDeferredToNativeControls(event)) {
629
+ // The player's own key handling takes these keys.
619
630
  // TEMP(native-controls): remove after device verification
620
631
  log_debug("[NativeControls] key deferred to player", {
621
632
  key: event.key,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-dom-app",
3
- "version": "16.0.0-alpha.1054344748",
3
+ "version": "16.0.0-alpha.1108851646",
4
4
  "description": "Zapp App Component for Applicaster's Quick Brick React Native App",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "homepage": "https://github.com/applicaster/zapp-react-dom-app#readme",
24
24
  "dependencies": {
25
- "@applicaster/zapp-react-dom-ui-components": "16.0.0-alpha.1054344748",
26
- "@applicaster/zapp-react-native-bridge": "16.0.0-alpha.1054344748",
27
- "@applicaster/zapp-react-native-redux": "16.0.0-alpha.1054344748",
28
- "@applicaster/zapp-react-native-ui-components": "16.0.0-alpha.1054344748",
29
- "@applicaster/zapp-react-native-utils": "16.0.0-alpha.1054344748",
25
+ "@applicaster/zapp-react-dom-ui-components": "16.0.0-alpha.1108851646",
26
+ "@applicaster/zapp-react-native-bridge": "16.0.0-alpha.1108851646",
27
+ "@applicaster/zapp-react-native-redux": "16.0.0-alpha.1108851646",
28
+ "@applicaster/zapp-react-native-ui-components": "16.0.0-alpha.1108851646",
29
+ "@applicaster/zapp-react-native-utils": "16.0.0-alpha.1108851646",
30
30
  "abortcontroller-polyfill": "^1.7.5",
31
31
  "typeface-montserrat": "^0.0.54",
32
32
  "video.js": "7.14.3",