@applicaster/zapp-react-native-utils 15.1.0-rc.1 → 16.0.0-alpha.9803580571

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.
Files changed (144) hide show
  1. package/README.md +0 -6
  2. package/actionUtils/index.ts +7 -0
  3. package/actionsExecutor/ActionExecutorContext.tsx +43 -12
  4. package/actionsExecutor/feedDecorator.ts +6 -6
  5. package/adsUtils/__tests__/createVMAP.test.ts +419 -0
  6. package/adsUtils/index.ts +2 -2
  7. package/analyticsUtils/README.md +1 -1
  8. package/appDataUtils/__tests__/urlScheme.test.ts +678 -0
  9. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +0 -188
  10. package/appUtils/HooksManager/__tests__/hooksManager.test.js +16 -2
  11. package/appUtils/HooksManager/index.ts +10 -10
  12. package/appUtils/RiverFocusManager/{index.js → index.ts} +25 -18
  13. package/appUtils/accessibilityManager/const.ts +4 -0
  14. package/appUtils/accessibilityManager/utils.ts +0 -1
  15. package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +7 -2
  16. package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +48 -0
  17. package/appUtils/contextKeysManager/contextResolver.ts +51 -22
  18. package/appUtils/contextKeysManager/index.ts +65 -10
  19. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +3 -0
  20. package/appUtils/focusManager/index.ios.ts +43 -4
  21. package/appUtils/focusManager/treeDataStructure/Tree/__tests__/Tree.test.js +46 -0
  22. package/appUtils/focusManager/treeDataStructure/Tree/index.js +18 -18
  23. package/appUtils/focusManagerAux/utils/index.ios.ts +122 -0
  24. package/appUtils/focusManagerAux/utils/index.ts +11 -3
  25. package/appUtils/focusManagerAux/utils/utils.ios.ts +202 -3
  26. package/appUtils/keyCodes/keys/keys.web.ts +1 -4
  27. package/appUtils/orientationHelper.ts +2 -4
  28. package/appUtils/platform/platformUtils.ts +1 -1
  29. package/appUtils/playerManager/player.ts +4 -0
  30. package/appUtils/playerManager/playerNative.ts +30 -21
  31. package/appUtils/playerManager/usePlayerState.tsx +14 -2
  32. package/cloudEventsUtils/__tests__/index.test.ts +529 -0
  33. package/cloudEventsUtils/index.ts +65 -1
  34. package/componentsUtils/index.ts +8 -0
  35. package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
  36. package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +207 -0
  37. package/errorUtils/__tests__/GeneralError.test.ts +97 -0
  38. package/errorUtils/__tests__/HttpStatusCode.test.ts +344 -0
  39. package/errorUtils/__tests__/MissingPluginError.test.ts +113 -0
  40. package/errorUtils/__tests__/NetworkError.test.ts +202 -0
  41. package/errorUtils/__tests__/getParsedResponse.test.ts +188 -0
  42. package/errorUtils/__tests__/invariant.test.ts +112 -0
  43. package/focusManager/aux/index.ts +1 -1
  44. package/headersUtils/__tests__/headersUtils.test.js +11 -1
  45. package/headersUtils/index.ts +2 -1
  46. package/manifestUtils/_internals/__tests__/index.test.js +41 -0
  47. package/manifestUtils/_internals/index.js +33 -0
  48. package/manifestUtils/defaultManifestConfigurations/player.js +59 -1
  49. package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +49 -0
  50. package/manifestUtils/fieldUtils/index.js +54 -0
  51. package/manifestUtils/index.js +2 -0
  52. package/manifestUtils/keys.js +228 -0
  53. package/manifestUtils/mobileAction/button/__tests__/mobileActionButton.test.js +168 -0
  54. package/manifestUtils/mobileAction/button/index.js +140 -0
  55. package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +102 -0
  56. package/manifestUtils/mobileAction/container/index.js +73 -0
  57. package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +127 -0
  58. package/manifestUtils/mobileAction/groups/defaults.js +76 -0
  59. package/manifestUtils/mobileAction/groups/index.js +80 -0
  60. package/manifestUtils/tvAction/container/index.js +1 -1
  61. package/numberUtils/__tests__/toNumber.test.ts +27 -0
  62. package/numberUtils/__tests__/toPositiveNumber.test.ts +193 -0
  63. package/numberUtils/index.ts +23 -1
  64. package/package.json +4 -4
  65. package/pluginUtils/index.ts +4 -0
  66. package/reactHooks/advertising/index.ts +2 -2
  67. package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +537 -0
  68. package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
  69. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  70. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
  71. package/reactHooks/cell-click/__tests__/index.test.js +1 -3
  72. package/reactHooks/cell-click/index.ts +2 -1
  73. package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
  74. package/reactHooks/connection/__tests__/index.test.js +1 -1
  75. package/reactHooks/debugging/__tests__/index.test.js +4 -4
  76. package/reactHooks/dev/__tests__/useReRenderLog.test.ts +188 -0
  77. package/reactHooks/device/useIsTablet.tsx +14 -19
  78. package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
  79. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
  80. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
  81. package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +4 -1
  82. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
  83. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +1 -1
  84. package/reactHooks/feed/index.ts +0 -2
  85. package/reactHooks/feed/useBatchLoading.ts +7 -1
  86. package/reactHooks/feed/useEntryScreenId.ts +2 -2
  87. package/reactHooks/feed/usePipesCacheReset.ts +3 -1
  88. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +13 -16
  89. package/reactHooks/layout/__tests__/index.test.tsx +1 -1
  90. package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
  91. package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.ts → useDimensions.test.tsx} +105 -25
  92. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
  93. package/reactHooks/navigation/__tests__/index.test.tsx +2 -4
  94. package/reactHooks/navigation/index.ts +7 -6
  95. package/reactHooks/navigation/useRoute.ts +8 -6
  96. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +27 -10
  97. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
  98. package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
  99. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
  100. package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
  101. package/reactHooks/resolvers/useCellResolver.ts +6 -2
  102. package/reactHooks/resolvers/useComponentResolver.ts +8 -2
  103. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
  104. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
  105. package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
  106. package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +12 -4
  107. package/reactHooks/screen/index.ts +0 -2
  108. package/reactHooks/screen/useTargetScreenData.ts +4 -2
  109. package/reactHooks/state/useRivers.ts +1 -1
  110. package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +580 -0
  111. package/reactHooks/usePluginConfiguration.ts +2 -2
  112. package/reactHooks/utils/__tests__/index.test.js +1 -1
  113. package/rectUtils/__tests__/index.test.ts +549 -0
  114. package/rectUtils/index.ts +2 -2
  115. package/refreshUtils/RefreshCoordinator/__tests__/refreshCoordinator.test.ts +206 -0
  116. package/refreshUtils/RefreshCoordinator/index.ts +245 -0
  117. package/refreshUtils/RefreshCoordinator/utils/__tests__/getDataRefreshConfig.test.ts +104 -0
  118. package/refreshUtils/RefreshCoordinator/utils/index.ts +29 -0
  119. package/screenPickerUtils/__tests__/index.test.ts +333 -0
  120. package/screenState/__tests__/index.test.ts +1 -1
  121. package/screenUtils/index.ts +3 -0
  122. package/searchUtils/const.ts +7 -0
  123. package/searchUtils/index.ts +3 -0
  124. package/stringUtils/index.ts +1 -1
  125. package/testUtils/index.tsx +30 -21
  126. package/time/__tests__/BackgroundTimer.test.ts +156 -0
  127. package/time/__tests__/Timer.test.ts +236 -0
  128. package/typeGuards/__tests__/isString.test.ts +21 -0
  129. package/typeGuards/index.ts +4 -0
  130. package/utils/__tests__/mergeRight.test.ts +48 -0
  131. package/utils/__tests__/path.test.ts +7 -0
  132. package/utils/__tests__/selectors.test.ts +124 -0
  133. package/utils/index.ts +13 -0
  134. package/utils/mergeRight.ts +5 -0
  135. package/utils/path.ts +6 -3
  136. package/utils/pathOr.ts +5 -1
  137. package/utils/selectors.ts +46 -0
  138. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +1 -1
  139. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
  140. package/zappFrameworkUtils/loginPluginUtils.ts +1 -1
  141. package/reactHooks/componentsMap/index.ts +0 -55
  142. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +0 -75
  143. package/reactHooks/feed/useFeedRefresh.tsx +0 -65
  144. package/reactHooks/screen/useIsStandaloneFullscreen.ts +0 -12
