@applicaster/zapp-react-native-utils 13.0.0-rc.6 → 13.0.0-rc.61

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 -10
  2. package/actionsExecutor/ActionExecutor.ts +16 -9
  3. package/actionsExecutor/ActionExecutorContext.tsx +74 -9
  4. package/alertUtils/alert.ts +148 -0
  5. package/alertUtils/index.ts +1 -148
  6. package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
  7. package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
  8. package/analyticsUtils/currentScreenProperities.ts +6 -4
  9. package/analyticsUtils/helpers/hooks.ts +1 -1
  10. package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
  11. package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +10 -8
  12. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +693 -0
  13. package/appUtils/HooksManager/__tests__/hook.test.js +196 -184
  14. package/appUtils/HooksManager/__tests__/hooksManager.test.js +96 -99
  15. package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
  16. package/appUtils/HooksManager/{index.js → index.ts} +122 -113
  17. package/appUtils/HooksManager/types.ts +119 -0
  18. package/appUtils/RiverFocusManager/index.js +17 -12
  19. package/appUtils/accessibilityManager/index.ts +42 -1
  20. package/appUtils/contextKeysManager/contextResolver.ts +77 -0
  21. package/appUtils/contextKeysManager/index.ts +2 -2
  22. package/appUtils/contextKeysManager/utils/index.ts +2 -1
  23. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +7 -6
  24. package/appUtils/focusManager/index.ts +100 -74
  25. package/appUtils/focusManager/treeDataStructure/Tree/index.js +25 -3
  26. package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -3
  27. package/appUtils/focusManager/utils/enums.ts +4 -0
  28. package/appUtils/focusManagerAux/utils/index.ts +4 -3
  29. package/appUtils/{index.js → index.ts} +1 -1
  30. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
  31. package/appUtils/keyCodes/__tests__/keyCodes.test.js +4 -4
  32. package/appUtils/keyCodes/{index.js → index.ts} +13 -7
  33. package/appUtils/keyCodes/keys/index.ts +3 -0
  34. package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
  35. package/appUtils/keyCodes/keys/keys.android.ts +18 -0
  36. package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
  37. package/appUtils/keyCodes/keys/keys.ts +15 -0
  38. package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +4 -0
  39. package/appUtils/keyCodes/keysByPlatform.ts +8 -0
  40. package/appUtils/keyInputHandler/KeyInputHandler.ts +68 -0
  41. package/appUtils/orientationHelper.ts +1 -2
  42. package/appUtils/platform/const.ts +14 -0
  43. package/appUtils/platform/platformUtils.ts +47 -18
  44. package/appUtils/playerManager/index.ts +7 -3
  45. package/appUtils/playerManager/player.ts +19 -7
  46. package/appUtils/playerManager/playerNative.ts +3 -2
  47. package/appUtils/playerManager/usePlayerControllerSetup.tsx +3 -3
  48. package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
  49. package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
  50. package/equalUtils/__tests__/isEqual/combination.test.ts +263 -0
  51. package/equalUtils/index.ts +49 -30
  52. package/focusManager/FocusManager.ts +11 -1
  53. package/focusManager/index.ts +41 -4
  54. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
  55. package/functionUtils/index.ts +36 -19
  56. package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
  57. package/idleUtils/index.ts +1 -1
  58. package/idleUtils/withTimeout.ts +15 -2
  59. package/logger/const.ts +9 -67
  60. package/manifestUtils/__tests__/createConfig.test.js +16 -16
  61. package/manifestUtils/fonts/index.js +5 -6
  62. package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
  63. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +875 -0
  64. package/manifestUtils/sharedConfiguration/screenPicker/utils.js +130 -0
  65. package/modalState/index.ts +1 -5
  66. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +6 -6
  67. package/navigationUtils/__tests__/itemTypes.test.js +2 -2
  68. package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
  69. package/navigationUtils/index.ts +17 -8
  70. package/numberUtils/index.ts +7 -5
  71. package/package.json +5 -9
  72. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  73. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +6 -8
  74. package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
  75. package/reactHooks/cell-click/__tests__/index.test.js +10 -2
  76. package/reactHooks/cell-click/index.ts +7 -1
  77. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
  78. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
  79. package/reactHooks/feed/useFeedLoader.tsx +1 -1
  80. package/reactHooks/feed/usePipesCacheReset.ts +5 -5
  81. package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
  82. package/reactHooks/layout/__tests__/index.test.tsx +10 -10
  83. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
  84. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
  85. package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
  86. package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
  87. package/reactHooks/navigation/__tests__/index.test.tsx +6 -21
  88. package/reactHooks/navigation/index.ts +38 -239
  89. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
  90. package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
  91. package/reactHooks/navigation/useGetTabBarHeight.ts +10 -0
  92. package/reactHooks/navigation/useIsScreenActive.ts +17 -0
  93. package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
  94. package/reactHooks/navigation/useNavigationType.ts +15 -0
  95. package/reactHooks/navigation/usePathname.ts +4 -0
  96. package/reactHooks/navigation/useProfilerLogging.ts +15 -0
  97. package/reactHooks/navigation/useRoute.ts +100 -0
  98. package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
  99. package/reactHooks/player/TVSeekControlller/ISeekController.ts +32 -0
  100. package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +71 -0
  101. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +258 -0
  102. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +53 -0
  103. package/reactHooks/player/__tests__/{useAutoSeek.test.tsx → useAutoSeek._test.tsx} +2 -3
  104. package/reactHooks/player/const.ts +0 -4
  105. package/reactHooks/player/index.ts +0 -6
  106. package/reactHooks/state/useRefWithInitialValue.ts +14 -0
  107. package/reactHooks/state/useStateFromSubscribe.ts +23 -0
  108. package/reactHooks/usePluginConfiguration.ts +1 -1
  109. package/reactHooks/useSubscriberFor.ts +4 -3
  110. package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
  111. package/reactUtils/__tests__/reactUtils.test.js +1 -37
  112. package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
  113. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +13 -79
  114. package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +2 -2
  115. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
  116. package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
  117. package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
  118. package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
  119. package/reactUtils/createContext/__tests__/{index.test.js → index.test.ts} +2 -2
  120. package/reactUtils/createContext/__tests__/provider.test.js +4 -6
  121. package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
  122. package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
  123. package/reactUtils/createContext/helpers.ts +6 -1
  124. package/reactUtils/createContext/index.ts +11 -25
  125. package/reactUtils/index.ts +0 -25
  126. package/screenState/index.ts +24 -10
  127. package/testUtils/index.tsx +0 -10
  128. package/theme/__mocks__/index.ts +4 -0
  129. package/theme/index.ts +1 -1
  130. package/time/Timer.ts +5 -2
  131. package/types.ts +1 -0
  132. package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
  133. package/appUtils/keyCodes/keys.ios.js +0 -46
  134. package/reactHooks/player/useAutoSeek.tsx +0 -87
  135. package/reactHooks/player/useSeek.tsx +0 -66
  136. package/reactHooks/player/useTapSeek.tsx +0 -58
  137. package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
  138. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  139. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  140. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  141. /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
  142. /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
  143. /package/domUtils/{logger.js → logger.ts} +0 -0
  144. /package/reactHooks/player/__tests__/{useTapSeek.test.ts → useTapSeek._test.ts} +0 -0
