@applicaster/zapp-react-native-utils 13.0.0-alpha.4534963565 → 13.0.0-alpha.4574923605

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.
@@ -16,12 +16,12 @@ import {
16
16
 
17
17
  export { StorageLevel };
18
18
 
19
- type KeyNameObj = {
19
+ export type KeyNameObj = {
20
20
  namespace: string;
21
21
  key: string;
22
22
  };
23
23
 
24
- type KeyName = string | KeyNameObj;
24
+ export type KeyName = string | KeyNameObj;
25
25
 
26
26
  type ParsedKeyAndNamespace = {
27
27
  namespace?: string;
@@ -1,10 +1,11 @@
1
1
  import * as R from "ramda";
2
2
  import { DEFAULT_NAMESPACE } from "../consts";
3
+ import { KeyName, KeyNameObj } from "../index";
3
4
 
4
5
  const lastDotRegex = /\.([^.]+)$/;
5
6
  const splitByLastDot = R.compose(R.init, R.split(lastDotRegex));
6
7
 
7
- export function getNamespaceAndKey(namespacedKey) {
8
+ export function getNamespaceAndKey(namespacedKey: KeyName): KeyNameObj {
8
9
  if (typeof namespacedKey !== "string") return namespacedKey;
9
10
 
10
11
  const namespacedKeyProps = namespacedKey.includes(".")
@@ -28,10 +28,12 @@ exports[`focusManager should be defined 1`] = `
28
28
  "moveFocus": [Function],
29
29
  "on": [Function],
30
30
  "press": [Function],
31
+ "pressIn": [Function],
31
32
  "pressOut": [Function],
32
33
  "recoverFocus": [Function],
33
34
  "register": [Function],
34
35
  "removeHandler": [Function],
36
+ "resetFocus": [Function],
35
37
  "setFocus": [Function],
36
38
  "setInitialFocus": [Function],
37
39
  "unregister": [Function],
@@ -1,5 +1,6 @@
1
1
  import { focusManager } from "../index.ts";
2
2
  import { focusManager as focusManagerIOS } from "../index.ios.ts";
3
+ import { findFirstFocusableItem } from "../utils";
3
4
 
4
5
  describe("focusManagerIOS", () => {
5
6
  it("should be defined", () => {
@@ -8,6 +9,67 @@ describe("focusManagerIOS", () => {
8
9
  });
9
10
  });
10
11
 
12
+ const focusableTree = {
13
+ root: {
14
+ children: [
15
+ {
16
+ children: [
17
+ {
18
+ children: [
19
+ {
20
+ children: [],
21
+ component: {
22
+ isGroup: true,
23
+ },
24
+ id: "route/1/1",
25
+ },
26
+ {
27
+ children: [],
28
+ component: {
29
+ isGroup: true,
30
+ },
31
+ id: "route/1/2",
32
+ },
33
+ ],
34
+ component: {
35
+ isGroup: true,
36
+ },
37
+ id: "route/1",
38
+ },
39
+ {
40
+ children: [
41
+ {
42
+ children: [],
43
+ component: {
44
+ isGroup: true,
45
+ },
46
+ id: "route/2/1",
47
+ },
48
+ {
49
+ children: [],
50
+ component: {
51
+ isGroup: false,
52
+ },
53
+ id: "route/2/2",
54
+ },
55
+ ],
56
+ component: {
57
+ isGroup: true,
58
+ },
59
+ id: "route/2",
60
+ },
61
+ ],
62
+ component: {
63
+ isGroup: true,
64
+ },
65
+ id: "/river/1234",
66
+ },
67
+ ],
68
+ component: null,
69
+ id: "root",
70
+ },
71
+ };
72
+
11
73
  describe("focusManager", () => {
12
74
  it("should be defined", () => {
13
75
  expect(focusManager).toBeDefined();
@@ -45,4 +107,24 @@ describe("focusManager", () => {
45
107
  it("placeholder test", () => {
46
108
  expect(true).toBe(true);
47
109
  });
110
+
111
+ it("findFirstFocusableItem returns focusableItem", () => {
112
+ const expectedItemId =
113
+ focusableTree.root.children[0].children[1].children[1].id;
114
+
115
+ const focusableItem = findFirstFocusableItem(focusableTree.root.children);
116
+
117
+ expect(
118
+ focusableItem !== undefined && expectedItemId === focusableItem.id
119
+ ).toBe(true);
120
+ });
121
+
122
+ it("findFirstFocusableItem does not return focusableItem", () => {
123
+ focusableTree.root.children[0].children[1].children[1].component.isGroup =
124
+ true;
125
+
126
+ const focusableItem = findFirstFocusableItem(focusableTree.root.children);
127
+
128
+ expect(focusableItem === undefined).toBe(true);
129
+ });
48
130
  });
@@ -30,6 +30,7 @@ describe("focusManager", () => {
30
30
  "isGroupItemFocused",
31
31
  "recoverFocus",
32
32
  "isCurrentFocusOnTheTopScreen",
33
+ "resetFocus",
33
34
  ];
34
35
 
35
36
  expectedMethods.forEach((method) => {
@@ -13,6 +13,7 @@ import {
13
13
  import { subscriber } from "../../functionUtils";
14
14
  import { coreLogger } from "../../logger";
15
15
  import { ACTION } from "./utils/enums";
16
+ import { findFirstFocusableItem } from "./utils";
16
17
 
17
18
  const logger = coreLogger.addSubsystem("focusManager");
18
19
 
@@ -345,6 +346,14 @@ export const focusManager = (function () {
345
346
  });
346
347
  }
347
348
 
349
+ function pressIn() {
350
+ const currentFocusable = getCurrentFocus();
351
+
352
+ if (currentFocusable) {
353
+ currentFocusable.pressIn(null);
354
+ }
355
+ }
356
+
348
357
  /**
349
358
  * calls the `press` method on the current focus component
350
359
  * @public
@@ -484,20 +493,29 @@ export const focusManager = (function () {
484
493
  return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
485
494
  }
486
495
 
487
- function recoverFocus() {
496
+ function resetFocus() {
488
497
  if (!isCurrentFocusOnTheTopScreen()) {
489
- // We've failed to set focused node on the new screen => run focus recovery
498
+ // We've failed to set focused node on the new screen => run focus reset
490
499
  setInitialFocus();
491
500
  }
492
501
  }
493
502
 
503
+ /**
504
+ * sets the focus on first focusable item
505
+ * @public
506
+ */
507
+ function recoverFocus() {
508
+ const focusableItem = findFirstFocusableItem(focusableTree.root.children);
509
+ focusableItem && setFocus(focusableItem.id);
510
+ }
511
+
494
512
  /**
495
513
  * moves the focus in a provided direction
496
514
  * @public
497
515
  * @param {Object} direction of the scroll
498
516
  */
499
517
  function moveFocus(direction) {
500
- if (!isCurrentFocusOnTheTopScreen()) {
518
+ if (!isCurrentFocusOnTheTopScreen() && currentFocusNode) {
501
519
  // We are opening new screen. We need to ignore movement of focus.
502
520
  return false;
503
521
  }
@@ -520,6 +538,8 @@ export const focusManager = (function () {
520
538
  } else {
521
539
  currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
522
540
  }
541
+ } else {
542
+ recoverFocus();
523
543
  }
524
544
  }
525
545
 
@@ -552,6 +572,7 @@ export const focusManager = (function () {
552
572
  invokeHandler,
553
573
  removeHandler,
554
574
  longPress,
575
+ pressIn,
555
576
  pressOut,
556
577
  enableFocus,
557
578
  setInitialFocus,
@@ -563,5 +584,6 @@ export const focusManager = (function () {
563
584
  isGroupItemFocused,
564
585
  recoverFocus,
565
586
  isCurrentFocusOnTheTopScreen,
587
+ resetFocus,
566
588
  };
567
589
  })();
@@ -98,3 +98,17 @@ export const waitForContent = (focusableTree) => {
98
98
  conditionFn: contentHasAnyChildren,
99
99
  });
100
100
  };
101
+
102
+ export const findFirstFocusableItem = (items) => {
103
+ for (const item of items) {
104
+ if (!item.component.isGroup) {
105
+ return item;
106
+ } else {
107
+ if (item.children) {
108
+ const focusableItem = findFirstFocusableItem(item.children);
109
+
110
+ if (focusableItem) return focusableItem;
111
+ }
112
+ }
113
+ }
114
+ };
@@ -54,7 +54,7 @@ export async function setFocusOnContent(currentRoute: string) {
54
54
  // For doing it we have to wait registration of first focusable node into focusableTree
55
55
  // we wait until content node starts having children
56
56
 
57
- const { setInitialFocus, focusableTree } = focusManager;
57
+ const { setInitialFocus, focusableTree, getCurrentFocus } = focusManager;
58
58
 
59
59
  try {
60
60
  await waitForActiveScreen(currentRoute, focusableTree);
@@ -71,6 +71,6 @@ export async function setFocusOnContent(currentRoute: string) {
71
71
  );
72
72
  }
73
73
  } catch (error) {
74
- setFocusOnContent(currentRoute);
74
+ !getCurrentFocus() && setFocusOnContent(currentRoute);
75
75
  }
76
76
  }
@@ -77,6 +77,12 @@ export class ClosedCaptioningManager {
77
77
 
78
78
  private constructor() {
79
79
  this.initialize();
80
+
81
+ window["vizioDebug"] = {
82
+ setCCEnabled: (isCCEnabled: boolean) => {
83
+ this.ccState$.next(isCCEnabled);
84
+ },
85
+ };
80
86
  }
81
87
 
82
88
  static getInstance(): ClosedCaptioningManager {
@@ -49,9 +49,16 @@ export const isEqual =
49
49
  }
50
50
 
51
51
  if (compareRemainingKeysBySameValue) {
52
- const undeclaredKeys = R.difference(
53
- [...byValue, ...byReference, byComparator.keys],
54
- Object.keys(prev)
52
+ const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
53
+
54
+ const declaredKeys = new Set([
55
+ ...byValue,
56
+ ...byReference,
57
+ ...byComparator.keys,
58
+ ]);
59
+
60
+ const undeclaredKeys = Array.from(allKeys).filter(
61
+ (key) => !declaredKeys.has(key)
55
62
  );
56
63
 
57
64
  const isRestKeysEqualAllBySameValue = undeclaredKeys.every((key) =>
@@ -250,7 +250,17 @@ class FocusManager {
250
250
  }
251
251
 
252
252
  onDisableFocusChange = (id) => {
253
- if (FocusManager.instance.isFocused(id)) {
253
+ const isFocused = FocusManager.instance.isFocused(id);
254
+
255
+ const isDescendantFocused = (() => {
256
+ try {
257
+ return FocusManager.instance.isAnyDescendantFocused(id);
258
+ } catch (_e) {
259
+ return false;
260
+ }
261
+ })();
262
+
263
+ if (isFocused || isDescendantFocused) {
254
264
  // Move focus to next one
255
265
  const nextFocus = FocusManager.instance.focused?.props
256
266
  ?.nextFocusDown as string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.0-alpha.4534963565",
3
+ "version": "13.0.0-alpha.4574923605",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "13.0.0-alpha.4534963565",
31
+ "@applicaster/applicaster-types": "13.0.0-alpha.4574923605",
32
32
  "buffer": "^5.2.1",
33
33
  "camelize": "^1.0.0",
34
34
  "create-react-context": "^0.2.3",
@@ -1,5 +1,5 @@
1
- import React, { useEffect, useMemo } from "react";
2
- import { BackHandler, InteractionManager, NativeModules } from "react-native";
1
+ import { useContext, useEffect, useMemo, useRef } from "react";
2
+ import { BackHandler } from "react-native";
3
3
 
4
4
  import {
5
5
  useContentTypes,
@@ -31,6 +31,8 @@ export { useGetBottomTabBarHeight } from "./useGetBottomTabBarHeight";
31
31
 
32
32
  export { useIsScreenActive } from "./useIsScreenActive";
33
33
 
34
+ export { useProfilerLogging } from "./useProfilerLogging";
35
+
34
36
  export {
35
37
  useUniqueRouteSuffix,
36
38
  useNavbarId,
@@ -121,10 +123,8 @@ export function isNavBarVisible(
121
123
  return true;
122
124
  }
123
125
 
124
- const { ProfilerBridge } = NativeModules;
125
-
126
126
  export const useBackHandler = (cb: () => boolean) => {
127
- React.useEffect(() => {
127
+ useEffect(() => {
128
128
  BackHandler.addEventListener("hardwareBackPress", cb);
129
129
 
130
130
  return () => {
@@ -158,7 +158,7 @@ export const useZappHooksForEntry = (
158
158
  setIsHooksExecutionInProgress,
159
159
  setIsPresentingFullScreen,
160
160
  setIsRunningInBackground,
161
- }: HookModalContextT = React.useContext(ZappHookModalContext.ReactContext);
161
+ }: HookModalContextT = useContext(ZappHookModalContext.ReactContext);
162
162
 
163
163
  const {
164
164
  appData: { layoutVersion },
@@ -256,22 +256,10 @@ export const useZappHooksForEntry = (
256
256
  }, [entry.id]);
257
257
  };
258
258
 
259
- export const useProfilerLogging = () => {
260
- const logTimestamp = (data: string) =>
261
- InteractionManager.runAfterInteractions(() => {
262
- if (ProfilerBridge && data) {
263
- ProfilerBridge.updateTimer("navigation", data, Date.now());
264
- }
265
- });
266
-
267
- return logTimestamp;
268
- };
269
-
270
259
  export const useRunIfLongPress = (
271
260
  options = { timeout: LONG_KEY_PRESS_TIMEOUT }
272
261
  ) => {
273
- const pressTimeout =
274
- React.useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
262
+ const pressTimeout = useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
275
263
 
276
264
  const resetTimeout = () => {
277
265
  clearTimeout(pressTimeout.current as ReturnType<typeof setTimeout>);
@@ -0,0 +1,15 @@
1
+ import { useCallback } from "react";
2
+ import { InteractionManager, NativeModules } from "react-native";
3
+
4
+ const { ProfilerBridge } = NativeModules;
5
+
6
+ export const useProfilerLogging = () =>
7
+ useCallback(
8
+ (data: string) =>
9
+ InteractionManager.runAfterInteractions(() => {
10
+ if (ProfilerBridge && data) {
11
+ ProfilerBridge.updateTimer("navigation", data, Date.now());
12
+ }
13
+ }),
14
+ []
15
+ );