@@ -1,4 +1,4 @@
1
- import * as R from "ramda";
1
+ import { isNil } from "@applicaster/zapp-react-native-utils/utils";
2
2
 
3
3
  import { StorageLevel, StorageType } from "./consts";
4
4
  import {
@@ -13,6 +13,7 @@ import {
13
13
  localStorage as LocalStorage,
14
14
  secureStorage as SecureStorage,
15
15
  } from "./storage";
16
+ import { NativeModules } from "react-native";
16
17
 
17
18
  export { StorageLevel };
18
19
 
@@ -154,11 +155,65 @@ export class ContextKeysManager {
154
155
  }
155
156
 
156
157
  public async getKeys(
157
- keys: Array<KeyName>
158
+ keys: Array<KeyName>,
159
+ contextObj?: { key: string; required?: boolean }[]
158
160
  ): Promise<Map<KeyName, ValueOrNothing>> {
159
- const values = await Promise.all(keys.map((key) => this.getKey(key)));
161
+ if (NativeModules.ContextResolverBridge) {
162
+ const keysObj: Record<string, boolean> = {};
160
163
 
161
- return new Map(R.zip(keys, values));
164
+ const requiredByKey = new Map(
165
+ contextObj?.map((item) => [item.key, item.required ?? false]) ?? []
166
+ );
167
+
168
+ let hasResolvableKeys = false;
169
+
170
+ const normalizedKeys: Array<{
171
+ original: KeyName;
172
+ normalized: string | null;
173
+ }> = [];
174
+
175
+ for (const key of keys) {
176
+ const {
177
+ isValid: isValidKey,
178
+ key: parsedKey,
179
+ namespace: parsedNamespace,
180
+ } = this.parseKey(key);
181
+
182
+ if (!isValidKey) {
183
+ normalizedKeys.push({ original: key, normalized: null });
184
+ continue;
185
+ }
186
+
187
+ const normalized = buildNamespaceKey(parsedKey, parsedNamespace);
188
+
189
+ keysObj[normalized] = requiredByKey.get(normalized) ?? false;
190
+ hasResolvableKeys = true;
191
+ normalizedKeys.push({ original: key, normalized });
192
+ }
193
+
194
+ if (!hasResolvableKeys) {
195
+ return new Map(
196
+ normalizedKeys.map(({ original }) => [original, NOTHING])
197
+ );
198
+ }
199
+
200
+ const res =
201
+ await NativeModules.ContextResolverBridge.resolveContextKeys(keysObj);
202
+
203
+ const resolved = res ?? {};
204
+
205
+ return new Map(
206
+ normalizedKeys.map(({ original, normalized }) => {
207
+ if (!normalized) return [original, NOTHING];
208
+
209
+ return [original, resolved[normalized] ?? NOTHING];
210
+ })
211
+ );
212
+ } else {
213
+ const values = await Promise.all(keys.map((key) => this.getKey(key)));
214
+
215
+ return new Map(keys.map((key, index) => [key, values[index]]));
216
+ }
162
217
  }
163
218
 
164
219
  public async getKey(key: KeyName): Promise<ValueOrNothing> {
@@ -181,7 +236,7 @@ export class ContextKeysManager {
181
236
  parsedNamespace
182
237
  );
183
238
 
184
- if (!R.isNil(resultByReference)) {
239
+ if (!isNil(resultByReference)) {
185
240
  return resultByReference;
186
241
  }
187
242
 
@@ -192,7 +247,7 @@ export class ContextKeysManager {
192
247
  parsedNamespace
193
248
  );
194
249
 
195
- if (!R.isNil(resultFromSessionStorage)) {
250
+ if (!isNil(resultFromSessionStorage)) {
196
251
  return resultFromSessionStorage;
197
252
  }
198
253
 
@@ -201,7 +256,7 @@ export class ContextKeysManager {
201
256
  parsedNamespace
202
257
  );
203
258
 
204
- if (!R.isNil(resultFromLocalStorage)) {
259
+ if (!isNil(resultFromLocalStorage)) {
205
260
  return resultFromLocalStorage;
206
261
  }
207
262
 
@@ -210,7 +265,7 @@ export class ContextKeysManager {
210
265
  parsedNamespace
211
266
  );
212
267
 
213
- if (!R.isNil(resultFromSecureStorage)) {
268
+ if (!isNil(resultFromSecureStorage)) {
214
269
  return resultFromSecureStorage;
215
270
  }
216
271
 
@@ -275,7 +330,7 @@ export class ContextKeysManager {
275
330
  keysProp.map((keyProp) => this.setKey(keyProp))
276
331
  );
277
332
 
278
- return new Map(R.zip(keys, values));
333
+ return new Map(keys.map((key, index) => [key, values[index]]));
279
334
  }
280
335
 
281
336
  // when succeed saving - return true
@@ -384,7 +439,7 @@ export class ContextKeysManager {
384
439
  public async removeKeys(keys: KeyName[]): Promise<Map<KeyName, boolean>> {
385
440
  const values = await Promise.all(keys.map((key) => this.removeKey(key)));
386
441
 
387
- return new Map(R.zip(keys, values));
442
+ return new Map(keys.map((key, index) => [key, values[index]]));
388
443
  }
389
444
  // REMOVE
390
445
  }
@@ -71,6 +71,9 @@ exports[`focusManagerIOS should be defined 1`] = `
71
71
  "invokeHandler": [Function],
72
72
  "isChildOf": [Function],
73
73
  "isFocusOn": [Function],
74
+ "isFocusOnContent": [Function],
75
+ "isFocusOnMenu": [Function],
76
+ "isFocusOnTabsScreenContent": [Function],
74
77
  "isGroupItemFocused": [Function],
75
78
  "moveFocus": [Function],
76
79
  "on": [Function],
@@ -4,7 +4,11 @@ import * as R from "ramda";
4
4
  import {
5
5
  isCurrentFocusOn,
6
6
  isChildOf as isChildOfUtils,
7
- } from "../focusManagerAux/utils";
7
+ isPartOfMenu,
8
+ isPartOfContent,
9
+ isPartOfTabsScreenContent,
10
+ } from "../focusManagerAux/utils/index.ios";
11
+
8
12
  import { Tree } from "./treeDataStructure/Tree";
9
13
  import { findFocusableNode } from "./treeDataStructure/Utils";
10
14
  import { subscriber } from "../../functionUtils";
@@ -188,9 +192,15 @@ export const focusManager = (function () {
188
192
  function register({ id, component }) {
189
193
  const { isGroup = false } = component;
190
194
 
191
- emitRegistered(id);
195
+ if (isGroup) {
196
+ registerGroup(id, component);
197
+ } else {
198
+ registerItem(id, component);
199
+ }
192
200
 
193
- return isGroup ? registerGroup(id, component) : registerItem(id, component);
201
+ const groupId = component?.props?.groupId;
202
+
203
+ emitRegistered({ id, groupId, isGroup });
194
204
  }
195
205
 
196
206
  function unregister(id, { group = false } = {}) {
@@ -273,7 +283,9 @@ export const focusManager = (function () {
273
283
  function setFocus(
274
284
  id: string,
275
285
  direction?: FocusManager.IOS.Direction,
276
- options?: Partial<{ groupFocusedChanged: boolean }>,
286
+ options?: Partial<{
287
+ groupFocusedChanged: boolean;
288
+ }>,
277
289
  callback?: any
278
290
  ) {
279
291
  blur(direction);
@@ -412,6 +424,30 @@ export const focusManager = (function () {
412
424
  return id && isCurrentFocusOn(id, currentFocusNode);
413
425
  }
414
426
 
427
+ function isFocusOnMenu(): boolean {
428
+ const currentFocusable = getCurrentFocus();
429
+
430
+ return isPartOfMenu(focusableTree, currentFocusable?.props?.id);
431
+ }
432
+
433
+ function isFocusOnContent(): boolean {
434
+ const currentFocusable = getCurrentFocus();
435
+
436
+ return isPartOfContent(focusableTree, currentFocusable?.props?.id);
437
+ }
438
+
439
+ function isFocusOnTabsScreenContent(
440
+ screenPickerContentContainerId: string
441
+ ): boolean {
442
+ const currentFocusable = getCurrentFocus();
443
+
444
+ return isPartOfTabsScreenContent(
445
+ focusableTree,
446
+ screenPickerContentContainerId,
447
+ currentFocusable?.props?.id
448
+ );
449
+ }
450
+
415
451
  function isChildOf(childId, parentId): boolean {
416
452
  return isChildOfUtils(focusableTree, childId, parentId);
417
453
  }
@@ -438,6 +474,9 @@ export const focusManager = (function () {
438
474
  isGroupItemFocused,
439
475
  getPreferredFocusChild,
440
476
  isFocusOn,
477
+ isFocusOnMenu,
478
+ isFocusOnContent,
479
+ isFocusOnTabsScreenContent,
441
480
  isChildOf,
442
481
  };
443
482
  })();
@@ -373,3 +373,49 @@ describe("addNode", () => {
373
373
  checkParents(tree.root);
374
374
  });
375
375
  });
376
+
377
+ describe("findInTree", () => {
378
+ function createNode(id, children) {
379
+ return {
380
+ id,
381
+ children,
382
+ };
383
+ }
384
+
385
+ it("returns a direct child match from root children", () => {
386
+ const tree = new Tree(treeLoaded);
387
+ const direct = createNode("direct-node");
388
+
389
+ tree.root.children = [direct];
390
+
391
+ expect(tree.findInTree("direct-node")).toEqual(direct);
392
+ });
393
+
394
+ it("returns a nested descendant match", () => {
395
+ const tree = new Tree(treeLoaded);
396
+ const nested = createNode("nested-node");
397
+ const intermediate = createNode("intermediate-node", [nested]);
398
+ const rootNode = createNode("root-node", [intermediate]);
399
+
400
+ tree.root.children = [rootNode];
401
+
402
+ expect(tree.findInTree("nested-node")).toEqual(nested);
403
+ });
404
+
405
+ it("returns null when node id does not exist", () => {
406
+ const tree = new Tree(treeLoaded);
407
+ const leaf = createNode("leaf-node");
408
+ const rootNode = createNode("root-node", [leaf]);
409
+
410
+ tree.root.children = [rootNode];
411
+
412
+ expect(tree.findInTree("missing-node")).toEqual(null);
413
+ });
414
+
415
+ it("returns null when tree has no children", () => {
416
+ const tree = new Tree(treeLoaded);
417
+ tree.root.children = null;
418
+
419
+ expect(tree.findInTree("any-node")).toEqual(null);
420
+ });
421
+ });
@@ -205,31 +205,31 @@ export class Tree {
205
205
  * @returns founded node or null
206
206
  */
207
207
  findInTree(id) {
208
- const retVal = null;
209
-
210
- return this.findInArray(id, this.root.children, retVal);
208
+ return this.findInArray(id, this.root.children);
211
209
  }
212
210
 
213
- findInArray(id, children, retVal) {
214
- if (!retVal && children) {
215
- retVal = children.find((obj) => obj.id === id);
211
+ findInArray(id, children) {
212
+ if (!children) {
213
+ return null;
214
+ }
216
215
 
217
- if (!retVal) {
218
- children.forEach((child) => {
219
- if (child.children) {
220
- retVal = this.findInArray(id, child.children, retVal);
216
+ const directMatch = children.find((obj) => obj.id === id);
221
217
 
222
- if (retVal) {
223
- return retVal;
224
- }
225
- }
226
- });
227
- } else {
228
- return retVal;
218
+ if (directMatch) {
219
+ return directMatch;
220
+ }
221
+
222
+ for (const child of children) {
223
+ if (child.children) {
224
+ const nestedMatch = this.findInArray(id, child.children);
225
+
226
+ if (nestedMatch) {
227
+ return nestedMatch;
228
+ }
229
229
  }
230
230
  }
231
231
 
232
- return retVal;
232
+ return null;
233
233
  }
234
234
 
235
235
  /**
@@ -0,0 +1,122 @@
1
+ import { isNil, startsWith } from "@applicaster/zapp-react-native-utils/utils";
2
+
3
+ import {
4
+ QUICK_BRICK_CONTENT,
5
+ QUICK_BRICK_NAVBAR,
6
+ } from "@applicaster/quick-brick-core/const";
7
+
8
+ const isNavBar = (node) => startsWith(QUICK_BRICK_NAVBAR, node?.id);
9
+ const isContent = (node) => startsWith(QUICK_BRICK_CONTENT, node?.id);
10
+ const isRoot = (node) => node?.id === "root";
11
+
12
+ export const isPartOfTabsScreenContent = (
13
+ focusableTree,
14
+ screenPickerContentContainerId,
15
+ id
16
+ ) => {
17
+ const node = focusableTree.findInTree(id);
18
+
19
+ if (isNil(node)) {
20
+ return false;
21
+ }
22
+
23
+ if (isRoot(node)) {
24
+ return false;
25
+ }
26
+
27
+ if (isNavBar(node)) {
28
+ return false;
29
+ }
30
+
31
+ if (isContent(node)) {
32
+ return false;
33
+ }
34
+
35
+ if (node?.id === screenPickerContentContainerId) {
36
+ return true;
37
+ }
38
+
39
+ return isPartOfTabsScreenContent(
40
+ focusableTree,
41
+ screenPickerContentContainerId,
42
+ node.parent?.id
43
+ );
44
+ };
45
+
46
+ export const isPartOfMenu = (focusableTree, id): boolean => {
47
+ const node = focusableTree.findInTree(id);
48
+
49
+ if (isNil(node)) {
50
+ return false;
51
+ }
52
+
53
+ if (isRoot(node)) {
54
+ return false;
55
+ }
56
+
57
+ if (isNavBar(node)) {
58
+ return true;
59
+ }
60
+
61
+ if (isContent(node)) {
62
+ return false;
63
+ }
64
+
65
+ return isPartOfMenu(focusableTree, node.parent?.id);
66
+ };
67
+
68
+ export const isPartOfContent = (focusableTree, id) => {
69
+ const node = focusableTree.findInTree(id);
70
+
71
+ if (isNil(node)) {
72
+ return false;
73
+ }
74
+
75
+ if (isRoot(node)) {
76
+ return false;
77
+ }
78
+
79
+ if (isNavBar(node)) {
80
+ return false;
81
+ }
82
+
83
+ if (isContent(node)) {
84
+ return true;
85
+ }
86
+
87
+ return isPartOfContent(focusableTree, node.parent?.id);
88
+ };
89
+
90
+ export const isCurrentFocusOn = (id, node) => {
91
+ if (!node) {
92
+ return false;
93
+ }
94
+
95
+ if (isRoot(node)) {
96
+ return false;
97
+ }
98
+
99
+ if (node?.id === id) {
100
+ return true;
101
+ }
102
+
103
+ return isCurrentFocusOn(id, node.parent);
104
+ };
105
+
106
+ export const isChildOf = (focusableTree, childId, parentId) => {
107
+ if (isNil(childId) || isNil(parentId)) {
108
+ return false;
109
+ }
110
+
111
+ const childNode = focusableTree.findInTree(childId);
112
+
113
+ if (isNil(childNode)) {
114
+ return false;
115
+ }
116
+
117
+ if (childNode.parent?.id === parentId) {
118
+ return true;
119
+ }
120
+
121
+ return isChildOf(focusableTree, childNode.parent?.id, parentId);
122
+ };
@@ -100,7 +100,7 @@ export const getNavbarNode = (focusableTree) => {
100
100
 
101
101
  export const waitForContent = (focusableTree) => {
102
102
  const contentHasAnyChildren = (): boolean => {
103
- const countOfChildren = pathOr(
103
+ const countOfChildren = pathOr<number>(
104
104
  0,
105
105
  ["children", "length"],
106
106
  getContentNode(focusableTree)
@@ -142,6 +142,10 @@ export const isTabsScreenOnContentFocused = (node) => {
142
142
  };
143
143
 
144
144
  export const isCurrentFocusOnMenu = (node) => {
145
+ if (isNil(node)) {
146
+ return false;
147
+ }
148
+
145
149
  if (isRoot(node)) {
146
150
  return false;
147
151
  }
@@ -154,10 +158,14 @@ export const isCurrentFocusOnMenu = (node) => {
154
158
  return false;
155
159
  }
156
160
 
157
- return isCurrentFocusOnMenu(node.parent);
161
+ return isCurrentFocusOnMenu(node?.parent);
158
162
  };
159
163
 
160
164
  export const isCurrentFocusOnContent = (node) => {
165
+ if (isNil(node)) {
166
+ return false;
167
+ }
168
+
161
169
  if (isRoot(node)) {
162
170
  return false;
163
171
  }
@@ -170,7 +178,7 @@ export const isCurrentFocusOnContent = (node) => {
170
178
  return true;
171
179
  }
172
180
 
173
- return isCurrentFocusOnContent(node.parent);
181
+ return isCurrentFocusOnContent(node?.parent);
174
182
  };
175
183
 
176
184
  export const isCurrentFocusOn = (id, node) => {