@applicaster/zapp-react-native-utils 13.0.0-rc.13 → 13.0.0-rc.131

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 (173) hide show
  1. package/README.md +0 -10
  2. package/actionsExecutor/ActionExecutor.ts +16 -9
  3. package/actionsExecutor/ActionExecutorContext.tsx +229 -17
  4. package/actionsExecutor/consts.ts +4 -0
  5. package/alertUtils/alert.ts +148 -0
  6. package/alertUtils/index.ts +1 -148
  7. package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
  8. package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
  9. package/analyticsUtils/currentScreenProperities.ts +6 -4
  10. package/analyticsUtils/helpers/hooks.ts +1 -1
  11. package/appUtils/HooksManager/Hook.ts +6 -2
  12. package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +7 -7
  13. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +186 -186
  14. package/appUtils/HooksManager/index.ts +3 -1
  15. package/appUtils/HooksManager/types.ts +5 -2
  16. package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
  17. package/appUtils/__tests__/allZappLocales.ts +79 -0
  18. package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
  19. package/appUtils/accessibilityManager/const.ts +18 -0
  20. package/appUtils/accessibilityManager/index.ts +42 -1
  21. package/appUtils/contextKeysManager/contextResolver.ts +66 -0
  22. package/appUtils/contextKeysManager/index.ts +2 -2
  23. package/appUtils/contextKeysManager/utils/index.ts +2 -1
  24. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +9 -6
  25. package/appUtils/focusManager/index.ios.ts +14 -4
  26. package/appUtils/focusManager/index.ts +108 -69
  27. package/appUtils/focusManager/treeDataStructure/Tree/index.js +23 -0
  28. package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
  29. package/appUtils/focusManager/utils/enums.ts +4 -0
  30. package/appUtils/focusManager/utils/index.ts +5 -0
  31. package/appUtils/focusManagerAux/utils/index.ts +4 -3
  32. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
  33. package/appUtils/keyCodes/__tests__/keyCodes.test.js +1 -1
  34. package/appUtils/keyCodes/index.ts +11 -7
  35. package/appUtils/keyCodes/keys/keysByPlatform.ts +4 -0
  36. package/appUtils/keyInputHandler/KeyInputHandler.ts +68 -0
  37. package/appUtils/localizationsHelper.ts +31 -0
  38. package/appUtils/orientationHelper.ts +11 -13
  39. package/appUtils/platform/const.ts +14 -0
  40. package/appUtils/platform/platformUtils.ts +47 -4
  41. package/appUtils/playerManager/index.ts +1 -1
  42. package/appUtils/playerManager/player.ts +42 -18
  43. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
  44. package/appUtils/playerManager/playerNative.ts +18 -2
  45. package/appUtils/playerManager/usePlayerControllerSetup.tsx +3 -3
  46. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
  47. package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
  48. package/cellUtils/index.ts +9 -5
  49. package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
  50. package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
  51. package/componentsUtils/__tests__/isGallery.test.ts +33 -0
  52. package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
  53. package/componentsUtils/index.ts +39 -0
  54. package/dateUtils/index.ts +19 -0
  55. package/equalUtils/__tests__/isEqual/combination.test.ts +263 -0
  56. package/equalUtils/index.ts +49 -30
  57. package/focusManager/FocusManager.ts +14 -2
  58. package/focusManager/index.ts +23 -4
  59. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
  60. package/functionUtils/index.ts +46 -8
  61. package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
  62. package/idleUtils/index.ts +1 -1
  63. package/idleUtils/withTimeout.ts +15 -2
  64. package/index.d.ts +0 -12
  65. package/localizationUtils/index.ts +8 -13
  66. package/logger/const.ts +9 -67
  67. package/manifestUtils/__tests__/createConfig.test.js +16 -16
  68. package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
  69. package/manifestUtils/defaultManifestConfigurations/player.js +9 -3
  70. package/manifestUtils/fonts/index.js +5 -6
  71. package/manifestUtils/index.js +2 -0
  72. package/manifestUtils/keys.js +27 -2
  73. package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
  74. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +875 -0
  75. package/manifestUtils/sharedConfiguration/screenPicker/utils.js +130 -0
  76. package/modalState/__tests__/index.test.ts +51 -0
  77. package/modalState/index.ts +32 -2
  78. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +7 -6
  79. package/navigationUtils/__tests__/itemTypes.test.js +2 -2
  80. package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
  81. package/navigationUtils/index.ts +6 -4
  82. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
  83. package/numberUtils/index.ts +11 -5
  84. package/package.json +5 -10
  85. package/playerUtils/configurationGenerator.ts +9 -11
  86. package/playerUtils/index.ts +17 -0
  87. package/reactHooks/advertising/index.ts +5 -5
  88. package/reactHooks/app/useAppState.ts +2 -2
  89. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  90. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +6 -8
  91. package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
  92. package/reactHooks/cell-click/__tests__/index.test.js +10 -2
  93. package/reactHooks/cell-click/index.ts +7 -1
  94. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
  95. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
  96. package/reactHooks/feed/useBatchLoading.ts +10 -12
  97. package/reactHooks/feed/useFeedLoader.tsx +18 -9
  98. package/reactHooks/feed/usePipesCacheReset.ts +5 -5
  99. package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
  100. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
  101. package/reactHooks/layout/__tests__/index.test.tsx +10 -10
  102. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
  103. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
  104. package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
  105. package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
  106. package/reactHooks/navigation/__tests__/index.test.tsx +6 -21
  107. package/reactHooks/navigation/getTabBarHeight.ts +10 -0
  108. package/reactHooks/navigation/index.ts +34 -236
  109. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +19 -0
  110. package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
  111. package/reactHooks/navigation/useIsScreenActive.ts +17 -0
  112. package/reactHooks/navigation/useNavigationPluginData.ts +19 -0
  113. package/reactHooks/navigation/useNavigationType.ts +17 -0
  114. package/reactHooks/navigation/usePathname.ts +4 -0
  115. package/reactHooks/navigation/useProfilerLogging.ts +15 -0
  116. package/reactHooks/navigation/useRoute.ts +98 -0
  117. package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
  118. package/reactHooks/player/TVSeekControlller/ISeekController.ts +32 -0
  119. package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +71 -0
  120. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +258 -0
  121. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +53 -0
  122. package/reactHooks/player/__tests__/{useAutoSeek.test.tsx → useAutoSeek._test.tsx} +2 -3
  123. package/reactHooks/player/const.ts +0 -4
  124. package/reactHooks/player/index.ts +0 -6
  125. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
  126. package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
  127. package/reactHooks/state/README.md +79 -0
  128. package/reactHooks/state/ZStoreProvider.tsx +71 -0
  129. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
  130. package/reactHooks/state/index.ts +2 -0
  131. package/reactHooks/state/useRefWithInitialValue.ts +14 -0
  132. package/reactHooks/state/useStateFromSubscribe.ts +23 -0
  133. package/reactHooks/useListenEventBusEvent.ts +1 -1
  134. package/reactHooks/usePluginConfiguration.ts +1 -1
  135. package/reactHooks/useSubscriberFor.ts +4 -3
  136. package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
  137. package/reactUtils/__tests__/reactUtils.test.js +1 -37
  138. package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
  139. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +13 -79
  140. package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +2 -2
  141. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
  142. package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
  143. package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
  144. package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
  145. package/reactUtils/createContext/__tests__/{index.test.js → index.test.ts} +2 -2
  146. package/reactUtils/createContext/__tests__/provider.test.js +4 -6
  147. package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
  148. package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
  149. package/reactUtils/createContext/helpers.ts +6 -1
  150. package/reactUtils/createContext/index.ts +11 -25
  151. package/reactUtils/index.ts +9 -11
  152. package/screenState/index.ts +24 -10
  153. package/services/js2native.ts +673 -0
  154. package/testUtils/index.tsx +0 -10
  155. package/theme/__mocks__/index.ts +4 -0
  156. package/theme/index.ts +1 -1
  157. package/time/BackgroundTimer.ts +55 -0
  158. package/time/Timer.ts +9 -6
  159. package/typeGuards/index.ts +3 -0
  160. package/types.ts +1 -0
  161. package/utils/README.md +30 -0
  162. package/utils/chunk.ts +5 -0
  163. package/utils/index.ts +5 -0
  164. package/utils/times.ts +5 -0
  165. package/zappFrameworkUtils/localStorageHelper.ts +38 -10
  166. package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
  167. package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
  168. package/zappFrameworkUtils/types.ts +11 -0
  169. package/reactHooks/player/useAutoSeek.tsx +0 -87
  170. package/reactHooks/player/useSeek.tsx +0 -66
  171. package/reactHooks/player/useTapSeek.tsx +0 -58
  172. package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
  173. /package/reactHooks/player/__tests__/{useTapSeek.test.ts → useTapSeek._test.ts} +0 -0
