@applicaster/zapp-react-native-utils 12.0.0-alpha.3977160850 → 12.0.0-alpha.4023252383

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 (101) hide show
  1. package/analyticsUtils/AnalyticPlayerListener.ts +141 -0
  2. package/analyticsUtils/AnalyticsEvents/index.ts +13 -0
  3. package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
  4. package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
  5. package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
  6. package/analyticsUtils/analyticsPlugins.ts +25 -0
  7. package/analyticsUtils/events.ts +12 -0
  8. package/analyticsUtils/index.tsx +12 -0
  9. package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
  10. package/appUtils/HooksManager/index.js +3 -1
  11. package/appUtils/accessibilityManager/const.ts +50 -0
  12. package/appUtils/accessibilityManager/hooks.ts +40 -0
  13. package/appUtils/accessibilityManager/index.ts +166 -0
  14. package/appUtils/accessibilityManager/logger.ts +5 -0
  15. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
  16. package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
  17. package/appUtils/focusManager/__tests__/index.test.ts +2 -3
  18. package/appUtils/focusManager/index.ts +76 -57
  19. package/appUtils/focusManager/treeDataStructure/Utils/index.js +75 -35
  20. package/appUtils/focusManager/utils/index.ts +100 -0
  21. package/appUtils/focusManagerAux/index.ts +76 -0
  22. package/appUtils/focusManagerAux/utils/index.ts +100 -0
  23. package/appUtils/index.js +2 -0
  24. package/appUtils/keyCodes/keys.web.js +8 -0
  25. package/appUtils/keyCodes/keysByPlatform.js +11 -1
  26. package/appUtils/platform/const.ts +25 -0
  27. package/appUtils/platform/index.ts +3 -0
  28. package/appUtils/platform/platformUtils.ts +217 -0
  29. package/appUtils/playerManager/conts.ts +6 -0
  30. package/appUtils/playerManager/index.ts +20 -32
  31. package/appUtils/playerManager/nativePlayerManager.ts +12 -0
  32. package/appUtils/playerManager/player.ts +44 -3
  33. package/appUtils/playerManager/playerFactory.ts +13 -11
  34. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
  35. package/appUtils/playerManager/playerNative.ts +15 -15
  36. package/appUtils/playerManager/usePlayerControllerSetup.tsx +8 -2
  37. package/appUtils/playerManager/usePlayerState.tsx +19 -34
  38. package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
  39. package/arrayUtils/index.ts +5 -0
  40. package/booleanUtils/index.ts +4 -4
  41. package/cellUtils/__tests__/cellUtils.test.ts +9 -20
  42. package/cellUtils/index.ts +48 -19
  43. package/dateUtils/index.ts +11 -0
  44. package/functionUtils/index.ts +38 -0
  45. package/headersUtils/index.ts +1 -0
  46. package/index.d.ts +15 -0
  47. package/inflatedUrlUtils/index.ts +4 -4
  48. package/keyboardState/index.ts +0 -0
  49. package/localizationUtils/__tests__/localizationUtils.test.ts +72 -53
  50. package/localizationUtils/index.ts +13 -22
  51. package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
  52. package/manifestUtils/defaultManifestConfigurations/player.js +2 -1
  53. package/manifestUtils/index.js +5 -1
  54. package/manifestUtils/secondaryImage/index.js +18 -15
  55. package/navigationUtils/index.ts +56 -4
  56. package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
  57. package/numberUtils/__tests__/toNumber.test.ts +2 -2
  58. package/numberUtils/index.ts +9 -18
  59. package/package.json +2 -2
  60. package/playerUtils/configurationGenerator.ts +3 -1
  61. package/playerUtils/imaScriptDom.ts +59 -0
  62. package/pluginUtils/index.ts +9 -0
  63. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
  64. package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
  65. package/reactHooks/cell-click/__tests__/index.test.js +10 -1
  66. package/reactHooks/cell-click/index.ts +12 -12
  67. package/reactHooks/debugging/__tests__/index.test.js +81 -11
  68. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
  69. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
  70. package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
  71. package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
  72. package/reactHooks/feed/useFeedRefresh.tsx +9 -7
  73. package/reactHooks/feed/useInflatedUrl.ts +44 -15
  74. package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +44 -29
  75. package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
  76. package/reactHooks/layout/types/index.ts +2 -0
  77. package/reactHooks/layout/useDimensions/useDimensions.ts +15 -7
  78. package/reactHooks/navigation/__mocks__/index.ts +15 -0
  79. package/reactHooks/navigation/__mocks__/useNavigation.ts +7 -0
  80. package/reactHooks/navigation/__tests__/index.test.tsx +26 -8
  81. package/reactHooks/navigation/index.ts +64 -21
  82. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
  83. package/reactHooks/resolvers/useCellResolver.ts +5 -1
  84. package/reactHooks/screen/__mocks__/useScreenContext.ts +3 -1
  85. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
  86. package/reactHooks/screen/index.ts +2 -0
  87. package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
  88. package/reactHooks/screen/useScreenContext.ts +4 -4
  89. package/reactHooks/screen/useScreenData.ts +1 -1
  90. package/reactHooks/screen/useTargetScreenData.ts +7 -0
  91. package/reactHooks/useListenEventBusEvent.ts +1 -4
  92. package/reactHooks/videoModal/index.ts +0 -3
  93. package/reactUtils/index.ts +104 -0
  94. package/screenState/index.ts +0 -8
  95. package/stringUtils/index.ts +5 -0
  96. package/testUtils/getByTestId.ts +14 -0
  97. package/testUtils/index.tsx +38 -23
  98. package/types.ts +4 -0
  99. package/appUtils/focusManager/FocusableEvents.ts +0 -8
  100. package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
  101. package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