@@ -59,7 +59,7 @@ export class Tree {
59
59
  /**
60
60
  * Update node with provided data from newNode
61
61
  * @private
62
- * @param {Object} node to update
62
+ * @param {Object} currentNode to update
63
63
  * @param {Object} newNode node to take data from
64
64
  */
65
65
  updateNode(currentNode, newNode) {
@@ -175,7 +175,6 @@ export class Tree {
175
175
  * @private
176
176
  * @param {Object} node to check
177
177
  * @returns True if node has defined groupID
178
- * @returns False if node has NOT defined groupID
179
178
  */
180
179
  hasGroupID(node) {
181
180
  return node.component && node.component.props.groupId;
@@ -191,7 +190,7 @@ export class Tree {
191
190
  const findChildNode = this.findInTree(node.component.props.groupId);
192
191
 
193
192
  if (findChildNode) {
194
- this.root.children.pop(findChildNode);
193
+ this.root.children.pop();
195
194
  node.children.push(findChildNode);
196
195
  }
197
196
  }
@@ -232,4 +231,27 @@ export class Tree {
232
231
 
233
232
  return retVal;
234
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
+ }
235
257
  }
@@ -191,7 +191,6 @@ const toDistance = ({ target, direction, comparisonRect }) => {
191
191
  * Get target in shortest distance in specified direction
192
192
  * @private
193
193
  * @param {Object} direction of the navigation, which led to this focus change
194
- * @param {Object} excludedNode node which should be excluded from target group
195
194
  * @param {Array} targetNodes array of input target nodes
196
195
  * @param {Object} comparisonRect rectangle to compare sides in direction.
197
196
  * @returns {Object} target in direction.
@@ -300,8 +299,7 @@ export function findFocusableNode(direction, currentFocusNode) {
300
299
  * @public
301
300
  * @param {Object} direction of the navigation, which led to this focus change
302
301
  * @param {Object} component - object to check props.prioritiseFocusOn
303
- * @returns {Boolean} TRUE if object has priority in direction.
304
- * @returns {Boolean} FALSE if object has NO priority in direction.
302
+ * @returns {Boolean} TRUE if object has priority in direction. FALSE if object has NO priority in direction.
305
303
  */
306
304
  export function hasPriorityInDirection(direction, component) {
307
305
  const { prioritiseFocusOn = 0 } = component.props;
@@ -0,0 +1,4 @@
1
+ export enum ACTION {
2
+ FOCUS = "focus",
3
+ BLUR = "blur",
4
+ }
@@ -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
 
@@ -9,7 +9,7 @@ export { KEYS, ARROW_KEYS } from "./keyCodes/keys";
9
9
 
10
10
  export { playerManager } from "./playerManager";
11
11
 
12
- export { accessibilityManager } from "./accessibilityManager";
12
+ export { AccessibilityManager } from "./accessibilityManager";
13
13
 
14
14
  export function deprecationMessage(fn, replacement) {
15
15
  if (__DEV__ && process.env.NODE_ENV !== "test") {
@@ -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
 
@@ -13,7 +13,7 @@ const globalTizen = {
13
13
  describe("keyCodes", () => {
14
14
  describe("keyCode", () => {
15
15
  const { keyCode } = require("..");
16
- const { KEYS } = require("../keys.web");
16
+ const { KEYS } = require("../keys/keys.web");
17
17
 
18
18
  it("returns an object for a given key event", () => {
19
19
  expect(keyCode(KEYS.Enter)).toMatchSnapshot();
@@ -63,7 +63,7 @@ describe("keyCodes", () => {
63
63
  it("uses the proper keyCode for the environment", () => {
64
64
  Platform.OS = "samsung_tv";
65
65
  const { keyCode } = require("../");
66
- const { KEYS } = require("../keys.web");
66
+ const { KEYS } = require("../keys/keys.web");
67
67
 
68
68
  expect(globalTizen.tvinputdevice.registerKeyBatch).toHaveBeenCalledWith([
69
69
  "MediaPlay",
@@ -97,7 +97,7 @@ describe("keyCodes", () => {
97
97
  it("uses the proper keyCode for the environment", () => {
98
98
  Platform.OS = "lg_tv";
99
99
  const { keyCode } = require("..");
100
- const { KEYS } = require("../keys.web");
100
+ const { KEYS } = require("../keys/keys.web");
101
101
  // Back is reserved on LG
102
102
  expect(keyCode({ keyCode: 417 }).matches(KEYS.Forward)).toBeTruthy();
103
103
  });
@@ -1,11 +1,11 @@
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
 
6
4
  import { directionHelper } from "../directionHelper";
7
5
  import { KEYS, ARROW_KEYS } from "./keys";
8
6
 
7
+ export { KEYS, ARROW_KEYS, keyEventMapper } from "./keys";
8
+
9
9
  /**
10
10
  * @typedef KeyCode
11
11
  * @property {Object} value of the Key
@@ -32,12 +32,18 @@ export function keyCode(key) {
32
32
  keyCode(keyEvent).matchesAny("Enter", "Backspace", KEYS.ArrowLeft, ...)
33
33
  */
34
34
 
35
- const matches = R.compose(
36
- R.either(R.eqProps("code", key), R.eqProps("keyCode", key)),
37
- R.when(R.is(String), R.prop(R.__, KEYS))
38
- );
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
+ };
39
45
 
40
- const matchesAny = (...args) => R.any(matches, args);
46
+ const matchesAny = (...args) => args.some(matches);
41
47
 
42
48
  const direction = () =>
43
49
  matchesAny(...ARROW_KEYS) ? directionHelper(key) : null;
@@ -0,0 +1,3 @@
1
+ export * from "./keys";
2
+
3
+ export { keyEventMapper } from "./keyEventMapper";
@@ -1,22 +1,3 @@
1
- export const KEYS = {
2
- Enter: { code: "Enter", keyCode: 13 },
3
- EnterKeyboard: { code: "EnterKeyboard", keyCode: 66 },
4
- Back: { code: "Back", keyCode: 8 },
5
- Space: { code: "Space", keyCode: 32 },
6
- m: { code: "m", keyCode: 77 },
7
- ArrowLeft: { code: "ArrowLeft", keyCode: 37 },
8
- ArrowUp: { code: "ArrowUp", keyCode: 38 },
9
- ArrowRight: { code: "ArrowRight", keyCode: 39 },
10
- ArrowDown: { code: "ArrowDown", keyCode: 40 },
11
- };
12
-
13
- export const ARROW_KEYS = [
14
- KEYS.ArrowUp,
15
- KEYS.ArrowLeft,
16
- KEYS.ArrowDown,
17
- KEYS.ArrowRight,
18
- ];
19
-
20
1
  export function keyEventMapper(keyCode) {
21
2
  switch (keyCode) {
22
3
  case 19: // ArrowUp
@@ -0,0 +1,18 @@
1
+ export const KEYS = {
2
+ Enter: { code: "Enter", keyCode: 13 },
3
+ EnterKeyboard: { code: "EnterKeyboard", keyCode: 66 },
4
+ Back: { code: "Back", keyCode: 8 },
5
+ Space: { code: "Space", keyCode: 32 },
6
+ m: { code: "m", keyCode: 77 },
7
+ ArrowLeft: { code: "ArrowLeft", keyCode: 37 },
8
+ ArrowUp: { code: "ArrowUp", keyCode: 38 },
9
+ ArrowRight: { code: "ArrowRight", keyCode: 39 },
10
+ ArrowDown: { code: "ArrowDown", keyCode: 40 },
11
+ };
12
+
13
+ export const ARROW_KEYS = [
14
+ KEYS.ArrowUp,
15
+ KEYS.ArrowLeft,
16
+ KEYS.ArrowDown,
17
+ KEYS.ArrowRight,
18
+ ];
@@ -0,0 +1,25 @@
1
+ export const KEYS = {
2
+ Enter: { code: "Enter", keyCode: 13 },
3
+ Space: { code: "Space", keyCode: 32 },
4
+ m: { code: "m", keyCode: 77 },
5
+ ArrowLeft: { code: "ArrowLeft", keyCode: 37 },
6
+ ArrowUp: { code: "ArrowUp", keyCode: 38 },
7
+ ArrowRight: { code: "ArrowRight", keyCode: 39 },
8
+ ArrowDown: { code: "ArrowDown", keyCode: 40 },
9
+ Back: { code: "Backspace", keyCode: 8 },
10
+ Play: { code: "PlayPause", keyCode: 179 },
11
+ Pause: { code: "Pause", keyCode: 179 },
12
+ Forward: { code: "Forward", keyCode: 176 },
13
+ Rewind: { code: "Rewind", keyCode: 177 },
14
+ VolumeUp: { code: "VolumeUp", keyCode: 175 },
15
+ VolumeDown: { code: "VolumeDown", keyCode: 174 },
16
+ ToggleMute: { code: "VolumeMute", keyCode: 173 },
17
+ Stop: { code: "Q", keyCode: 81 },
18
+ };
19
+
20
+ export const ARROW_KEYS = [
21
+ KEYS.ArrowUp,
22
+ KEYS.ArrowLeft,
23
+ KEYS.ArrowDown,
24
+ KEYS.ArrowRight,
25
+ ];
@@ -0,0 +1,15 @@
1
+ // dummy file to make sure the folder structure is created
2
+ export const KEYS: Record<string, { code: string; keyCode: number }> = {
3
+ ArrowLeft: { code: "ArrowLeft", keyCode: -1 },
4
+ ArrowUp: { code: "ArrowUp", keyCode: -1 },
5
+ ArrowRight: { code: "ArrowRight", keyCode: -1 },
6
+ ArrowDown: { code: "ArrowDown", keyCode: -1 },
7
+ Back: { code: "Back", keyCode: -1 },
8
+ };
9
+
10
+ export const ARROW_KEYS = [
11
+ KEYS.ArrowUp,
12
+ KEYS.ArrowLeft,
13
+ KEYS.ArrowDown,
14
+ KEYS.ArrowRight,
15
+ ];
@@ -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,8 @@
1
+ export {
2
+ lg_tv,
3
+ samsung_tv,
4
+ web,
5
+ tvos,
6
+ android_tv,
7
+ vizio,
8
+ } from "./keys/keysByPlatform";
@@ -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
+ }
@@ -1,8 +1,7 @@
1
- /// <reference types="@types/react-native" />
2
1
  import * as ReactNative from "react-native";
3
2
  import { isTV, platformSelect } from "../reactUtils";
4
3
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
- import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
4
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks/device/useIsTablet";
6
5
 
7
6
  import {
8
7
  QUICK_BRICK_EVENTS,
@@ -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
+ }
@@ -6,22 +6,10 @@ import {
6
6
  isSamsungPlatform,
7
7
  } from "@applicaster/zapp-react-dom-app/App/Loader/utils/platform";
8
8
 
9
- import { PLATFORM_KEYS, PLATFORMS } from "./const";
9
+ import { PLATFORM_KEYS, PLATFORMS, ZappPlatform } from "./const";
10
10
  import { createLogger, utilsLogger } from "../../logger";
11
-
12
- declare global {
13
- interface Window {
14
- VIZIO?: {
15
- exitApplication: () => void;
16
- Chromevox: {
17
- play: (text: string) => void;
18
- };
19
- setClosedCaptionHandler: (
20
- callback: (isCCEnabled: boolean) => void
21
- ) => void;
22
- };
23
- }
24
- }
11
+ import { getPlatform } from "../../reactUtils";
12
+ import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
25
13
 
26
14
  const { log_debug } = createLogger({
27
15
  category: "General",
@@ -29,6 +17,20 @@ const { log_debug } = createLogger({
29
17
  parent: utilsLogger,
30
18
  });
31
19
 
20
+ export const getZappPlatform = (): ZappPlatform => {
21
+ const platform = appStore.get("appData")?.platform;
22
+
23
+ switch (platform) {
24
+ case "amazon_fire_tv":
25
+ return ZappPlatform.AmazonFireTV;
26
+ case ZappPlatform.Vizio:
27
+ return ZappPlatform.Vizio;
28
+ default:
29
+ // TODO: Use only ZappPlatform enum
30
+ return getPlatform() as ZappPlatform;
31
+ }
32
+ };
33
+
32
34
  export const getUserAgent = () => window?.navigator?.userAgent;
33
35
 
34
36
  /** VIZIO UTILS */
@@ -75,13 +77,22 @@ export class ClosedCaptioningManager {
75
77
 
76
78
  private constructor() {
77
79
  this.initialize();
80
+
81
+ window["vizioDebug"] = {
82
+ setCCEnabled: (isCCEnabled: boolean) => {
83
+ this.ccState$.next(isCCEnabled);
84
+ },
85
+ };
78
86
  }
79
87
 
80
88
  static getInstance(): ClosedCaptioningManager {
81
89
  if (!ClosedCaptioningManager.ccManagerInstance) {
90
+ log_debug("ClosedCaptioningManager: Creating new instance");
82
91
  ClosedCaptioningManager.ccManagerInstance = new ClosedCaptioningManager();
83
92
  }
84
93
 
94
+ log_debug("ClosedCaptioningManager: Returning existing instance");
95
+
85
96
  return ClosedCaptioningManager.ccManagerInstance;
86
97
  }
87
98
 
@@ -90,23 +101,41 @@ export class ClosedCaptioningManager {
90
101
  PLATFORM_KEYS.deviceClosedCaptioningEnabled
91
102
  );
92
103
 
93
- log_debug("Initializing closed captioning manager", { existingState });
94
-
95
104
  if (existingState !== null) {
105
+ log_debug(
106
+ `ClosedCaptioningManager: Setting initial state to ${existingState}`
107
+ );
108
+
96
109
  this.ccState$.next(existingState === "true" || existingState === true);
110
+ } else {
111
+ log_debug("ClosedCaptioningManager: No existing state found");
97
112
  }
98
113
 
99
114
  if (hasVizioAPIs()) {
115
+ log_debug("ClosedCaptioningManager: Setting closed captioning handler");
116
+
100
117
  window.VIZIO!.setClosedCaptionHandler((isCCEnabled) => {
101
- log_debug("CC state changed", { isCCEnabled });
118
+ log_debug(
119
+ "ClosedCaptioningManager: There was a change in the CC state",
120
+ { isCCEnabled }
121
+ );
102
122
 
103
123
  sessionStorage.setItem(
104
124
  PLATFORM_KEYS.deviceClosedCaptioningEnabled,
105
125
  isCCEnabled
106
126
  );
107
127
 
128
+ log_debug(
129
+ "ClosedCaptioningManager: Saved CC state to session storage",
130
+ { isCCEnabled }
131
+ );
132
+
108
133
  this.ccState$.next(isCCEnabled);
109
134
  });
135
+ } else {
136
+ log_debug(
137
+ "ClosedCaptioningManager: Vizio APIs are not available, skipping setup"
138
+ );
110
139
  }
111
140
  }
112
141
 
@@ -169,8 +169,12 @@ export class PlayerManager {
169
169
  this.addPlayer(id, playerController);
170
170
 
171
171
  if (playerController.playerRole === PlayerRole.Primary) {
172
- if (playerController.playerPluginId !== "QuickBrickPlayerPlugin") {
173
- RNPlayerManager?.["QuickBrickPlayerPlugin"]?.stopBackgroundPlayback?.();
172
+ if (RNPlayerManager) {
173
+ Object.keys(RNPlayerManager).forEach((pluginId) => {
174
+ if (pluginId !== playerController.playerPluginId) {
175
+ RNPlayerManager[pluginId]?.stopBackgroundPlayback?.();
176
+ }
177
+ });
174
178
  }
175
179
 
176
180
  playerController.addListener({
@@ -448,7 +452,7 @@ export class PlayerManager {
448
452
  if (this.castingReceiver) {
449
453
  this.castingReceiver.closeNativePlayer();
450
454
  } else {
451
- this.getActivePlayer().closeNativePlayer();
455
+ this.getActivePlayer()?.closeNativePlayer();
452
456
  this.invokeHandler("close");
453
457
  }
454
458
  }
@@ -5,6 +5,7 @@ import { createLogger, utilsLogger } from "../../logger";
5
5
  import { getBoolFromConfigValue } from "../../configurationUtils";
6
6
  import { PlayerRole, SharedPlayerCallBacksKeys } from "./conts";
7
7
  import { OverlaysObserver } from "./OverlayObserver/OverlaysObserver";
8
+ import { TVSeekController } from "../../reactHooks/player/TVSeekControlller/TVSeekController";
8
9
 
9
10
  const logger = createLogger({
10
11
  category: "PlayerController",
@@ -49,6 +50,8 @@ export class Player {
49
50
  [key: string]: QuickBrickPlayer.SharedPlayerCallBacks;
50
51
  } = {};
51
52
 
53
+ public seekController?: TVSeekController | null;
54
+
52
55
  get entry(): ZappEntry | null {
53
56
  return this._entry;
54
57
  }
@@ -59,6 +62,8 @@ export class Player {
59
62
  }
60
63
 
61
64
  constructor(props) {
65
+ this.seekController = null;
66
+
62
67
  this.playerState = {
63
68
  // We want isLive null by default to be able not to show controls if we do not know type
64
69
  isLive: null,
@@ -91,6 +96,8 @@ export class Player {
91
96
  this.onVideoProgress = this.onVideoProgress.bind(this);
92
97
  this.onVideoLoad = this.onVideoLoad.bind(this);
93
98
  this.isFullScreenSupported = this.isFullScreenSupported.bind(this);
99
+ this.onPlayerSeekComplete = this.onPlayerSeekComplete.bind(this);
100
+
94
101
  this.entryObservable = null;
95
102
  }
96
103
 
@@ -147,8 +154,8 @@ export class Player {
147
154
  this.isSeeking()
148
155
  ? this.playerState.seekPosition
149
156
  : this.playerState.adState
150
- ? this.playerState.adState.adPosition
151
- : this.playerState.contentPosition;
157
+ ? this.playerState.adState.adPosition
158
+ : this.playerState.contentPosition;
152
159
 
153
160
  getEntry = (): ZappEntry | null => this.entry;
154
161
 
@@ -321,7 +328,7 @@ export class Player {
321
328
  * You should use dispatchPlayerEvent at PlayerContainer or call playerManager.invokeHandler directly
322
329
  * This allows you to use one method for everything.
323
330
  */
324
- onSeekStart = (event: QuickBrickPlayer.OnSeekStartEvent) => {
331
+ onPlayerSeekStart = (event: QuickBrickPlayer.OnSeekStartEvent) => {
325
332
  const { isLive, seekableDuration, currentTime, duration, seekTime } = event;
326
333
 
327
334
  this.setPlaybackState({ currentTime, duration, seekableDuration, isLive });
@@ -336,10 +343,11 @@ export class Player {
336
343
  });
337
344
  };
338
345
 
339
- onSeekComplete = (event: QuickBrickPlayer.OnSeekCompleteEvent) => {
346
+ onPlayerSeekComplete = (event: QuickBrickPlayer.OnSeekCompleteEvent) => {
340
347
  const { isLive, seekableDuration, currentTime, duration } = event;
341
348
 
342
349
  this.setPlaybackState({ currentTime, duration, seekableDuration, isLive });
350
+
343
351
  this.playerState.seekPosition = null;
344
352
 
345
353
  if (!isLive) {
@@ -620,14 +628,18 @@ export class Player {
620
628
  selectTrack = (_track) => {};
621
629
 
622
630
  protected notifyPlayHeadPositionUpdate = () => {
631
+ const currentTime = this.getPosition();
632
+
623
633
  const event = {
624
- currentTime: this.getPosition(),
634
+ currentTime,
625
635
  duration: this.getDuration(),
626
636
  item: this.getEntry(),
627
637
  seekableDuration: this.playerState.seekableDuration,
628
638
  isLive: this.isLive(),
629
639
  };
630
640
 
641
+ this.seekController?.onVideoProgress(currentTime);
642
+
631
643
  this.invokeListenersUpdate({
632
644
  callbackName: SharedPlayerCallBacksKeys.OnVideoProgress,
633
645
  event,
@@ -653,8 +665,8 @@ export class Player {
653
665
  };
654
666
 
655
667
  public getListener = (): QuickBrickPlayer.SharedPlayerCallBacks => ({
656
- onPlayerSeekStart: this.onSeekStart,
657
- onPlayerSeekComplete: this.onSeekComplete,
668
+ onPlayerSeekStart: this.onPlayerSeekStart,
669
+ onPlayerSeekComplete: this.onPlayerSeekComplete,
658
670
  onVideoEnd: this.onVideoEnd,
659
671
  onError: this.onError,
660
672
  onVideoError: this.onVideoError,
@@ -58,7 +58,7 @@ export class PlayerNative extends Player {
58
58
  return this.playerState.contentPosition;
59
59
  };
60
60
 
61
- currentPlayerComponent = (): ReactComponent<PlayerProps> =>
61
+ currentPlayerComponent = (): ReactComponent<PlayerRefProps> =>
62
62
  this.playerComponent?.current;
63
63
 
64
64
  getState = () => this.playerState;
@@ -222,9 +222,10 @@ export class PlayerNative extends Player {
222
222
 
223
223
  onVideoLoad = (event) => {
224
224
  if (this.startPosition) {
225
- super.onVideoLoad({ ...event, ignoreContinueWatching: true });
226
225
  this.seekTo(this.startPosition);
227
226
  this.startPosition = null;
227
+
228
+ super.onVideoLoad({ ...event, ignoreContinueWatching: true });
228
229
  } else {
229
230
  super.onVideoLoad(event);
230
231
  }