@@ -11,8 +11,9 @@ import {
11
11
  haveSameParentBeforeRoot,
12
12
  } from "./treeDataStructure/Utils";
13
13
  import { subscriber } from "../../functionUtils";
14
- import * as FOCUS_EVENTS from "./events";
15
14
  import { coreLogger } from "../../logger";
15
+ import { ACTION } from "./utils/enums";
16
+
16
17
  const logger = coreLogger.addSubsystem("focusManager");
17
18
 
18
19
  const isFocusEnabled = (focusableItem): boolean => {
@@ -167,13 +168,82 @@ export const focusManager = (function () {
167
168
  }
168
169
 
169
170
  /**
170
- * returns the current focusable's group
171
- * @private
172
- * @returns {React.Component} the current focus
171
+ * Computes the difference in parent groups between two nodes.
172
+ * It returns the IDs of parents that are present in the current node but not in the previous node,
173
+ * as well as the IDs of parents that are present in the previous node but not in the current node.
174
+ *
175
+ * @param {string} currentNodeID - The ID of the current node.
176
+ * @param {string} previousNodeID - The ID of the previous node.
177
+ * @returns {Object} An object containing the differences in parent IDs:
178
+ * - {Array<string>} currentNodeParentsIDs: Parent IDs present in the current node but not in the previous node.
179
+ * - {Array<string>} previousNodeParentsIDs: Parent IDs present in the previous node but not in the current node.
173
180
  */
174
- function getCurrentGroup() {
175
- return getGroupById(R.path(["props", "groupId"], getCurrentFocus()));
176
- }
181
+ const getParentDiffs = (currentNodeID, previousNodeID) => {
182
+ const currentNodeParentsIDs =
183
+ focusableTree.getParentGroupIds(currentNodeID);
184
+
185
+ const previousNodeParentsIDs =
186
+ focusableTree.getParentGroupIds(previousNodeID);
187
+
188
+ return {
189
+ currentNodeParentsIDs: R.difference(
190
+ currentNodeParentsIDs,
191
+ previousNodeParentsIDs
192
+ ),
193
+ previousNodeParentsIDs: R.difference(
194
+ previousNodeParentsIDs,
195
+ currentNodeParentsIDs
196
+ ),
197
+ };
198
+ };
199
+
200
+ /**
201
+ * Updates the focus state of nodes identified by their IDs.
202
+ * For each ID, if `setFocus` is true, the node will receive focus;
203
+ * otherwise, it will lose focus.
204
+ *
205
+ * @param {Array<string>} ids - An array of node IDs to update.
206
+ * @param {boolean} setFocus - A flag indicating whether to set focus (true) or blur (false) on the nodes.
207
+ */
208
+ const updateNodeFocus = (ids, action) => {
209
+ if (!ids || ids.length === 0) {
210
+ return; // Nothing to do
211
+ }
212
+
213
+ const startNode = focusableTree.findInTree(ids[0]);
214
+
215
+ if (!startNode) {
216
+ return; // Start node not found
217
+ }
218
+
219
+ const endNodeId = ids[ids.length - 1]; // Get the ID of the end node
220
+
221
+ let currentNode = startNode;
222
+
223
+ // Function to apply the action (focus or blur)
224
+ const applyAction = (node) => {
225
+ if (node && node.component) {
226
+ if (action === "focus") {
227
+ node.component.setFocus();
228
+ } else if (action === "blur") {
229
+ node.component.setBlur();
230
+ }
231
+ }
232
+ };
233
+
234
+ // Apply action to the starting node
235
+ applyAction(currentNode);
236
+
237
+ // Traverse upwards
238
+ while (currentNode && currentNode.id !== endNodeId) {
239
+ currentNode = currentNode.parent;
240
+
241
+ if (currentNode) {
242
+ // check if current node exists, as parent might be root
243
+ applyAction(currentNode);
244
+ }
245
+ }
246
+ };
177
247
 
178
248
  /**
179
249
  * Sets the current focus to the provided id. If there is an existing focus, it will
@@ -181,34 +251,29 @@ export const focusManager = (function () {
181
251
  * @private
182
252
  * @param {String} id of the focusable to set as currentFocus
183
253
  * @param {Object} direction of the navigation, which led to this focus change
184
- * @param {Object} options
185
- * @param {Boolean} options.groupFocusedChanged tells wether the focus has moved
186
254
  * to another group or not. defaults to false
187
255
  */
188
- function setFocus(
189
- id: string,
190
- direction?: FocusManager.Web.Direction,
191
- options?: Partial<{ groupFocusedChanged: boolean }>
192
- ) {
256
+ function setFocus(id: string, direction?: FocusManager.Web.Direction) {
193
257
  if (focusDisabled) return false;
194
258
 
195
- const { groupFocusedChanged = false } = options || {};
196
-
197
259
  // due to optimisiation it's recommanded to set currentFocusNode before setFocus
198
260
  // is called, so there will be no delays on tree search.
199
261
  // If currentFocusNode is null or undefined we still want it to findInTree
200
262
  if (currentFocusNode?.id !== id) {
263
+ const { currentNodeParentsIDs, previousNodeParentsIDs } = getParentDiffs(
264
+ id,
265
+ currentFocusNode?.id
266
+ );
267
+
268
+ // Set focus on current node parents and blur on previous node parents
269
+ updateNodeFocus(currentNodeParentsIDs, ACTION.FOCUS);
270
+ updateNodeFocus(previousNodeParentsIDs, ACTION.BLUR);
271
+
201
272
  currentFocusNode = focusableTree.findInTree(id);
202
273
  }
203
274
 
204
275
  setLastFocusOnParentNode(currentFocusNode);
205
276
 
206
- const currentGroup = getCurrentGroup();
207
-
208
- if (groupFocusedChanged && currentGroup) {
209
- currentGroup.hasLostFocus(currentGroup, direction);
210
- }
211
-
212
277
  focus(direction);
213
278
  }
214
279
 
@@ -251,6 +316,7 @@ export const focusManager = (function () {
251
316
  !focusableItem.isGroup &&
252
317
  isFocusEnabled(focusableItem)
253
318
  ) {
319
+ // TODO: Move this to setFocus method
254
320
  blur();
255
321
 
256
322
  logger.log({
@@ -260,8 +326,6 @@ export const focusManager = (function () {
260
326
  },
261
327
  });
262
328
 
263
- currentFocusNode = focusCandidate;
264
- focusableItem.willReceiveFocus(focusableItem, null);
265
329
  focusableItem && setFocus(focusCandidate.id, null);
266
330
 
267
331
  return { success: true };
@@ -270,15 +334,14 @@ export const focusManager = (function () {
270
334
  return { success: false };
271
335
  }
272
336
 
273
- function treeLoaded(val, node) {
274
- logger.log({
275
- message: "treeLoaded",
276
- data: {
277
- val,
278
- node,
279
- currentFocusNode,
280
- },
281
- });
337
+ function treeLoaded(_val, _node) {}
338
+
339
+ function pressIn() {
340
+ const currentFocusable = getCurrentFocus();
341
+
342
+ if (currentFocusable) {
343
+ currentFocusable.pressIn(null);
344
+ }
282
345
  }
283
346
 
284
347
  /**
@@ -412,7 +475,7 @@ export const focusManager = (function () {
412
475
  function isCurrentFocusOnTheTopScreen() {
413
476
  const routes = R.pathOr([], ["root", "children"], focusableTree);
414
477
 
415
- if (routes.length <= 1) {
478
+ if (currentFocusNode && routes.length <= 1) {
416
479
  // there is only one or zero routes
417
480
  return true;
418
481
  }
@@ -448,20 +511,10 @@ export const focusManager = (function () {
448
511
  const nextRect = nextFocusable.component.getRect();
449
512
 
450
513
  if (nextRect && nextRect.width > 0) {
451
- nextFocusable.component.willReceiveFocus(
452
- nextFocusable.component,
453
- direction
454
- );
455
-
514
+ // TODO: Move this to setFocus method
456
515
  blur(direction);
457
516
 
458
- const groupChanged =
459
- nextFocusable.component.props.groupId !==
460
- currentFocusNode?.component?.props?.groupId;
461
-
462
- setFocus(nextFocusable.id, direction, {
463
- groupFocusedChanged: groupChanged,
464
- });
517
+ setFocus(nextFocusable.id, direction);
465
518
  }
466
519
  } else {
467
520
  currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
@@ -481,33 +534,17 @@ export const focusManager = (function () {
481
534
 
482
535
  const { on, invokeHandler, removeHandler } = eventHandler;
483
536
 
484
- on(FOCUS_EVENTS.FOCUS, (focusable, direction) => {
485
- if (focusable.props.groupId) {
486
- const parent = focusableTree.findInTree(focusable.props.groupId);
487
- parent?.component?.onFocus(focusable.component, direction);
488
- }
489
- });
537
+ function findPreferredFocusChild(id) {
538
+ const node = getCurrentFocusNode(id);
490
539
 
491
- on(FOCUS_EVENTS.BLUR, (focusable, direction) => {
492
- if (focusable.props.groupId) {
493
- const parent = focusableTree.findInTree(focusable.props.groupId);
494
- parent?.component?.onBlur(focusable.component, direction);
495
- }
496
- });
540
+ const preferredFocus = findPriorityItem([node]);
497
541
 
498
- on(FOCUS_EVENTS.HAS_LOST_FOCUS, (focusable, direction) => {
499
- if (focusable.props.groupId) {
500
- const parent = focusableTree.findInTree(focusable.props.groupId);
501
- parent?.component?.hasLostFocus(parent.component, direction);
542
+ if (isNilOrEmpty(preferredFocus)) {
543
+ return undefined;
502
544
  }
503
- });
504
545
 
505
- on(FOCUS_EVENTS.WILL_RECEIVE_FOCUS, (focusable, direction) => {
506
- if (focusable.props.groupId) {
507
- const parent = focusableTree.findInTree(focusable.props.groupId);
508
- parent?.component?.willReceiveFocus(focusable.component, direction);
509
- }
510
- });
546
+ return preferredFocus[0];
547
+ }
511
548
 
512
549
  /**
513
550
  * this is the list of the functions available externally
@@ -526,6 +563,7 @@ export const focusManager = (function () {
526
563
  invokeHandler,
527
564
  removeHandler,
528
565
  longPress,
566
+ pressIn,
529
567
  pressOut,
530
568
  enableFocus,
531
569
  setInitialFocus,
@@ -537,5 +575,6 @@ export const focusManager = (function () {
537
575
  isGroupItemFocused,
538
576
  recoverFocus,
539
577
  isCurrentFocusOnTheTopScreen,
578
+ findPreferredFocusChild,
540
579
  };
541
580
  })();
@@ -231,4 +231,27 @@ export class Tree {
231
231
 
232
232
  return retVal;
233
233
  }
234
+
235
+ /**
236
+ * Gets the IDs of all parent groups for a given node id.
237
+ * @public
238
+ * @param {String} nodeId - The id of the node to get parent IDs for.
239
+ * @returns {Array} An array of parent group IDs or an empty array if the node is not found.
240
+ */
241
+ getParentGroupIds(nodeId) {
242
+ const parentIds = [];
243
+ let currentNode = this.findInTree(nodeId);
244
+
245
+ if (!currentNode) {
246
+ return []; // Node not found, return an empty array
247
+ }
248
+
249
+ // Traverse up the tree, collecting parent IDs
250
+ while (currentNode.parent && currentNode.parent.id !== "root") {
251
+ parentIds.push(currentNode.parent.id);
252
+ currentNode = currentNode.parent;
253
+ }
254
+
255
+ return parentIds;
256
+ }
234
257
  }
@@ -0,0 +1,35 @@
1
+ import { findChild } from "../index";
2
+
3
+ describe("findChild", () => {
4
+ const makeNode = (children = []) => ({ children });
5
+
6
+ const child = (preferredFocus: boolean) => ({
7
+ component: { props: { preferredFocus } },
8
+ });
9
+
10
+ it("returns the child with preferredFocus=true", () => {
11
+ const node = makeNode([child(false), child(true), child(false)]);
12
+ expect(findChild(true, node)).toBe(node.children[1]);
13
+ });
14
+
15
+ it("returns the child with preferredFocus=false", () => {
16
+ const node = makeNode([child(false), child(true), child(false)]);
17
+ expect(findChild(false, node)).toBe(node.children[0]);
18
+ });
19
+
20
+ it("returns undefined if no child matches preferredFocus", () => {
21
+ const node = makeNode([child(false), child(false)]);
22
+ expect(findChild(true, node)).toBeUndefined();
23
+ });
24
+
25
+ it("returns undefined if node has no children", () => {
26
+ const node = makeNode();
27
+ expect(findChild(true, node)).toBeUndefined();
28
+ expect(findChild(false, node)).toBeUndefined();
29
+ });
30
+
31
+ it("returns undefined if children is not an array", () => {
32
+ const node = { children: null };
33
+ expect(findChild(true, node)).toBeUndefined();
34
+ });
35
+ });
@@ -0,0 +1,4 @@
1
+ export enum ACTION {
2
+ FOCUS = "focus",
3
+ BLUR = "blur",
4
+ }
@@ -98,3 +98,8 @@ export const waitForContent = (focusableTree) => {
98
98
  conditionFn: contentHasAnyChildren,
99
99
  });
100
100
  };
101
+
102
+ export const findChild = (preferredFocus: boolean, node) =>
103
+ (node?.children || []).find(
104
+ (child) => child?.component?.props?.preferredFocus === preferredFocus
105
+ );
@@ -1,8 +1,8 @@
1
1
  import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
2
- import { find, last, startsWith, pathOr } from "ramda";
2
+ import { find, last, pathOr, startsWith } from "ramda";
3
3
  import {
4
- QUICK_BRICK_NAVBAR,
5
4
  QUICK_BRICK_CONTENT,
5
+ QUICK_BRICK_NAVBAR,
6
6
  } from "@applicaster/quick-brick-core/const";
7
7
 
8
8
  // run check each 300 ms
@@ -66,7 +66,8 @@ export const getContentNode = (focusableTree) => {
66
66
  const content = find(
67
67
  (child) =>
68
68
  startsWith(QUICK_BRICK_CONTENT, child.id) ||
69
- startsWith("player", child.id),
69
+ startsWith("player", child.id) ||
70
+ startsWith("/hooks", child?.parent?.id), // For startup hooks case
70
71
  getActiveScreenChildren(focusableTree)
71
72
  );
72
73
 
@@ -1,11 +1,11 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`keyCodes keyCode returns an object for a given key event 1`] = `
4
- Object {
4
+ {
5
5
  "direction": [Function],
6
6
  "matches": [Function],
7
7
  "matchesAny": [Function],
8
- "value": Object {
8
+ "value": {
9
9
  "code": "Enter",
10
10
  "keyCode": 13,
11
11
  },
@@ -1,4 +1,4 @@
1
- const { Platform } = require("react-native");
1
+ import { Platform } from "react-native";
2
2
 
3
3
  Platform.OS = "web";
4
4
 
@@ -1,5 +1,3 @@
1
- import * as R from "ramda";
2
-
3
1
  // more keys can be added here. Simply make sure
4
2
  // the key matches the code value
5
3
 
@@ -34,12 +32,18 @@ export function keyCode(key) {
34
32
  keyCode(keyEvent).matchesAny("Enter", "Backspace", KEYS.ArrowLeft, ...)
35
33
  */
36
34
 
37
- const matches = R.compose(
38
- R.either(R.eqProps("code", key), R.eqProps("keyCode", key)),
39
- R.when(R.is(String), R.prop(R.__, KEYS))
40
- );
35
+ const matches = (compareKey) => {
36
+ // If compareKey is a string, look it up in KEYS object
37
+ const keyToCompare =
38
+ typeof compareKey === "string" ? KEYS[compareKey] : compareKey;
39
+
40
+ // Compare either code or keyCode properties
41
+ return (
42
+ keyToCompare?.code === key.code || keyToCompare?.keyCode === key.keyCode
43
+ );
44
+ };
41
45
 
42
- const matchesAny = (...args) => R.any(matches, args);
46
+ const matchesAny = (...args) => args.some(matches);
43
47
 
44
48
  const direction = () =>
45
49
  matchesAny(...ARROW_KEYS) ? directionHelper(key) : null;
@@ -77,6 +77,10 @@ const web = {
77
77
  Exit: { code: "Exit", keyCode: null },
78
78
  Escape: { code: "Escape", keyCode: 27 },
79
79
  EscapeDevice: { code: "Escape", keyCode: 27 },
80
+ // For Debugging
81
+ Z: { code: "KeyZ", keyCode: 90 }, // Hardware Backward Button
82
+ X: { code: "KeyX", keyCode: 88 }, // Hardware Enter Button
83
+ C: { code: "KeyC", keyCode: 67 }, // Hardware Forward Button
80
84
  };
81
85
 
82
86
  const android_tv = {
@@ -0,0 +1,68 @@
1
+ export type SeekKeyHandlerListener = {
2
+ onRewindPress: () => boolean;
3
+ onForwardPress: () => boolean;
4
+ onRewindPressOut: () => void;
5
+ onForwardPressOut: () => void;
6
+ };
7
+
8
+ export class KeyInputHandler {
9
+ private static instance: KeyInputHandler;
10
+
11
+ public static getInstance() {
12
+ if (!KeyInputHandler.instance) {
13
+ KeyInputHandler.instance = new KeyInputHandler();
14
+ }
15
+
16
+ return KeyInputHandler.instance;
17
+ }
18
+
19
+ constructor() {
20
+ this.listeners = [];
21
+ }
22
+
23
+ private listeners: SeekKeyHandlerListener[] = [];
24
+
25
+ addListener = (listener: SeekKeyHandlerListener) => {
26
+ this.listeners.splice(0, 0, listener);
27
+
28
+ return () => this.removeListener(listener);
29
+ };
30
+
31
+ removeListener = (listener: SeekKeyHandlerListener) => {
32
+ this.listeners = this.listeners.filter((l) => l !== listener);
33
+ };
34
+
35
+ onRewindPress = () => {
36
+ for (const element of this.listeners) {
37
+ if (element.onRewindPress()) {
38
+ break;
39
+ }
40
+ }
41
+ };
42
+
43
+ onForwardPress = () => {
44
+ for (const element of this.listeners) {
45
+ if (element.onForwardPress()) {
46
+ break;
47
+ }
48
+ }
49
+ };
50
+
51
+ // TODO: Event should be send to listeners who handled down event
52
+ onRewindPressOut = () => {
53
+ this.listeners.forEach((listener) => listener.onRewindPressOut());
54
+ };
55
+
56
+ onForwardPressOut = () => {
57
+ this.listeners.forEach((listener) => listener.onForwardPressOut());
58
+ };
59
+
60
+ getSeekMethods = () => {
61
+ return {
62
+ onForwardPress: this.onForwardPress,
63
+ onRewindPress: this.onRewindPress,
64
+ onForwardPressOut: this.onForwardPressOut,
65
+ onRewindPressOut: this.onRewindPressOut,
66
+ };
67
+ };
68
+ }
@@ -65,6 +65,37 @@ export function getUILanguage() {
65
65
  return getLanguageCode();
66
66
  }
67
67
 
68
+ /**
69
+ * fallback to en for missing dayjslocalization
70
+ * converts the locale code to a dayjs compatible locale code
71
+ * lowercase input code is expected, e.g. "en-GB" => "en-gb"
72
+ * @param {string} code - locale code, e.g. "en", "fr", "es-LA"
73
+ * */
74
+ export const toDayJSLocaleMap = (code) => {
75
+ const map = {
76
+ hy: "hy-am",
77
+ fj: null,
78
+ la: null,
79
+ no: null,
80
+ pa: "pa-in",
81
+ qu: null,
82
+ sm: null,
83
+ to: null,
84
+ xh: null,
85
+ tt: null,
86
+ "es-LA": "es",
87
+ "en-UK": "en-gb",
88
+ };
89
+
90
+ if (map[code] === null) {
91
+ return "en";
92
+ } else if (map[code]) {
93
+ return map[code];
94
+ } else {
95
+ return code.toLowerCase();
96
+ }
97
+ };
98
+
68
99
  /**
69
100
  * returns the localization for the app based on the locale. If the locale is null
70
101
  * or undefined, or if the given locale doesn't exist in the localizations,
@@ -1,8 +1,8 @@
1
- /// <reference types="@types/react-native" />
2
1
  import * as ReactNative from "react-native";
3
- import { isTV, platformSelect } from "../reactUtils";
4
2
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
- import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
3
+
4
+ import { isTV, platformSelect } from "../reactUtils";
5
+ import { useIsTablet } from "../reactHooks";
6
6
 
7
7
  import {
8
8
  QUICK_BRICK_EVENTS,
@@ -121,8 +121,15 @@ export const removeOrientationChangeListener = (listener: any): void => {
121
121
  */
122
122
  export const isOrientationCompatible = ({
123
123
  orientation,
124
- layoutData: { width, height },
124
+ layoutData: { width, height, isTablet, isTabletPortrait },
125
125
  }) => {
126
+ // We count portrait as compatible, because if default in zapp for mobile layout
127
+ // We count landscape as compatible, because if isTabletPortrait is false,
128
+ // it defaults for device
129
+ if (isTablet && !isTabletPortrait) {
130
+ return true;
131
+ }
132
+
126
133
  const isLandscape = width > height;
127
134
 
128
135
  if (
@@ -146,15 +153,6 @@ export const isOrientationCompatible = ({
146
153
  return true;
147
154
  };
148
155
 
149
- export const useIsOrientationCompatible = (orientation) => {
150
- const { height, width } = ReactNative.useWindowDimensions();
151
-
152
- return isOrientationCompatible({
153
- orientation,
154
- layoutData: { width, height },
155
- });
156
- };
157
-
158
156
  /**
159
157
  * Determine final screen orientation based on device type,
160
158
  * settings and orientation value provided by screen data settings
@@ -23,3 +23,17 @@ export const PLATFORMS = {
23
23
  export const PLATFORM_KEYS = {
24
24
  deviceClosedCaptioningEnabled: "device_closed_captioning_enabled",
25
25
  };
26
+
27
+ export enum ZappPlatform {
28
+ Android = "android",
29
+ AndroidTV = "android_tv",
30
+ AmazonFireTV = "amazon",
31
+ iOS = "ios",
32
+ tvOS = "tvos",
33
+ Roku = "roku",
34
+ Tizen = "tizen",
35
+ Web = "web",
36
+ WebOS = "webos",
37
+ Vizio = "vizio",
38
+ Unknown = "unknown",
39
+ }