@applicaster/zapp-react-native-utils 12.0.0-alpha.5251355865 → 12.0.0-alpha.5350212300

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 (41) hide show
  1. package/analyticsUtils/helpers/hooks.ts +1 -1
  2. package/appUtils/HooksManager/index.js +3 -1
  3. package/appUtils/focusManager/index.ts +128 -25
  4. package/appUtils/focusManager/treeDataStructure/Utils/index.js +97 -33
  5. package/appUtils/focusManager/utils/index.ts +65 -0
  6. package/appUtils/playerManager/conts.ts +5 -0
  7. package/appUtils/playerManager/player.ts +35 -0
  8. package/appUtils/playerManager/playerFactory.ts +12 -10
  9. package/appUtils/playerManager/playerNative.ts +9 -0
  10. package/cellUtils/index.ts +5 -3
  11. package/focusManager/index.ts +1 -1
  12. package/localizationUtils/index.ts +6 -3
  13. package/logger/index.ts +1 -1
  14. package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
  15. package/manifestUtils/defaultManifestConfigurations/player.js +2 -1
  16. package/manifestUtils/index.js +5 -1
  17. package/manifestUtils/secondaryImage/index.js +18 -15
  18. package/package.json +2 -2
  19. package/playerUtils/configurationGenerator.ts +3 -1
  20. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
  21. package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
  22. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
  23. package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +44 -29
  24. package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
  25. package/reactHooks/layout/types/index.ts +2 -0
  26. package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
  27. package/reactHooks/navigation/__mocks__/index.ts +15 -0
  28. package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
  29. package/reactHooks/navigation/index.ts +84 -19
  30. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
  31. package/reactHooks/screen/useScreenContext.ts +4 -3
  32. package/reactHooks/screen/useScreenData.ts +1 -1
  33. package/reactHooks/screen/useTargetScreenData.ts +7 -0
  34. package/reactHooks/state/useRivers.ts +3 -1
  35. package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
  36. package/reactUtils/helpers.tsx +1 -1
  37. package/riverComponetsMeasurementProvider/index.tsx +2 -2
  38. package/screenState/index.ts +0 -8
  39. package/stringUtils/index.ts +5 -0
  40. package/testUtils/getByTestId.ts +14 -0
  41. package/testUtils/index.tsx +24 -22
@@ -26,7 +26,7 @@ export const useHookAnalytics = (props): void => {
26
26
  if (screenData?.hookPlugin?.module?.runInBackground) {
27
27
  sendScreenEvent({ analyticsScreenData });
28
28
  }
29
- }, [hookPlugin.screen_id]);
29
+ }, [sendScreenEvent, screenData, hookPlugin.screen_id]);
30
30
  };
31
31
 
