@applicaster/zapp-react-native-utils 15.0.0-alpha.4153840309 → 15.0.0-alpha.4200413542

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 (125) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +3 -6
  2. package/actionsExecutor/feedDecorator.ts +6 -6
  3. package/adsUtils/__tests__/createVMAP.test.ts +419 -0
  4. package/adsUtils/index.ts +2 -2
  5. package/analyticsUtils/README.md +1 -1
  6. package/analyticsUtils/analyticsMapper.ts +10 -2
  7. package/appDataUtils/__tests__/urlScheme.test.ts +678 -0
  8. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +0 -188
  9. package/appUtils/HooksManager/__tests__/hooksManager.test.js +16 -2
  10. package/appUtils/HooksManager/index.ts +10 -10
  11. package/appUtils/RiverFocusManager/{index.js → index.ts} +25 -18
  12. package/appUtils/accessibilityManager/__tests__/utils.test.ts +360 -0
  13. package/appUtils/accessibilityManager/const.ts +4 -0
  14. package/appUtils/accessibilityManager/utils.ts +25 -5
  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/focusManagerAux/utils/index.ios.ts +122 -0
  22. package/appUtils/focusManagerAux/utils/index.ts +1 -1
  23. package/appUtils/focusManagerAux/utils/utils.ios.ts +199 -3
  24. package/appUtils/keyCodes/keys/keys.web.ts +1 -4
  25. package/appUtils/orientationHelper.ts +2 -4
  26. package/appUtils/platform/platformUtils.ts +1 -1
  27. package/appUtils/playerManager/player.ts +4 -0
  28. package/appUtils/playerManager/playerNative.ts +29 -16
  29. package/appUtils/playerManager/usePlayerState.tsx +14 -2
  30. package/cloudEventsUtils/__tests__/index.test.ts +529 -0
  31. package/cloudEventsUtils/index.ts +65 -1
  32. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +38 -0
  33. package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
  34. package/configurationUtils/index.ts +17 -11
  35. package/dateUtils/__tests__/dayjs.test.ts +330 -0
  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/defaultManifestConfigurations/player.js +125 -11
  47. package/manifestUtils/platformIsTV.js +13 -0
  48. package/manifestUtils/tvAction/container/index.js +1 -1
  49. package/navigationUtils/index.ts +15 -5
  50. package/numberUtils/__tests__/toNumber.test.ts +12 -0
  51. package/numberUtils/__tests__/toPositiveNumber.test.ts +165 -0
  52. package/numberUtils/index.ts +19 -1
  53. package/package.json +4 -4
  54. package/pluginUtils/index.ts +4 -0
  55. package/reactHooks/advertising/index.ts +2 -2
  56. package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +537 -0
  57. package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
  58. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  59. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
  60. package/reactHooks/cell-click/__tests__/index.test.js +1 -3
  61. package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
  62. package/reactHooks/connection/__tests__/index.test.js +1 -1
  63. package/reactHooks/debugging/__tests__/index.test.js +4 -4
  64. package/reactHooks/dev/__tests__/useReRenderLog.test.ts +188 -0
  65. package/reactHooks/device/useIsTablet.tsx +14 -19
  66. package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
  67. package/reactHooks/events/index.ts +20 -0
  68. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
  69. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
  70. package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +4 -1
  71. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
  72. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +1 -1
  73. package/reactHooks/feed/__tests__/{useInflatedUrl.test.ts → useInflatedUrl.test.tsx} +62 -7
  74. package/reactHooks/feed/useEntryScreenId.ts +2 -2
  75. package/reactHooks/feed/useInflatedUrl.ts +43 -17
  76. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +13 -16
  77. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +12 -8
  78. package/reactHooks/index.ts +2 -0
  79. package/reactHooks/layout/__tests__/index.test.tsx +1 -1
  80. package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
  81. package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.ts → useDimensions.test.tsx} +105 -25
  82. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
  83. package/reactHooks/navigation/__tests__/index.test.tsx +40 -9
  84. package/reactHooks/navigation/index.ts +27 -11
  85. package/reactHooks/navigation/useRoute.ts +11 -7
  86. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +27 -10
  87. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
  88. package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
  89. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
  90. package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
  91. package/reactHooks/resolvers/useCellResolver.ts +6 -2
  92. package/reactHooks/resolvers/useComponentResolver.ts +8 -2
  93. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
  94. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
  95. package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
  96. package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +12 -4
  97. package/reactHooks/screen/useTargetScreenData.ts +4 -2
  98. package/reactHooks/state/useRefWithInitialValue.ts +10 -0
  99. package/reactHooks/state/useRivers.ts +1 -1
  100. package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +580 -0
  101. package/reactHooks/usePluginConfiguration.ts +2 -2
  102. package/reactHooks/utils/__tests__/index.test.js +1 -1
  103. package/rectUtils/__tests__/index.test.ts +549 -0
  104. package/rectUtils/index.ts +2 -2
  105. package/screenPickerUtils/__tests__/index.test.ts +333 -0
  106. package/screenState/__tests__/index.test.ts +1 -1
  107. package/searchUtils/const.ts +7 -0
  108. package/searchUtils/index.ts +3 -0
  109. package/services/storageServiceSync.web.ts +1 -1
  110. package/stringUtils/index.ts +1 -1
  111. package/testUtils/index.tsx +30 -21
  112. package/time/__tests__/BackgroundTimer.test.ts +156 -0
  113. package/time/__tests__/Timer.test.ts +236 -0
  114. package/typeGuards/__tests__/isString.test.ts +21 -0
  115. package/typeGuards/index.ts +4 -0
  116. package/utils/__tests__/mergeRight.test.ts +48 -0
  117. package/utils/__tests__/selectors.test.ts +124 -0
  118. package/utils/index.ts +13 -0
  119. package/utils/mergeRight.ts +5 -0
  120. package/utils/path.ts +6 -3
  121. package/utils/pathOr.ts +5 -1
  122. package/utils/selectors.ts +46 -0
  123. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +34 -11
  124. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
  125. package/reactHooks/componentsMap/index.ts +0 -55