@@ -31,13 +31,12 @@ export const getKeyForLabel = (dataKey, customKey) => {
31
31
  * This method will return true if the argument passed to it is either empty or nil
32
32
  * The method prevents zero from being evaluated as falsey in an || condition
33
33
  * i.e. - NaN, null, {}, "", [], should all return true
34
- * @param {*} argument - argument passed to it could be any type
35
34
  * @returns {Boolean} returns true if empty or nil
35
+ * @param value
36
36
  */
37
- export const isEmptyOrNil = R.either(
38
- R.equals(NaN),
39
- R.either(R.isEmpty, R.isNil)
40
- );
37
+
38
+ export const isEmptyOrNil = (value: any): boolean =>
39
+ Number.isNaN(value) || value == null || R.isEmpty(value);
41
40
 
42
41
  /**
43
42
  * This method is used to find the appropriate label for any given text field.
@@ -182,8 +181,8 @@ export const getHeight = (width, aspectRatio) => {
182
181
  * @returns {string} duration
183
182
  */
184
183
 
185
- export const getDurationInMinutes = (seconds: number): string => {
186
- if (seconds === (null || undefined)) return "00:00";
184
+ export const getDurationInMinutes = (seconds?: number): string => {
185
+ if (!seconds) return "00:00";
187
186
  const minutes = Math.floor(Number(seconds) / 60);
188
187
  const secondsReminder = Math.floor(Number(seconds)) % 60;
189
188
  const pad = (num: number): string => `${num < 10 ? "0" : ""}${num}`;
@@ -320,6 +319,7 @@ export const ifElseFocusedSelected = (
320
319
  * @param {Object} stylesObj styles object with keys from zapp { button_icon_width: "10" }
321
320
  * @returns {*} value, returns any type that can be used as styles
322
321
  */
322
+
323
323
  export const getValue = (stylesObj) => (key) => {
324
324
  return R.propOr(null, key, stylesObj);
325
325
  };
@@ -447,28 +447,57 @@ export const toResizeMode = (
447
447
 
448
448
  type GetColorFromData = {
449
449
  data: Record<string, any>;
450
- key: string;
451
- getValue: Function;
452
- defaultColor: string;
450
+ valueFromLayout: string;
453
451
  };
454
452
 
455
453
  export const getColorFromData = ({
456
454
  data,
457
- key,
458
- getValue,
459
- defaultColor,
455
+ valueFromLayout,
460
456
  }: GetColorFromData): string => {
461
- const value = getValue(key);
462
-
463
- if (validateColor(value)) {
464
- return value;
457
+ // Temporary hack to fix color validation when alpha is floating point number
458
+ // https://github.com/dreamyguy/validate-color/issues/44
459
+ if (validateColor(valueFromLayout.replace(".00", ""))) {
460
+ return valueFromLayout;
465
461
  }
466
462
 
467
- const pathValue = R.path(value.split("."), data);
463
+ const pathValue = R.path(valueFromLayout.split("."), data);
468
464
 
469
465
  if (pathValue && validateColor(pathValue)) {
470
466
  return pathValue;
471
467
  }
472
468
 
473
- return defaultColor;
469
+ return valueFromLayout;
470
+ };
471
+
472
+ export const getImageStyles = ({
473
+ image,
474
+ value,
475
+ }: {
476
+ image: any;
477
+ value: any;
478
+ }) => {
479
+ return {
480
+ width: image.imageContentWidth,
481
+ height: image.imageContentHeight,
482
+ aspectRatio: image.aspectRatio,
483
+ borderRadius: toNumberWithDefaultZero(value("image_corner_radius")),
484
+ };
485
+ };
486
+
487
+ export const getImageContainerPaddingStyles = ({ value }: { value: any }) => {
488
+ return {
489
+ paddingTop: value("image_padding_top"),
490
+ paddingBottom: value("image_padding_bottom"),
491
+ paddingLeft: value("image_padding_left"),
492
+ paddingRight: value("image_padding_right"),
493
+ };
494
+ };
495
+
496
+ export const getImageContainerMarginStyles = ({ value }: { value: any }) => {
497
+ return {
498
+ marginTop: value("image_margin_top"),
499
+ marginBottom: value("image_margin_bottom"),
500
+ marginLeft: value("image_margin_left"),
501
+ marginRight: value("image_margin_right"),
502
+ };
474
503
  };
@@ -0,0 +1,11 @@
1
+ import dayjs from "dayjs";
2
+ import utc from "dayjs/plugin/utc";
3
+ import timezone from "dayjs/plugin/timezone";
4
+ import isoWeek from "dayjs/plugin/isoWeek";
5
+
6
+ // Extend dayjs with plugins
7
+ dayjs.extend(utc);
8
+ dayjs.extend(timezone);
9
+ dayjs.extend(isoWeek);
10
+
11
+ export { dayjs };
@@ -1,4 +1,42 @@
1
1
  import * as R from "ramda";
2
+ import { useRef } from "react";
3
+
4
+ /**
5
+ *
6
+ * @param callback - The function to throttle.
7
+ * @param delay - The delay in milliseconds.
8
+ * @returns A throttled version of the callback.
9
+ */
10
+ export const useThrottle = (callback: () => void, delay: number) => {
11
+ const lastCallRef = useRef<number>(0);
12
+ const timeoutRef = useRef<any | null>(null);
13
+
14
+ const throttledFunction = () => {
15
+ const now = Date.now();
16
+
17
+ if (now - lastCallRef.current >= delay) {
18
+ callback();
19
+ lastCallRef.current = now;
20
+ } else if (!timeoutRef.current) {
21
+ const remainingTime = delay - (now - lastCallRef.current);
22
+
23
+ timeoutRef.current = setTimeout(() => {
24
+ callback();
25
+ lastCallRef.current = Date.now();
26
+ timeoutRef.current = null;
27
+ }, remainingTime);
28
+ }
29
+ };
30
+
31
+ const cancel = () => {
32
+ if (timeoutRef.current) {
33
+ clearTimeout(timeoutRef.current);
34
+ timeoutRef.current = null;
35
+ }
36
+ };
37
+
38
+ return { throttledFunction, cancel };
39
+ };
2
40
 
3
41
  /**
4
42
  * standard debounce function.
@@ -28,5 +28,6 @@ export const getAppDataHeaders = (screenId: string) => {
28
28
  "x-applicaster-signed-device-info-token": appData.signedDeviceInfoToken,
29
29
  "x-applicaster-device-make": appData.deviceMake,
30
30
  "x-applicaster-device-model": appData.deviceModel,
31
+ "x-applicaster-uuid": appData?.uuid,
31
32
  };
32
33
  };
package/index.d.ts CHANGED
@@ -143,3 +143,18 @@ declare type BuilderData = {
143
143
  river?: ZappRiver;
144
144
  method?: string;
145
145
  };
146
+
147
+ declare type AccessibilityState = {
148
+ screenReaderEnabled: boolean;
149
+ reduceMotionEnabled: boolean;
150
+ boldTextEnabled: boolean;
151
+ };
152
+
153
+ declare type AccessibilityProps = {
154
+ accessibilityLabel?: string;
155
+ accessibilityHint?: string;
156
+ "aria-label"?: string;
157
+ "aria-description"?: string;
158
+ accessibilityRole?: string;
159
+ "aria-role"?: string;
160
+ };
@@ -1,4 +1,4 @@
1
- import { last, head, isNil, includes, all, pathOr } from "ramda";
1
+ import { last, head, isNil, includes, pathOr } from "ramda";
2
2
 
3
3
  const stripBrackets = (path) => {
4
4
  const withoutPrepened = last(path.split("{{"));
@@ -13,15 +13,15 @@ const hasValue = (context) => (path) => {
13
13
  return !isNil(value);
14
14
  };
15
15
 
16
- export const isEnoughDataToInflateString = (context, string): boolean => {
16
+ export const getMissingValues = (context, string): string[] => {
17
17
  if (!includes("{{", string)) {
18
- return true;
18
+ return [];
19
19
  }
20
20
 
21
21
  const SEARCH_REGEX = new RegExp(/{{.*?}}/g);
22
22
  const paths = string.toString().match(SEARCH_REGEX);
23
23
 
24
- return all(hasValue(context), paths);
24
+ return paths.filter((path) => !hasValue(context)(path));
25
25
  };
26
26
 
27
27
  export const isNonInflatedUrl = (url: string): boolean =>
File without changes
@@ -1,59 +1,59 @@
1
- import * as ReactNative from "react-native";
2
-
3
- const mocked_state = {
4
- remoteConfigurations: { localizations: {} },
5
- appData: {
6
- languageCode: "en",
7
- countryCode: "US",
8
- },
9
- };
10
-
11
- let mocked_locale = ["en-US"];
12
-
13
- jest.doMock("react-redux", () => ({
14
- useSelector: jest.fn((fn) => fn(mocked_state)),
15
- }));
1
+ import { NativeModules } from "react-native";
16
2
 
17
- const SettingsManager = {
3
+ const settingsManagerMock = {
18
4
  getConstants: jest.fn(() => ({
19
- settings: { AppleLanguages: mocked_locale },
5
+ settings: { AppleLanguages: ["en-US"] },
20
6
  })),
21
7
  };
22
8
 
23
- const QuickBrickCommunicationModule = {
9
+ const qbCommunicationModuleMock = {
24
10
  languageLocale: "en",
25
11
  countryLocale: "US",
12
+ uiLanguage: "en-US",
26
13
  };
27
14
 
28
- ReactNative.NativeModules.SettingsManager = SettingsManager;
29
-
30
- ReactNative.NativeModules.QuickBrickCommunicationModule =
31
- QuickBrickCommunicationModule;
32
-
33
- const { useIsRTL } = require("../index");
15
+ NativeModules.SettingsManager = settingsManagerMock;
16
+ NativeModules.QuickBrickCommunicationModule = qbCommunicationModuleMock;
34
17
 
35
18
  describe("localizationUtils", () => {
19
+ beforeEach(() => {
20
+ jest.resetModules();
21
+ });
22
+
36
23
  describe("when the app has a single locale", () => {
37
24
  describe("when the app has only a RTL language as locale", () => {
38
25
  beforeEach(() => {
39
- mocked_state.remoteConfigurations.localizations = {
40
- he: "http://url.com",
26
+ jest.resetModules();
27
+ const RN = require("react-native");
28
+
29
+ RN.NativeModules.QuickBrickCommunicationModule = {
30
+ languageLocale: "he",
31
+ countryLocale: "IL",
32
+ uiLanguage: "he-HE",
41
33
  };
42
34
  });
43
35
 
44
36
  it("useIsRTL returns true", () => {
37
+ const { useIsRTL } = require("../index");
45
38
  expect(useIsRTL()).toBe(true);
46
39
  });
47
40
  });
48
41
 
49
42
  describe("when the app has only a LTR language as locale", () => {
50
43
  beforeEach(() => {
51
- mocked_state.remoteConfigurations.localizations = {
52
- en: "http://url.com",
44
+ jest.resetModules();
45
+
46
+ const RN = require("react-native");
47
+
48
+ RN.NativeModules.QuickBrickCommunicationModule = {
49
+ languageLocale: "en",
50
+ countryLocale: "US",
51
+ uiLanguage: "en",
53
52
  };
54
53
  });
55
54
 
56
55
  it("useIsRTL returns false", () => {
56
+ const { useIsRTL } = require("../index");
57
57
  expect(useIsRTL()).toBe(false);
58
58
  });
59
59
  });
@@ -63,45 +63,56 @@ describe("localizationUtils", () => {
63
63
  describe("when device locale is in the app's locales", () => {
64
64
  describe("when device locale is LTR", () => {
65
65
  beforeEach(() => {
66
- mocked_state.remoteConfigurations.localizations = {
67
- he: "http://url.com",
68
- en: "http://url.com",
69
- };
66
+ jest.resetModules();
67
+
68
+ const RN = require("react-native");
70
69
 
71
- mocked_locale = ["en-US"];
70
+ RN.NativeModules.QuickBrickCommunicationModule = {
71
+ languageLocale: "en",
72
+ countryLocale: "US",
73
+ uiLanguage: "en-US",
74
+ };
72
75
  });
73
76
 
74
77
  it("useIsRTL returns false", () => {
78
+ const { useIsRTL } = require("../index");
75
79
  expect(useIsRTL()).toBe(false);
76
80
  });
77
81
  });
78
82
 
79
83
  describe("when device locale is RTL", () => {
80
84
  beforeEach(() => {
81
- mocked_state.remoteConfigurations.localizations = {
82
- he: "http://url.com",
83
- en: "http://url.com",
84
- };
85
+ jest.resetModules();
86
+ const RN = require("react-native");
85
87
 
86
- mocked_locale = ["he-HE"];
88
+ RN.NativeModules.QuickBrickCommunicationModule = {
89
+ languageLocale: "he",
90
+ countryLocale: "IL",
91
+ uiLanguage: "he",
92
+ };
87
93
  });
88
94
 
89
95
  it("useIsRTL returns true", () => {
96
+ const { useIsRTL } = require("../index");
90
97
  expect(useIsRTL()).toBe(true);
91
98
  });
92
99
  });
93
100
 
94
101
  describe("when a device has a secondary language in a supported language", () => {
95
102
  beforeEach(() => {
96
- mocked_state.remoteConfigurations.localizations = {
97
- en: "http://url.com",
98
- he: "http://url.com",
99
- };
103
+ jest.resetModules();
104
+
105
+ const RN = require("react-native");
100
106
 
101
- mocked_locale = ["es-SP", "he-HE"];
107
+ RN.NativeModules.QuickBrickCommunicationModule = {
108
+ languageLocale: "he",
109
+ countryLocale: "IL",
110
+ uiLanguage: "he",
111
+ };
102
112
  });
103
113
 
104
114
  it("useIsRTL returns true", () => {
115
+ const { useIsRTL } = require("../index");
105
116
  expect(useIsRTL()).toBe(true);
106
117
  });
107
118
  });
@@ -110,30 +121,38 @@ describe("localizationUtils", () => {
110
121
  describe("when device locale is not in the app's locales", () => {
111
122
  describe("when first app locale is LTR", () => {
112
123
  beforeEach(() => {
113
- mocked_state.remoteConfigurations.localizations = {
114
- en: "http://url.com",
115
- he: "http://url.com",
116
- };
124
+ jest.resetModules();
125
+
126
+ const RN = require("react-native");
117
127
 
118
- mocked_locale = ["fr-FR"];
128
+ RN.NativeModules.QuickBrickCommunicationModule = {
129
+ languageLocale: "fr",
130
+ countryLocale: "FR",
131
+ uiLanguage: "fr-FR",
132
+ };
119
133
  });
120
134
 
121
135
  it("useIsRTL returns false", () => {
136
+ const { useIsRTL } = require("../index");
122
137
  expect(useIsRTL()).toBe(false);
123
138
  });
124
139
  });
125
140
 
126
141
  describe("when first app locale is RTL", () => {
127
142
  beforeEach(() => {
128
- mocked_state.remoteConfigurations.localizations = {
129
- he: "http://url.com",
130
- en: "http://url.com",
131
- };
143
+ jest.resetModules();
144
+
145
+ const RN = require("react-native");
132
146
 
133
- mocked_locale = ["fr-FR"];
147
+ RN.NativeModules.QuickBrickCommunicationModule = {
148
+ languageLocale: "he",
149
+ countryLocale: "HE",
150
+ uiLanguage: "he",
151
+ };
134
152
  });
135
153
 
136
154
  it("useIsRTL returns true", () => {
155
+ const { useIsRTL } = require("../index");
137
156
  expect(useIsRTL()).toBe(true);
138
157
  });
139
158
  });
@@ -1,10 +1,9 @@
1
1
  import * as R from "ramda";
2
2
  import { ViewStyle } from "react-native";
3
- import { useSelector } from "react-redux";
4
3
 
5
- import { getDeviceLocales } from "./deviceLocales";
6
4
  import { useMemo } from "react";
7
5
  import { getUILanguage } from "../appUtils/localizationsHelper";
6
+ import { utilsLogger } from "../logger";
8
7
 
9
8
  export {
10
9
  getLocale,
@@ -45,34 +44,26 @@ export function useLocalizedStrings({ localizations }) {
45
44
  return result;
46
45
  }
47
46
 
48
- function getLocaleToUse(languageCodes, localizations) {
49
- const matchingLanguages = languageCodes.filter(
50
- R.contains(R.__, localizations)
51
- );
47
+ export function getIsRTL(): boolean {
48
+ const uiLanguage = getUILanguage();
49
+
50
+ if (!uiLanguage) {
51
+ utilsLogger.error({ message: "getUILanguage returned null or undefined" });
52
52
 
53
- if (matchingLanguages?.length) {
54
- return R.head(matchingLanguages);
53
+ return false;
55
54
  }
56
55
 
57
- return R.head(localizations);
58
- }
56
+ const [languageCode] = uiLanguage.split("-");
59
57
 
60
- export function getIsRTLFromLocalizations(localizations): boolean {
61
- const deviceLocales = getDeviceLocales() || [];
62
- const localeToUse = getLocaleToUse(deviceLocales, R.keys(localizations));
58
+ const lowerCaseLanguageCode = languageCode.toLocaleLowerCase();
63
59
 
64
- return R.contains(localeToUse, RTL_LOCALES);
65
- }
60
+ const isRTL = R.contains(lowerCaseLanguageCode, RTL_LOCALES);
66
61
 
67
- const remoteConfigLocalizationSelector = R.path([
68
- "remoteConfigurations",
69
- "localizations",
70
- ]);
62
+ return isRTL;
63
+ }
71
64
 
72
65
  export function useIsRTL(): boolean {
73
- const localizations = useSelector(remoteConfigLocalizationSelector);
74
-
75
- return getIsRTLFromLocalizations(localizations);
66
+ return getIsRTL();
76
67
  }
77
68
 
78
69
  export const applyRTLStylesIfNeeded = (
@@ -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: {
@@ -1663,7 +1663,8 @@ function getPlayerConfiguration({ platform }) {
1663
1663
  platform.includes("lg") ||
1664
1664
  platform.includes("samsung") ||
1665
1665
  platform.includes("android_tv") ||
1666
- platform.includes("amazon")
1666
+ platform.includes("amazon") ||
1667
+ platform.includes("vizio")
1667
1668
  ) {
1668
1669
  styles.fields.push(
1669
1670
  // Captions Menu
@@ -22,7 +22,11 @@ const {
22
22
  const { buttonKey } = require("./buttons");
23
23
  const { tvBadges } = require("./tvBadges");
24
24
  const { mobileProgressBar, tvProgressBar } = require("./progressBar");
25
- const { secondaryImage, getUpdatedSecondaryImageKeys } = require("./secondaryImage");
25
+
26
+ const {
27
+ secondaryImage,
28
+ getUpdatedSecondaryImageKeys,
29
+ } = require("./secondaryImage");
26
30
 
27
31
  module.exports = {
28
32
  fontKey,
@@ -132,29 +132,32 @@ function secondaryImage({
132
132
 
133
133
  const POSITION_OVER_IMAGE = toSnakeCase(position_over_image);
134
134
 
135
- const getUpdatedSecondaryImageKeys = (secondaryImageManifest) => secondaryImageManifest.fields.reduce((acc, item) => {
136
- const key = item.key;
135
+ const getUpdatedSecondaryImageKeys = (secondaryImageManifest) =>
136
+ secondaryImageManifest.fields.reduce((acc, item) => {
137
+ const key = item.key;
137
138
 
138
- const conditionalFields = (item.conditional_fields || []).map((field) => field.key);
139
+ const conditionalFields = (item.conditional_fields || []).map(
140
+ (field) => field.key
141
+ );
139
142
 
140
- const updatedConditionalFields = conditionalFields.reduce((acc, field) => {
141
- const updatedKey = field.replace("secondary_image_", "nested_image_");
143
+ const updatedConditionalFields = conditionalFields.reduce((acc, field) => {
144
+ const updatedKey = field.replace("secondary_image_", "nested_image_");
145
+
146
+ return {
147
+ ...acc,
148
+ [updatedKey]: field,
149
+ };
150
+ }, {});
151
+
152
+ const updatedKey = key.replace("secondary_image_", "nested_image_");
142
153
 
143
154
  return {
144
155
  ...acc,
145
- [updatedKey]: field,
156
+ ...updatedConditionalFields,
157
+ [updatedKey]: key,
146
158
  };
147
159
  }, {});
148
160
 
149
- const updatedKey = key.replace("secondary_image_", "nested_image_");
150
-
151
- return {
152
- ...acc,
153
- ...updatedConditionalFields,
154
- [updatedKey]: key,
155
- };
156
- }, {});
157
-
158
161
  module.exports = {
159
162
  secondaryImage,
160
163
  getUpdatedSecondaryImageKeys,
@@ -114,7 +114,11 @@ export function getNavigationProps({
114
114
  } | null {
115
115
  const { activeRiver } = navigator;
116
116
  const { navigations, id: selected, home } = activeRiver; // use useRiver hook instead
117
- const navigation = R.find(R.propEq("category", category), navigations);
117
+
118
+ const navigation: ZappNavigation = R.find(
119
+ R.propEq("category", category),
120
+ navigations
121
+ );
118
122
 
119
123
  if (!navigation) {
120
124
  return null;
@@ -127,9 +131,9 @@ export function getNavigationProps({
127
131
  title,
128
132
  home,
129
133
  styles,
130
- assets,
131
- nav_items,
132
- rules,
134
+ assets: assets as ZappNavBarAssets,
135
+ nav_items: nav_items as any as NavBarItem[],
136
+ rules: rules as any as ZappNavBarRules,
133
137
  };
134
138
  }
135
139
 
@@ -588,3 +592,51 @@ export function isMenuDisabled(
588
592
  export function routeIsPlayerScreen(currentRoute) {
589
593
  return currentRoute?.includes("/playable");
590
594
  }
595
+
596
+ export const getNavBarProps =
597
+ (currentRiver: ZappRiver, pathname: string, title: string) => () => {
598
+ const props = getNavigationPropsV2({
599
+ currentRiver,
600
+ title,
601
+ category: "nav_bar",
602
+ });
603
+
604
+ if (props) {
605
+ return {
606
+ ...props,
607
+ id: pathname,
608
+ pathname: pathname,
609
+ };
610
+ }
611
+
612
+ return null;
613
+ };
614
+
615
+ export const findMenuPlugin = (
616
+ navigations: ZappNavigation[],
617
+ plugins
618
+ ): QuickBrickPlugin => {
619
+ return resolveNavigationPlugin({
620
+ category: "menu",
621
+ navigations,
622
+ plugins,
623
+ }) as QuickBrickPlugin;
624
+ };
625
+
626
+ export const shouldNavBarDisplayMenu = (currentRiver: ZappRiver, plugins) =>
627
+ findMenuPlugin(currentRiver.navigations, plugins)?.identifier ===
628
+ "quick_brick_side_menu" ||
629
+ currentRiver?.navigations.findIndex(
630
+ (nav) => nav.category === "nav_bar" && nav.general?.override_menu_target
631
+ ) > -1;
632
+
633
+ export const getScreenId = (
634
+ screenData: LegacyNavigationScreenData,
635
+ fallbackRiver: ZappRiver
636
+ ) => {
637
+ return screenData?.targetScreen
638
+ ? screenData?.targetScreen?.id ?? fallbackRiver.id
639
+ : "screen_id" in screenData
640
+ ? screenData.screen_id
641
+ : fallbackRiver.id;
642
+ };
@@ -7,12 +7,24 @@ describe("isLikeNumber", () => {
7
7
  expect(isLikeNumber(value)).toBe(true);
8
8
  });
9
9
 
10
+ it("value is a number", () => {
11
+ const value = 123.45;
12
+
13
+ expect(isLikeNumber(value)).toBe(true);
14
+ });
15
+
10
16
  it("value is a string as number", () => {
11
17
  const value = "123";
12
18
 
13
19
  expect(isLikeNumber(value)).toBe(true);
14
20
  });
15
21
 
22
+ it("value is a string as number", () => {
23
+ const value = "123.45";
24
+
25
+ expect(isLikeNumber(value)).toBe(true);
26
+ });
27
+
16
28
  it("value is empty string", () => {
17
29
  const value = "";
18
30
 
@@ -54,4 +66,10 @@ describe("isLikeNumber", () => {
54
66
 
55
67
  expect(isLikeNumber(value)).toBe(false);
56
68
  });
69
+
70
+ it("NaN is not a number", () => {
71
+ const value = NaN;
72
+
73
+ expect(isLikeNumber(value)).toBe(false);
74
+ });
57
75
  });