32
32
  export const useScreenAnalytics = (props) => {
@@ -270,7 +270,9 @@ export function HooksManager({
270
270
  });
271
271
 
272
272
  const isHookInHomescreen = payload?.home;
273
- const isHookFlowBlocker = hookPlugin?.module?.isFlowBlocker();
273
+
274
+ const isHookFlowBlocker =
275
+ hookPlugin?.module?.isFlowBlocker?.() ?? false;
274
276
 
275
277
  // TODO: We technically need to also support the case where there is more than one hook in the stack,
276
278
  // if needed we could add differentiation explicit user cancellation (when we should go to background) and other types of cancellation
@@ -1,9 +1,20 @@
1
1
  import * as R from "ramda";
2
+ import {
3
+ isNilOrEmpty,
4
+ isNotNil,
5
+ } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
6
+
2
7
  import { Tree } from "./treeDataStructure/Tree";
3
8
  import { findFocusableNode, findPriorityItem } from "./treeDataStructure/Utils";
4
9
  import { subscriber } from "../../functionUtils";
5
10
  import * as FOCUS_EVENTS from "./events";
6
11
  import { coreLogger } from "../../logger";
12
+ import {
13
+ waitForActiveScreen,
14
+ waitForContent,
15
+ getContentNode,
16
+ getActiveScreen,
17
+ } from "./utils";
7
18
 
8
19
  const logger = coreLogger.addSubsystem("focusManager");
9
20
 
@@ -46,7 +57,7 @@ export const focusManager = (function () {
46
57
  let lastFocusedId = null;
47
58
  let initialFocusOnContent = null;
48
59
  let focusDisabled = false;
49
- let focusAutoEnabled = true;
60
+ let focusAutoEnabled = false;
50
61
  const eventHandler = subscriber();
51
62
 
52
63
  function disableAutomaticFocus() {
@@ -93,7 +104,7 @@ export const focusManager = (function () {
93
104
  !currentFocusable.isGroup &&
94
105
  currentFocusable.isMounted()
95
106
  ) {
96
- currentFocusable.blur(currentFocusable, direction);
107
+ currentFocusable.blur(direction);
97
108
  currentFocusable.hasLostFocus(getCurrentFocus(), direction);
98
109
  }
99
110
  }
@@ -219,38 +230,55 @@ export const focusManager = (function () {
219
230
  /**
220
231
  * sets the initial focus when the screen loads, or when focus is lost
221
232
  */
222
- function setInitialFocus(lastAddedParentNode?: any) {
233
+ function setInitialFocus(lastAddedParentNode?: any, fallback?: () => void) {
223
234
  const preferredFocus = findPriorityItem(
224
235
  lastAddedParentNode?.children || focusableTree.root.children
225
236
  );
226
237
 
227
238
  logger.log({ message: "setInitialFocus", data: { preferredFocus } });
228
239
 
229
- if (preferredFocus && preferredFocus.length > 0) {
230
- const focusCandidate = preferredFocus[0];
231
- const focusableItem = focusCandidate.component;
232
-
233
- if (
234
- focusableItem &&
235
- !focusableItem.isGroup &&
236
- isFocusEnabled(focusableItem)
237
- ) {
238
- if (initialFocusOnContent) {
239
- blur();
240
- initialFocusOnContent = false;
241
- }
240
+ if (isNilOrEmpty(preferredFocus)) {
241
+ fallback?.();
242
+ return;
243
+ }
242
244
 
243
- logger.log({
244
- message: "setInitialFocus – currentFocusNode",
245
- data: {
246
- focusableItem,
247
- },
248
- });
245
+ const focusCandidate = preferredFocus[0];
246
+ const focusableItem = focusCandidate.component;
249
247
 
250
- currentFocusNode = focusCandidate;
251
- focusableItem.willReceiveFocus(focusableItem, null);
252
- focusableItem && setFocus(focusCandidate.id, null);
248
+ const currentFocusedId = R.path(["props", "id"], getCurrentFocus());
249
+ const nextFocusedId = R.path(["props", "id"], focusableItem);
250
+
251
+ if (currentFocusedId === nextFocusedId) {
252
+ // we are going to move focus to the same node(already focused) -> nothing to do
253
+ return;
254
+ }
255
+
256
+ if (preferredFocus.length === 1 && focusableItem && focusableItem.isGroup) {
257
+ // we've found only one FocusableGroup as a candidate to focus, try to use fallback
258
+ fallback?.();
259
+ return;
260
+ }
261
+
262
+ if (
263
+ focusableItem &&
264
+ !focusableItem.isGroup &&
265
+ isFocusEnabled(focusableItem)
266
+ ) {
267
+ if (initialFocusOnContent) {
268
+ blur();
269
+ initialFocusOnContent = false;
253
270
  }
271
+
272
+ logger.log({
273
+ message: "setInitialFocus – currentFocusNode",
274
+ data: {
275
+ focusableItem,
276
+ },
277
+ });
278
+
279
+ currentFocusNode = focusCandidate;
280
+ focusableItem.willReceiveFocus(focusableItem, null);
281
+ focusableItem && setFocus(focusCandidate.id, null);
254
282
  }
255
283
  }
256
284
 
@@ -323,6 +351,8 @@ export const focusManager = (function () {
323
351
  * @returns {React.Component} the registered component
324
352
  */
325
353
  function registerGroup(id, component) {
354
+ // console.log("debug_2", "FM registerGroup", { id, component });
355
+
326
356
  focusableTree.addNode({ id, component });
327
357
 
328
358
  return component;
@@ -338,6 +368,8 @@ export const focusManager = (function () {
338
368
  * @returns {React.Component} the registered component
339
369
  */
340
370
  function registerItem(id, component) {
371
+ // console.log("debug_2", "FM registerItem", { id, component });
372
+
341
373
  focusableTree.addNode({ id, component });
342
374
 
343
375
  if (!currentFocusNode && id === lastFocusedId && !id.includes("menu")) {
@@ -354,6 +386,8 @@ export const focusManager = (function () {
354
386
  * @param {String} id of the group to unregister
355
387
  */
356
388
  function unregisterGroup(id) {
389
+ // console.log("debug_2", "FM unregisterGroup", { id });
390
+
357
391
  focusableTree.findAndRemoveNode(id);
358
392
  }
359
393
 
@@ -365,6 +399,8 @@ export const focusManager = (function () {
365
399
  * @param {String} id of the Focusable to unregister
366
400
  */
367
401
  function unregisterItem(id) {
402
+ // console.log("debug_2", "FM unregisterItem", { id });
403
+
368
404
  focusableTree.findAndRemoveNode(id);
369
405
 
370
406
  if (currentFocusNode && currentFocusNode.id === id) {
@@ -403,6 +439,8 @@ export const focusManager = (function () {
403
439
  }
404
440
 
405
441
  function setupInitialFocus(id: string, focusOnContent?: boolean) {
442
+ console.log("debug_2", "FM - setupInitialFocus", { id, focusOnContent });
443
+
406
444
  if (focusOnContent) {
407
445
  initialFocusOnContent = focusOnContent;
408
446
  } else {
@@ -410,6 +448,62 @@ export const focusManager = (function () {
410
448
  }
411
449
  }
412
450
 
451
+ async function setFocusOnMenu(currentRoute: string) {
452
+ console.log("debug_2", "FM - setFocusOnMenu", { currentRoute });
453
+
454
+ try {
455
+ await waitForActiveScreen(currentRoute, focusableTree);
456
+
457
+ const navbar = R.find(
458
+ (child) => R.startsWith("navbar", child.id),
459
+ getActiveScreen(focusableTree).children
460
+ );
461
+
462
+ const selectedMenuItemId = R.find(
463
+ (child) => child.component.props.selected,
464
+ navbar?.children || []
465
+ )?.id;
466
+
467
+ if (selectedMenuItemId) {
468
+ // set focus on selected menu item
469
+ setFocus(selectedMenuItemId);
470
+ return;
471
+ }
472
+
473
+ const firstMenuItemId = R.path(["children", 0, "id"], navbar);
474
+
475
+ if (firstMenuItemId) {
476
+ // set focus on first menu item
477
+ setFocus(firstMenuItemId);
478
+ }
479
+ } catch (error) {
480
+ // failed to put focus on top-menu => log it
481
+ }
482
+ }
483
+
484
+ async function setFocusOnContent(currentRoute: string) {
485
+ console.log("debug_2", "FM - setFocusOnContent", { currentRoute });
486
+
487
+ // we need to put focus on content here
488
+ // For doing it we have to wait registration of first focusable node into focusableTree
489
+ // this is the temporary hack with waiting until content node starts having children
490
+ try {
491
+ await waitForActiveScreen(currentRoute, focusableTree);
492
+ await waitForContent(focusableTree);
493
+
494
+ const content = getContentNode(focusableTree);
495
+
496
+ const fallback = () => {
497
+ throw Error("Failed to land focus on content");
498
+ };
499
+
500
+ setInitialFocus(content, fallback);
501
+ } catch (e) {
502
+ // we've failed to put initial focus on content, put focus on menu as a last chance
503
+ setFocusOnMenu(currentRoute);
504
+ }
505
+ }
506
+
413
507
  /**
414
508
  * moves the focus in a provided direction
415
509
  * @public
@@ -422,6 +516,13 @@ export const focusManager = (function () {
422
516
  currentFocusNode.component.willLoseFocus(currentFocusNode, direction);
423
517
  const nextFocusable = findFocusableNode(direction, currentFocusNode);
424
518
 
519
+ // console.log("debug_2", "FM moveFocus", {
520
+ // direction: direction?.value,
521
+ // currentFocusNode,
522
+ // focusableTree,
523
+ // nextFocusable,
524
+ // });
525
+
425
526
  if (nextFocusable?.component?.isMounted?.()) {
426
527
  const nextRect = nextFocusable.component.getRect();
427
528
 
@@ -518,5 +619,7 @@ export const focusManager = (function () {
518
619
  enableAutomaticFocus,
519
620
  getGroupRootById,
520
621
  isGroupItemFocused,
622
+ setFocusOnMenu,
623
+ setFocusOnContent,
521
624
  };
522
625
  })();
@@ -1,4 +1,4 @@
1
- import * as R from "ramda";
1
+ import { allPass, pathOr, startsWith, isNil } from "ramda";
2
2
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
3
3
 
4
4
  import { rejectItemByPath } from "../../../../objectUtils";
@@ -143,7 +143,49 @@ const hasChildren = (target) =>
143
143
 
144
144
  const hasComponent = (target) => !!target?.component;
145
145
 
146
- const hasComponentAndChildren = R.allPass([hasComponent, hasChildren]);
146
+ const hasComponentAndChildren = allPass([hasComponent, hasChildren]);
147
+
148
+ const positiveOrZero = (value) => value >= 0;
149
+
150
+ const excludeFromFocusSearching = pathOr(false, [
151
+ "component",
152
+ "props",
153
+ "excludeFromFocusSearching",
154
+ ]);
155
+
156
+ // const isAllScreens = all(excludeFromFocusSearching);
157
+
158
+ const isNavBar = (target) => startsWith("navbar", target?.id);
159
+
160
+ const ignoreMenuIfMultipleTargets = (nodes) => {
161
+ if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
162
+ return nodes.filter((n) => !isNavBar(n?.target));
163
+ }
164
+
165
+ return nodes;
166
+ };
167
+
168
+ const toDistance = ({ target, direction, comparisonRect }) => {
169
+ const componentRect = target.component?.getRect();
170
+
171
+ const orientationDistance = measureDistance(
172
+ direction,
173
+ comparisonRect,
174
+ componentRect
175
+ );
176
+
177
+ if (orientationDistance === -1) {
178
+ // ignores offscreen targets when moving up/down
179
+ return null;
180
+ }
181
+
182
+ const euclidDistance = measureEuclidianDistanceToCenter(
183
+ comparisonRect,
184
+ componentRect
185
+ );
186
+
187
+ return orientationDistance > 0 ? euclidDistance : orientationDistance;
188
+ };
147
189
 
148
190
  /**
149
191
  * Get target in shortest distance in specified direction
@@ -173,43 +215,32 @@ export function getTargetInDirection(
173
215
 
174
216
  const distances = targets
175
217
  .filter(hasComponentAndChildren)
176
- .map((target) => {
177
- const componentRect = target.component?.getRect();
178
-
179
- const distance = measureDistance(
180
- direction,
181
- comparisonRect,
182
- componentRect
183
- );
184
-
185
- if (distance === -1) {
186
- // ignores offscreen targets when moving up/down
187
- return null;
188
- }
218
+ .filter((target) => !excludeFromFocusSearching(target))
219
+ .map((target) => ({
220
+ distance: toDistance({ target, direction, comparisonRect }),
221
+ target,
222
+ }))
223
+ .filter(({ distance }) => positiveOrZero(distance));
189
224
 
190
- return {
191
- distance:
192
- measureDistance(direction, comparisonRect, componentRect) +
193
- measureEuclidianDistanceToCenter(comparisonRect, componentRect) /
194
- 10000,
195
- target,
196
- };
197
- })
198
- .filter((obj) => obj && obj.distance >= 0);
225
+ // if (isAllScreens(targets)) {
226
+ // return last(targets);
227
+ // }
199
228
 
200
229
  const match = distances
201
230
  .sort((a, b) => a.distance - b.distance) // sorts the distances
202
231
  .filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
203
232
 
204
- return distances && distances.length > 0 ? match.target : null;
205
- }
206
-
207
- function ignoreMenuIfMultipleTargets(nodes) {
208
- if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
209
- return nodes.filter((n) => n?.target?.id !== "navbar");
210
- }
211
-
212
- return nodes;
233
+ // console.log("debug_2", "FM getTargetInDirection", {
234
+ // direction: direction?.value,
235
+ // currentNode,
236
+ // targetNodes,
237
+ // targets,
238
+ // comparisonRect,
239
+ // distances,
240
+ // match,
241
+ // });
242
+
243
+ return match?.target;
213
244
  }
214
245
 
215
246
  /**
@@ -300,3 +331,36 @@ export function hasPriorityInDirection(direction, component) {
300
331
 
301
332
  return (prioritiseFocusOn & mappedDirection) > 0;
302
333
  }
334
+
335
+ export const getNodeBeforeRoot = (node) => {
336
+ if (isNil(node)) {
337
+ return undefined;
338
+ }
339
+
340
+ if (node.parent.id === "root") {
341
+ return node;
342
+ }
343
+
344
+ return getNodeBeforeRoot(node.parent);
345
+ };
346
+
347
+ export const isAllowedToMoveTo = (currentNode, nextNode) => {
348
+ const screenForCurrentNode = getNodeBeforeRoot(currentNode);
349
+ const screenForNextNode = getNodeBeforeRoot(nextNode);
350
+
351
+ return screenForCurrentNode.id === screenForNextNode.id;
352
+
353
+ // console.log("debug_2", "isAllowedToMoveTo", {
354
+ // nodeBeforeRoot,
355
+ // });
356
+
357
+ // if (nodeBeforeRoot?.id === "navbar") {
358
+ // return true;
359
+ // }
360
+
361
+ // if (nodeBeforeRoot?.component?.props?.isActive) {
362
+ // return true;
363
+ // }
364
+
365
+ // return false;
366
+ };
@@ -0,0 +1,65 @@
1
+ import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
2
+ import { find, last, startsWith } from "ramda";
3
+
4
+ // run check each 300 ms
5
+ // run this check too often could lead to performance penalty on low-end devices
6
+ const HOW_OFTEN_TO_CHECK_CONDITION = 300; //ms
7
+
8
+ type Props = {
9
+ maxTimeout: number;
10
+ conditionFn: () => boolean;
11
+ };
12
+
13
+ export const waitUntil = ({ maxTimeout, conditionFn }: Props) => {
14
+ let interval;
15
+ return new Promise((resolve, reject) => {
16
+ interval = setInterval(() => {
17
+ if (conditionFn()) {
18
+ clearInterval(interval);
19
+ resolve(undefined);
20
+ }
21
+ }, HOW_OFTEN_TO_CHECK_CONDITION);
22
+
23
+ setTimeout(() => {
24
+ reject();
25
+ }, maxTimeout);
26
+ });
27
+ };
28
+
29
+ export const waitForActiveScreen = (currentRoute: string, focusableTree) => {
30
+ const isActiveScreenAvailableToFocus = (): boolean => {
31
+ const routes = focusableTree.root.children;
32
+
33
+ const route = find((route) => route.id === currentRoute, routes);
34
+ return isNotNil(route);
35
+ };
36
+
37
+ return waitUntil({
38
+ maxTimeout: 2500, //ms
39
+ conditionFn: isActiveScreenAvailableToFocus,
40
+ });
41
+ };
42
+
43
+ export const getActiveScreen = (focusableTree) => {
44
+ return last(focusableTree.root.children);
45
+ };
46
+
47
+ export const getContentNode = (focusableTree) => {
48
+ const content = find(
49
+ (child) => startsWith("content", child.id),
50
+ getActiveScreen(focusableTree)?.children
51
+ );
52
+
53
+ return content;
54
+ };
55
+
56
+ export const waitForContent = (focusableTree) => {
57
+ const contentHasAnyChildren = (): boolean => {
58
+ return getContentNode(focusableTree)?.children.length > 0;
59
+ };
60
+
61
+ return waitUntil({
62
+ maxTimeout: 2500, //ms
63
+ conditionFn: contentHasAnyChildren,
64
+ });
65
+ };
@@ -20,6 +20,11 @@ export enum SharedPlayerCallBacksKeys {
20
20
  OnPlayerMute = "onPlayerMute",
21
21
  OnPlayerUnmute = "onPlayerUnmute",
22
22
 
23
+ onVideoFullscreenPlayerDidDismiss = "onVideoFullscreenPlayerDidDismiss",
24
+ onVideoFullscreenPlayerDidPresent = "onVideoFullscreenPlayerDidPresent",
25
+ onVideoFullscreenPlayerWillDismiss = "onVideoFullscreenPlayerWillDismiss",
26
+ onVideoFullscreenPlayerWillPresent = "onVideoFullscreenPlayerWillPresent",
27
+
23
28
  OnAdBegin = "onAdBegin",
24
29
  OnAdBreakBegin = "onAdBreakBegin",
25
30
  OnAdBreakEnd = "onAdBreakEnd",
@@ -414,6 +414,32 @@ export class Player {
414
414
  });
415
415
  };
416
416
 
417
+ onVideoFullscreenPlayerWillDismiss = () => {
418
+ this.invokeListenersUpdate({
419
+ callbackName:
420
+ SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillDismiss,
421
+ });
422
+ };
423
+
424
+ onVideoFullscreenPlayerDidDismiss = () => {
425
+ this.invokeListenersUpdate({
426
+ callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidDismiss,
427
+ });
428
+ };
429
+
430
+ onVideoFullscreenPlayerWillPresent = () => {
431
+ this.invokeListenersUpdate({
432
+ callbackName:
433
+ SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillPresent,
434
+ });
435
+ };
436
+
437
+ onVideoFullscreenPlayerDidPresent = () => {
438
+ this.invokeListenersUpdate({
439
+ callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidPresent,
440
+ });
441
+ };
442
+
417
443
  public invokeListenersUpdate = ({
418
444
  callbackName,
419
445
  event = {},
@@ -634,6 +660,12 @@ export class Player {
634
660
  onBufferStart: this.onBufferStart,
635
661
  onBufferComplete: this.onBufferComplete,
636
662
 
663
+ onPlayerClose: this.onPlayerClose,
664
+ onVideoFullscreenPlayerWillPresent: this.onVideoFullscreenPlayerWillPresent,
665
+ onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
666
+ onVideoFullscreenPlayerWillDismiss: this.onVideoFullscreenPlayerWillDismiss,
667
+ onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
668
+
637
669
  // Ads Callbacks:
638
670
  onAdBegin: this.onAdBegin,
639
671
  onAdBreakBegin: this.onAdBreakBegin,
@@ -693,4 +725,7 @@ export class Player {
693
725
  isFullScreenSupported(): boolean {
694
726
  return true;
695
727
  }
728
+
729
+ public supportsNativeControls = (): boolean => false;
730
+ public supportNativeCast = (): boolean => false;
696
731
  }
@@ -9,21 +9,23 @@ type PlayerFactoryItem = {
9
9
  };
10
10
 
11
11
  type PlayerFactoryProps = {
12
- player: any,
13
- playerId: string,
14
- autoplay: boolean,
15
- entry: ZappEntry,
16
- muted: boolean,
17
- playerPluginId: string,
18
- screenConfig: Record<string, any>,
19
- playerRole: PlayerRole,
20
- }
12
+ player: any;
13
+ playerId: string;
14
+ autoplay: boolean;
15
+ entry: ZappEntry;
16
+ muted: boolean;
17
+ playerPluginId: string;
18
+ screenConfig: Record<string, any>;
19
+ playerRole: PlayerRole;
20
+ };
21
21
 
22
22
  interface PlayerConstructor {
23
23
  new (params: any): Player;
24
24
  }
25
25
 
26
- export const playerFactory = (config: PlayerFactoryProps): PlayerFactoryItem | null => {
26
+ export const playerFactory = (
27
+ config: PlayerFactoryProps
28
+ ): PlayerFactoryItem | null => {
27
29
  const IDENTIFIER = config?.playerPluginId;
28
30
 
29
31
  if (!IDENTIFIER) {
@@ -5,6 +5,7 @@ import { Player } from "./player";
5
5
  import { findNodeHandle, NativeModules } from "react-native";
6
6
  import { isApplePlatform } from "../../reactUtils";
7
7
  import { PlayerRole } from "./conts";
8
+ import { toBooleanWithDefaultFalse } from "../../booleanUtils";
8
9
 
9
10
  const RNPlayerManager = {
10
11
  QuickBrickPlayerPlugin: isApplePlatform()
@@ -291,4 +292,12 @@ export class PlayerNative extends Player {
291
292
 
292
293
  return !(isFullScreenAudioPlayer && this.isAudioItem());
293
294
  };
295
+
296
+ supportsNativeControls = (): boolean =>
297
+ toBooleanWithDefaultFalse(this.getConfig()?.["supports_native_controls"]);
298
+
299
+ supportNativeCast = (): boolean =>
300
+ toBooleanWithDefaultFalse(
301
+ this.playerPluginId === "quick-brick-player-brightcove"
302
+ );
294
303
  }
@@ -182,8 +182,8 @@ export const getHeight = (width, aspectRatio) => {
182
182
  * @returns {string} duration
183
183
  */
184
184
 
185
- export const getDurationInMinutes = (seconds: number): string => {
186
- if (seconds === (null || undefined)) return "00:00";
185
+ export const getDurationInMinutes = (seconds?: number): string => {
186
+ if (!seconds) return "00:00";
187
187
  const minutes = Math.floor(Number(seconds) / 60);
188
188
  const secondsReminder = Math.floor(Number(seconds)) % 60;
189
189
  const pad = (num: number): string => `${num < 10 ? "0" : ""}${num}`;
@@ -460,7 +460,9 @@ export const getColorFromData = ({
460
460
  }: GetColorFromData): string => {
461
461
  const value = getValue(key);
462
462
 
463
- if (validateColor(value)) {
463
+ // Temporary hack to fix color validation when alpha is floating point number
464
+ // https://github.com/dreamyguy/validate-color/issues/44
465
+ if (validateColor(value.replace(".00", ""))) {
464
466
  return value;
465
467
  }
466
468
 
@@ -86,7 +86,7 @@ export const isFocusable = (component: ZappEntry | ZappUIComponent | any) =>
86
86
 
87
87
  type Props = {
88
88
  component: ZappUIComponent;
89
- zappPipesData: ZappPipesData;
89
+ zappPipesData?: ZappPipesData;
90
90
  } & Record<string, any>;
91
91
 
92
92
  export const useIsFocusable = (props: Props): void => {
@@ -64,10 +64,13 @@ export function getIsRTLFromLocalizations(localizations): boolean {
64
64
  return R.contains(localeToUse, RTL_LOCALES);
65
65
  }
66
66
 
67
+ const remoteConfigLocalizationSelector = R.path([
68
+ "remoteConfigurations",
69
+ "localizations",
70
+ ]);
71
+
67
72
  export function useIsRTL(): boolean {
68
- const localizations = useSelector(
69
- R.path(["remoteConfigurations", "localizations"])
70
- );
73
+ const localizations = useSelector(remoteConfigLocalizationSelector);
71
74
 
72
75
  return getIsRTLFromLocalizations(localizations);
73
76
  }
package/logger/index.ts CHANGED
@@ -72,7 +72,7 @@ const { quickBrickLogger, coreLogger, logger, logLevels, utilsLogger } =
72
72
  function useLogger(
73
73
  logger: XRayLoggerI,
74
74
  eventData: UseLoggerHookArgs,
75
- deps = []
75
+ deps: string[] = []
76
76
  ) {
77
77
  const {
78
78
  level = logLevels.verbose,
@@ -245,6 +245,12 @@ const generalContent = () => ({
245
245
  ],
246
246
  initial_value: "reloading...",
247
247
  },
248
+ {
249
+ type: "text_input",
250
+ label: "Navigation Bar Screen Title",
251
+ key: "screen_title",
252
+ initial_value: null,
253
+ },
248
254
  ],
249
255
  },
250
256
  advertising: {