@@ -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
  })();
@@ -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
+ };
@@ -102,7 +102,7 @@ export const getNavbarNode = (focusableTree) => {
102
102
 
103
103
  export const waitForContent = (focusableTree) => {
104
104
  const contentHasAnyChildren = (): boolean => {
105
- const countOfChildren = pathOr(
105
+ const countOfChildren = pathOr<number>(
106
106
  0,
107
107
  ["children", "length"],
108
108
  getContentNode(focusableTree)
@@ -1,7 +1,9 @@
1
- import { ReplaySubject } from "rxjs";
2
- import { filter } from "rxjs/operators";
1
+ import { ReplaySubject, Subject } from "rxjs";
2
+ import { filter, switchMap, take, withLatestFrom, map } from "rxjs/operators";
3
+
3
4
  import { BUTTON_PREFIX } from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const";
4
5
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
6
+ import { isPartOfMenu, isPartOfContent } from "./index.ios";
5
7
 
6
8
  type FocusableID = string;
7
9
  type RegistrationEvent = {
@@ -9,6 +11,25 @@ type RegistrationEvent = {
9
11
  registered: boolean;
10
12
  };
11
13
 
14
+ let focusableViewRegistrationSubject$ = new Subject<{
15
+ id: FocusableID;
16
+ groupId: FocusableID;
17
+ }>();
18
+
19
+ let focusableGroupRegistrationSubject$ = new ReplaySubject<{
20
+ id: FocusableID;
21
+ }>();
22
+
23
+ let focusableNativeViewRegistrationSubject$ = new Subject<{
24
+ id: FocusableID;
25
+ groupId: FocusableID;
26
+ }>();
27
+
28
+ let focusableNativeGroupRegistrationSubject$ = new ReplaySubject<{
29
+ id: FocusableID;
30
+ groupId: FocusableID;
31
+ }>();
32
+
12
33
  const isFocusableButton = (id: Option<FocusableID>): boolean =>
13
34
  id && id.includes?.(BUTTON_PREFIX);
14
35
 
@@ -22,14 +43,189 @@ export const focusableButtonsRegistration$ = (focusableGroupId: string) =>
22
43
  )
23
44
  );
24
45
 
25
- export const emitRegistered = (id: Option<FocusableID>): void => {
46
+ export const resetFocusableRegistration = () => {
47
+ // complete the old subject so subscribers are notified and resources are freed
48
+ if (!focusableViewRegistrationSubject$.closed) {
49
+ focusableViewRegistrationSubject$.complete();
50
+ }
51
+
52
+ if (!focusableGroupRegistrationSubject$.closed) {
53
+ focusableGroupRegistrationSubject$.complete();
54
+ }
55
+
56
+ if (!focusableNativeViewRegistrationSubject$.closed) {
57
+ focusableNativeViewRegistrationSubject$.complete();
58
+ }
59
+
60
+ if (!focusableNativeGroupRegistrationSubject$.closed) {
61
+ focusableNativeGroupRegistrationSubject$.complete();
62
+ }
63
+
64
+ focusableViewRegistrationSubject$ = new Subject<{
65
+ id: FocusableID;
66
+ groupId: FocusableID;
67
+ }>();
68
+
69
+ focusableGroupRegistrationSubject$ = new ReplaySubject<{
70
+ id: FocusableID;
71
+ }>();
72
+
73
+ focusableNativeViewRegistrationSubject$ = new Subject<{
74
+ id: FocusableID;
75
+ groupId: FocusableID;
76
+ }>();
77
+
78
+ focusableNativeGroupRegistrationSubject$ = new ReplaySubject<{
79
+ id: FocusableID;
80
+ groupId: FocusableID;
81
+ }>();
82
+ };
83
+
84
+ const focusableNativeViewRegistration = ({ focusableView, focusableGroup }) => {
85
+ return focusableNativeViewRegistrationSubject$.pipe(
86
+ filter(
87
+ (focusableNativeView) => focusableNativeView.id === focusableView.id
88
+ ),
89
+ take(1),
90
+ switchMap((focusableNativeView) =>
91
+ // start waiting registration of its parent FocusableNativeGroup
92
+ focusableNativeGroupRegistrationSubject$.pipe(
93
+ filter(
94
+ (focusableNativeGroup) =>
95
+ focusableNativeGroup.id === focusableNativeView.groupId
96
+ ),
97
+ take(1),
98
+ map((focusableNativeGroup) => ({
99
+ focusableNativeGroup,
100
+ focusableNativeView,
101
+ focusableView,
102
+ focusableGroup,
103
+ }))
104
+ )
105
+ )
106
+ );
107
+ };
108
+
109
+ export const firstFocusableViewRegistrationFactory = () =>
110
+ focusableViewRegistrationSubject$.pipe(
111
+ take(1), // we care about only first FocusableView registration
112
+ switchMap((focusableView) =>
113
+ // start waiting registration of its parent FocusableGroup
114
+ focusableGroupRegistrationSubject$.pipe(
115
+ filter((focusableGroup) => focusableGroup.id === focusableView.groupId),
116
+ take(1),
117
+ map((focusableGroup) => ({
118
+ focusableView,
119
+ focusableGroup,
120
+ }))
121
+ )
122
+ ),
123
+ // start waiting registration for FocusableNativeView and its parent FocusableNativeGroup
124
+ switchMap(({ focusableView, focusableGroup }) =>
125
+ focusableNativeViewRegistration({
126
+ focusableView,
127
+ focusableGroup,
128
+ })
129
+ )
130
+ );
131
+
132
+ // registration on RN level(into RN focusManager)
133
+ export const emitRegistered = ({
134
+ id,
135
+ groupId,
136
+ isGroup,
137
+ }: {
138
+ id: Option<FocusableID>;
139
+ groupId: Option<FocusableID>;
140
+ isGroup: boolean;
141
+ }): void => {
26
142
  if (isFocusableButton(id)) {
27
143
  registeredSubject$.next({ id, registered: true });
28
144
  }
145
+
146
+ if (isGroup && id) {
147
+ focusableGroupRegistrationSubject$.next({ id });
148
+ }
149
+
150
+ if (!isGroup && id && groupId) {
151
+ focusableViewRegistrationSubject$.next({ id, groupId });
152
+ }
29
153
  };
30
154
 
155
+ // unregistration on RN level(into RN focusManager)
31
156
  export const emitUnregistered = (id: Option<FocusableID>): void => {
32
157
  if (isFocusableButton(id)) {
33
158
  registeredSubject$.next({ id, registered: false });
34
159
  }
35
160
  };
161
+
162
+ // registration focusableNativeView and focusableNativeGroup
163
+ export const emitNativeRegistered = ({
164
+ id,
165
+ groupId,
166
+ isGroup,
167
+ }: {
168
+ id: Option<FocusableID>;
169
+ groupId: Option<FocusableID>;
170
+ isGroup: boolean;
171
+ }): void => {
172
+ if (!isGroup && id && groupId) {
173
+ focusableNativeViewRegistrationSubject$.next({ id, groupId });
174
+ }
175
+
176
+ if (isGroup && id && groupId) {
177
+ focusableNativeGroupRegistrationSubject$.next({ id, groupId });
178
+ }
179
+ };
180
+
181
+ // /////
182
+
183
+ const focusedSubject$ = new Subject<FocusableID>();
184
+
185
+ const focused$ = focusedSubject$.asObservable();
186
+
187
+ export const emitFocused = (id: FocusableID): void => {
188
+ focusedSubject$.next(id);
189
+ };
190
+
191
+ export const topMenuItemFocused$ = focused$.pipe(
192
+ filter((id) => id && isPartOfMenu(focusManager.focusableTree, id))
193
+ );
194
+
195
+ export const contentFocused$ = focused$.pipe(
196
+ filter((id) => {
197
+ const isContent = isPartOfContent(focusManager.focusableTree, id);
198
+
199
+ return id && isContent;
200
+ })
201
+ );
202
+
203
+ const createFocusableRegistry = () => {
204
+ const subject$ = new ReplaySubject<FocusableID | undefined>(1);
205
+
206
+ return {
207
+ observable$: subject$.asObservable(),
208
+ register: (id: FocusableID) => {
209
+ // save focusable_id on registration
210
+ subject$.next(id);
211
+ },
212
+ unregister: () => {
213
+ // reset focusable_id on unregistration
214
+ subject$.next(undefined);
215
+ },
216
+ };
217
+ };
218
+
219
+ /// HOME_TOP_MENU_ITEM
220
+ export const HomeTopMenuItemRegistry = createFocusableRegistry();
221
+
222
+ export const homeTopMenuItemFocused$ = topMenuItemFocused$.pipe(
223
+ withLatestFrom(HomeTopMenuItemRegistry.observable$),
224
+ filter(([id, homeId]) => id === homeId)
225
+ );
226
+
227
+ /// SCREEN_PICKER
228
+ export const ScreenPickerContentContainerRegistry = createFocusableRegistry();
229
+
230
+ /// SEARCH_INPUT
231
+ export const SearchInputRegistry = createFocusableRegistry();
@@ -10,10 +10,7 @@ import { Platform } from "react-native";
10
10
  * platformKeys[Platform.OS] should only include keys
11
11
  * that are unique to that platform, i.e. Exit: { keyCode: 10182 }
12
12
  */
13
- export const KEYS = Object.assign(
14
- platformKeys["web"],
15
- platformKeys[Platform.OS]
16
- );
13
+ export const KEYS = Object.assign(platformKeys.web, platformKeys[Platform.OS]);
17
14
 
18
15
  export const ARROW_KEYS = [
19
16
  KEYS.ArrowUp,
@@ -1,5 +1,5 @@
1
1
  import * as ReactNative from "react-native";
2
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
2
+ import { useAppData } from "@applicaster/zapp-react-native-redux/hooks";
3
3
 
4
4
  import { isTV, platformSelect } from "../reactUtils";
5
5
  import { useIsTablet } from "../reactHooks";
@@ -184,9 +184,7 @@ export const getScreenOrientation = ({
184
184
 
185
185
  export const useGetScreenOrientation = (screenData) => {
186
186
  const isTablet = useIsTablet();
187
-
188
- const { appData } = usePickFromState(["appData"]);
189
- const isTabletPortrait = appData?.isTabletPortrait;
187
+ const { isTabletPortrait } = useAppData();
190
188
 
191
189
  return getScreenOrientation({
192
190
  screenData,
@@ -79,7 +79,7 @@ export class ClosedCaptioningManager {
79
79
  private constructor() {
80
80
  this.initialize();
81
81
 
82
- window["vizioDebug"] = {
82
+ window.vizioDebug = {
83
83
  setCCEnabled: (isCCEnabled: boolean) => {
84
84
  this.ccState$.next(isCCEnabled);
85
85
  },
@@ -250,6 +250,10 @@ export class Player {
250
250
  return false;
251
251
  }
252
252
 
253
+ if (!Number.isFinite(duration)) {
254
+ return this.getSeekableDuration() > 0;
255
+ }
256
+
253
257
  return duration > 0;
254
258
  };
255
259