@applicaster/zapp-react-native-utils 12.0.0-rc.6 → 12.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 (82) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +16 -8
  2. package/analyticsUtils/AnalyticsEvents/index.ts +6 -0
  3. package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
  4. package/analyticsUtils/__tests__/analyticsManager.test.js +75 -73
  5. package/analyticsUtils/events.ts +6 -0
  6. package/analyticsUtils/helpers/hooks.ts +1 -1
  7. package/analyticsUtils/index.tsx +6 -0
  8. package/appUtils/HooksManager/index.js +3 -1
  9. package/appUtils/focusManager/treeDataStructure/Utils/index.js +12 -3
  10. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +1 -1
  11. package/appUtils/keyCodes/__tests__/keyCodes.test.js +80 -85
  12. package/appUtils/keyCodes/keys.web.js +1 -0
  13. package/appUtils/orientationHelper.ts +1 -1
  14. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +53 -0
  15. package/appUtils/playerManager/conts.ts +5 -0
  16. package/appUtils/playerManager/player.ts +47 -4
  17. package/appUtils/playerManager/playerFactory.ts +15 -1
  18. package/appUtils/playerManager/playerNative.ts +35 -0
  19. package/appUtils/playerManager/usePlayerState.tsx +13 -1
  20. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +50 -0
  21. package/cellUtils/__tests__/cellUtils.test.ts +48 -0
  22. package/cellUtils/__tests__/fixtures.ts +13 -0
  23. package/cellUtils/index.ts +40 -2
  24. package/dateUtils/index.ts +11 -0
  25. package/focusManager/index.ts +1 -1
  26. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +2 -2
  27. package/functionUtils/__tests__/functionUtils.test.js +244 -248
  28. package/headersUtils/index.ts +1 -0
  29. package/inflatedUrlUtils/index.ts +4 -4
  30. package/localizationUtils/__tests__/localizationUtils.test.ts +17 -13
  31. package/localizationUtils/index.ts +6 -3
  32. package/logger/index.ts +1 -1
  33. package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
  34. package/manifestUtils/defaultManifestConfigurations/player.js +10 -1
  35. package/manifestUtils/index.js +5 -1
  36. package/manifestUtils/secondaryImage/index.js +18 -15
  37. package/modalState/index.ts +57 -0
  38. package/navigationUtils/index.ts +56 -7
  39. package/numberUtils/index.ts +4 -0
  40. package/package.json +3 -2
  41. package/playerUtils/configurationGenerator.ts +11 -1
  42. package/pluginUtils/index.ts +11 -7
  43. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
  44. package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
  45. package/reactHooks/cell-click/__tests__/index.test.js +6 -2
  46. package/reactHooks/cell-click/index.ts +2 -2
  47. package/reactHooks/device/index.ts +0 -1
  48. package/reactHooks/device/useIsTablet.tsx +28 -3
  49. package/reactHooks/device/useMemoizedIsTablet.ts +1 -1
  50. package/reactHooks/device/useOrientation.tsx +3 -1
  51. package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
  52. package/reactHooks/feed/useInflatedUrl.ts +44 -15
  53. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
  54. package/reactHooks/index.ts +2 -0
  55. package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +66 -59
  56. package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
  57. package/reactHooks/layout/index.ts +2 -1
  58. package/reactHooks/layout/types/index.ts +2 -0
  59. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +11 -16
  60. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +12 -14
  61. package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
  62. package/reactHooks/navigation/__mocks__/index.ts +15 -0
  63. package/reactHooks/navigation/__mocks__/useNavigation.ts +7 -0
  64. package/reactHooks/navigation/__tests__/index.test.tsx +234 -78
  65. package/reactHooks/navigation/index.ts +251 -85
  66. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
  67. package/reactHooks/screen/__mocks__/useScreenContext.ts +3 -1
  68. package/reactHooks/screen/index.ts +2 -0
  69. package/reactHooks/screen/useScreenContext.ts +2 -2
  70. package/reactHooks/screen/useScreenData.ts +1 -1
  71. package/reactHooks/screen/useTargetScreenData.ts +7 -0
  72. package/reactHooks/state/useRivers.ts +3 -1
  73. package/reactHooks/useListenEventBusEvent.ts +1 -4
  74. package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
  75. package/riverComponetsMeasurementProvider/index.tsx +2 -2
  76. package/stringUtils/index.ts +5 -0
  77. package/testUtils/getByTestId.ts +14 -0
  78. package/testUtils/index.tsx +38 -23
  79. package/types.ts +4 -0
  80. package/reactHooks/device/useIsTablet.android.tsx +0 -14
  81. package/reactHooks/device/useIsTablet.ios.tsx +0 -7
  82. package/reactHooks/device/useIsTablet.web.tsx +0 -3
@@ -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: {
@@ -41,6 +41,14 @@ function getPlayerConfiguration({ platform }) {
41
41
  label_tooltip:
42
42
  "Enable if you want to have adaptive background gradient based on audio background image.",
43
43
  },
44
+ {
45
+ key: "audio_player_background_image_query",
46
+ label: "Audio Player Image Query",
47
+ initial_value: "",
48
+ type: "text_input",
49
+ label_tooltip:
50
+ "Set the query params which control the size of the image e.g width, height. This will help to optimize the loading time, when magic background is enabled.",
51
+ },
44
52
  ],
45
53
  };
46
54
 
@@ -1655,7 +1663,8 @@ function getPlayerConfiguration({ platform }) {
1655
1663
  platform.includes("lg") ||
1656
1664
  platform.includes("samsung") ||
1657
1665
  platform.includes("android_tv") ||
1658
- platform.includes("amazon")
1666
+ platform.includes("amazon") ||
1667
+ platform.includes("vizio")
1659
1668
  ) {
1660
1669
  styles.fields.push(
1661
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,
@@ -0,0 +1,57 @@
1
+ import { create } from "zustand";
2
+
3
+ interface ModalState {
4
+ visible: boolean;
5
+ screen: any;
6
+ options: Record<string, unknown>;
7
+ props: Record<string, unknown>;
8
+ }
9
+
10
+ interface ModalStore {
11
+ modalState: ModalState;
12
+ setModalState: (newState: Partial<ModalState>) => void;
13
+ openModal: (
14
+ item: any,
15
+ options?: Record<string, unknown>,
16
+ props?: Record<string, unknown>
17
+ ) => void;
18
+ dismissModal: () => void;
19
+ }
20
+
21
+ const initialModalState: ModalState = {
22
+ visible: false,
23
+ screen: null,
24
+ options: {},
25
+ props: {},
26
+ };
27
+
28
+ export const useModalStore = create<ModalStore>((set) => ({
29
+ modalState: initialModalState,
30
+ setModalState: (newState) =>
31
+ set((state) => ({
32
+ modalState: { ...state.modalState, ...newState },
33
+ })),
34
+ openModal: ({
35
+ item,
36
+ options = {},
37
+ props = { screenData: {} },
38
+ }: OpenModalArg) =>
39
+ set({
40
+ modalState: {
41
+ visible: true,
42
+ screen: item,
43
+ options: {
44
+ animated: true,
45
+ animationType: "slide",
46
+ onShow: () => {},
47
+ presentationStyle: "overFullScreen",
48
+ transparent: true,
49
+ ...options,
50
+ onDismiss: () => set({ modalState: initialModalState }),
51
+ onRequestClose: () => set({ modalState: initialModalState }),
52
+ },
53
+ props,
54
+ },
55
+ }),
56
+ dismissModal: () => set(() => ({ modalState: initialModalState })),
57
+ }));
@@ -93,19 +93,68 @@ export function resolveNavigationPlugin({
93
93
  * @param {Object} navigator to look for navigation props
94
94
  * @param {String} category to look for in screen navigations
95
95
  * @returns {Object} props
96
+ * @deprecated use getNavigationPropsV2 instead
96
97
  */
97
98
  export function getNavigationProps({
98
- navigator: { activeRiver },
99
+ navigator,
99
100
  title = "",
100
101
  category,
101
102
  }: {
102
103
  navigator: QuickBrickAppNavigator;
103
104
  title: string;
104
105
  category: string;
105
- }): Nullable<
106
- Partial<ZappNavigation> & { selected: string; title: string; home: boolean }
107
- > {
108
- const { navigations, id: selected, home } = activeRiver;
106
+ }): {
107
+ selected: string;
108
+ title: string;
109
+ home: boolean;
110
+ styles: unknown;
111
+ assets: ZappNavBarAssets;
112
+ rules: ZappNavBarRules;
113
+ nav_items: NavBarItem[];
114
+ } | null {
115
+ const { activeRiver } = navigator;
116
+ const { navigations, id: selected, home } = activeRiver; // use useRiver hook instead
117
+
118
+ const navigation: ZappNavigation = R.find(
119
+ R.propEq("category", category),
120
+ navigations
121
+ );
122
+
123
+ if (!navigation) {
124
+ return null;
125
+ }
126
+
127
+ const { styles, assets, nav_items, rules } = navigation;
128
+
129
+ return {
130
+ selected,
131
+ title,
132
+ home,
133
+ styles,
134
+ assets: assets as ZappNavBarAssets,
135
+ nav_items: nav_items as any as NavBarItem[],
136
+ rules: rules as any as ZappNavBarRules,
137
+ };
138
+ }
139
+
140
+ export function getNavigationPropsV2({
141
+ currentRiver,
142
+ title = "",
143
+ category,
144
+ }: {
145
+ currentRiver: ZappRiver;
146
+ title: string;
147
+ category: string;
148
+ }): {
149
+ selected: string;
150
+ title: string;
151
+ home: boolean;
152
+ styles: unknown;
153
+ assets: ZappNavBarAssets;
154
+ rules: ZappNavBarRules;
155
+ nav_items: NavBarItem[];
156
+ } | null {
157
+ const { navigations, id: selected, home } = currentRiver; // use useRiver hook instead
109
158
  const navigation = R.find(R.propEq("category", category), navigations);
110
159
 
111
160
  if (!navigation) {
@@ -170,7 +219,7 @@ export function getScreenIdForContentType(payload, contentTypes) {
170
219
  export function getItemTargetId(
171
220
  item: ZappEntry | ZappRiver,
172
221
  screenType?: string, // todo: creat screen types enum
173
- contentTypes?: ZappContentTypes,
222
+ contentTypes?: ZappContentTypes | null,
174
223
  rivers?: Record<string, ZappRiver>
175
224
  ) {
176
225
  // Pipes v2 resolution
@@ -217,7 +266,7 @@ export function getTargetRoute(
217
266
  layoutVersion = undefined,
218
267
  rivers = {},
219
268
  }: {
220
- contentTypes?: ZappContentTypes;
269
+ contentTypes?: ZappContentTypes | null;
221
270
  layoutVersion?: Maybe<ZappLayoutVersions>;
222
271
  rivers?: Record<string, ZappRiver>;
223
272
  } = {}
@@ -75,3 +75,7 @@ export const isLikeNumber = (value: any): boolean => {
75
75
 
76
76
  export const isZero = (value: unknown): boolean =>
77
77
  Object.is(value, -0) || Object.is(value, 0);
78
+
79
+ export const isMinusZero = (value: number) => {
80
+ return 1 / value === -Infinity;
81
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "12.0.0-rc.6",
3
+ "version": "12.0.0-rc.61",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "12.0.0-rc.6",
31
+ "@applicaster/applicaster-types": "12.0.0-rc.61",
32
32
  "buffer": "^5.2.1",
33
33
  "camelize": "^1.0.0",
34
34
  "create-react-context": "^0.2.3",
@@ -44,6 +44,7 @@
44
44
  "immer": "*",
45
45
  "react": "17.0.2",
46
46
  "react-native": "0.68.6",
47
+ "validate-color": "*",
47
48
  "zustand": "*"
48
49
  },
49
50
  "devDependencies": {
@@ -22,6 +22,7 @@ function getDevice(platform) {
22
22
  case "android_tv_for_quickbrick":
23
23
  case "lg_tv":
24
24
  case "samsung_tv":
25
+ case "vizio":
25
26
  case "web":
26
27
  return "tv";
27
28
  }
@@ -46,6 +47,14 @@ function getPlayerConfiguration({ platform }) {
46
47
  label_tooltip:
47
48
  "Enable if you want to have adaptive background gradient based on audio background image.",
48
49
  },
50
+ {
51
+ key: "audio_player_background_image_query",
52
+ label: "Audio Player Image Query",
53
+ initial_value: "",
54
+ type: "text_input",
55
+ label_tooltip:
56
+ "Set the query params which control the size of the image e.g width, height. This will help to optimize the loading time, when magic background is enabled.",
57
+ },
49
58
  ],
50
59
  };
51
60
 
@@ -1651,7 +1660,8 @@ function getPlayerConfiguration({ platform }) {
1651
1660
  platform.includes("lg") ||
1652
1661
  platform.includes("samsung") ||
1653
1662
  platform.includes("android_tv") ||
1654
- platform.includes("amazon")
1663
+ platform.includes("amazon") ||
1664
+ platform.includes("vizio")
1655
1665
  ) {
1656
1666
  styles.fields.push(
1657
1667
  // Captions Menu for TV
@@ -20,14 +20,14 @@ type Plugin = {
20
20
  * returns the component module for a specific type
21
21
  * Will look in components first, then in plugins
22
22
  */
23
- function getComponentModule({
23
+ export function getComponentModule({
24
24
  componentType,
25
25
  components,
26
26
  plugins,
27
27
  }: {
28
28
  componentType: string;
29
29
  components: ComponentsMap;
30
- plugins: Plugin[];
30
+ plugins: Plugin[] | QuickBrickPlugin[];
31
31
  }) {
32
32
  const component = R.compose(
33
33
  R.prop(R.__, components),
@@ -68,11 +68,13 @@ export function findComponentByType({
68
68
  * Returns the plugin for a given identifier, if found
69
69
  * @param identifier of the plugin to look for
70
70
  * @param plugins map of the available plugins
71
+ * @param doNotWarn if set to true, will not log a warning if plugin is not found
71
72
  * @return whole plugin if found, undefined otherwise
72
73
  */
73
74
  export function findPluginByIdentifier(
74
75
  identifier: string,
75
- plugins: (QuickBrickPlugin | ZappPlugin | Plugin)[]
76
+ plugins: (QuickBrickPlugin | ZappPlugin | Plugin)[],
77
+ doNotWarn: boolean = false
76
78
  ): (QuickBrickPlugin | ZappPlugin | Plugin) | undefined {
77
79
  if (!identifier) {
78
80
  return undefined;
@@ -84,10 +86,12 @@ export function findPluginByIdentifier(
84
86
  )(plugins);
85
87
 
86
88
  if (!plugin && identifier !== "river" && identifier !== "hooks") {
87
- pluginUtilsLogger.warning({
88
- message: `Could not find plugin for identifier ${identifier}`,
89
- data: { identifier },
90
- });
89
+ if (!doNotWarn) {
90
+ pluginUtilsLogger.warning({
91
+ message: `Could not find plugin for identifier ${identifier}`,
92
+ data: { identifier },
93
+ });
94
+ }
91
95
  }
92
96
 
93
97
  return plugin;
@@ -12,6 +12,8 @@ jest.mock(
12
12
  })
13
13
  );
14
14
 
15
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation");
16
+
15
17
  jest.useFakeTimers();
16
18
 
17
19
  const {
@@ -1,4 +1,8 @@
1
+ import React from "react";
2
+
1
3
  import { act, renderHook } from "@testing-library/react-hooks";
4
+ import { Provider } from "react-redux";
5
+ import configureStore from "redux-mock-store";
2
6
 
3
7
  const mockUpdateComponentsPositions = jest.fn();
4
8
 
@@ -17,11 +21,24 @@ jest.mock(
17
21
 
18
22
  jest.useFakeTimers();
19
23
 
24
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation");
25
+
26
+ const mockStore = configureStore();
27
+
28
+ const store = mockStore({ appData: {} });
29
+
30
+ const Wrapper = ({ children }: { children: React.ReactChild }) => (
31
+ /* @ts-ignore */
32
+ <Provider store={store}>{children}</Provider>
33
+ );
34
+
20
35
  const { useTrackedView } = require("../useTrackedView");
21
36
 
22
37
  describe("useTrackCurrentAutoScrollingElement", () => {
23
38
  it("should update position for selected component", async () => {
24
- const { result } = renderHook(() => useTrackedView("123"));
39
+ const { result } = renderHook(() => useTrackedView("123"), {
40
+ wrapper: Wrapper,
41
+ });
25
42
 
26
43
  // Fast-forward until all timers have been executed
27
44
  jest.advanceTimersByTime(3000);
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { cleanup, renderHook } from "@testing-library/react-hooks";
3
3
  import { CellTapContext } from "@applicaster/zapp-react-native-ui-components/Contexts/CellTapContext";
4
+ import { waitFor } from "@testing-library/react-native";
4
5
 
5
6
  const navigator = {
6
7
  getPathname: () => "pathnameMock",
@@ -64,13 +65,16 @@ describe("useCellClick", () => {
64
65
  cleanup();
65
66
  });
66
67
 
67
- it("runs onCellTap", () => {
68
+ it("runs onCellTap", async () => {
68
69
  const { result } = renderHook(() => useCellClick(mockProps), { wrapper });
69
70
  const useCellClickHook = result.current;
70
71
 
71
72
  useCellClickHook();
72
73
 
73
- expect(onCellTap).toHaveBeenCalled();
74
+ await waitFor(() => {
75
+ expect(onCellTap).toHaveBeenCalled();
76
+ });
77
+
74
78
  cleanup();
75
79
  });
76
80
  });
@@ -53,7 +53,7 @@ export const useCellClick = (props: Props) => {
53
53
  );
54
54
 
55
55
  const onPress = React.useCallback(
56
- (_item?: ZappEntry | any, _index?: number) => {
56
+ async (_item?: ZappEntry | any, _index?: number) => {
57
57
  // Making sure we are not getting GestureResponderEvent in here.
58
58
  const isZappEntry = _item?.id && _item?.type;
59
59
  const selectedItem = isZappEntry ? _item : props.item;
@@ -70,7 +70,7 @@ export const useCellClick = (props: Props) => {
70
70
  logOnPress(selectedItem, pathname, component, onCellTap);
71
71
 
72
72
  if (selectedItem) {
73
- actionExecutor?.handleEntryActions(selectedItem);
73
+ await actionExecutor?.handleEntryActions(selectedItem);
74
74
  }
75
75
 
76
76
  if (selectedItem?.type?.value === "action") {
@@ -1,6 +1,5 @@
1
1
  export { useOrientation } from "./useOrientation";
2
2
 
3
- // @ts-ignore
4
3
  export { useIsTablet } from "./useIsTablet";
5
4
 
6
5
  export { useMemoizedIsTablet } from "./useMemoizedIsTablet";
@@ -1,3 +1,28 @@
1
- // Template function for TS to correctly compile.
2
- // The RN app will use files with platform suffixes
3
- export const useIsTablet = (): boolean => false;
1
+ /* eslint react-native/split-platform-components: off */
2
+
3
+ import { Platform, PlatformIOSStatic, NativeModules } from "react-native";
4
+ import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
+ import { F as alwaysFalse } from "ramda";
6
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
7
+
8
+ const useIsTabletIos = (): boolean => {
9
+ const { isPad } = Platform as PlatformIOSStatic;
10
+
11
+ return isPad;
12
+ };
13
+
14
+ const useIsTabletAndroid = (): boolean => {
15
+ const { initialProps } = NativeModules.QuickBrickCommunicationModule;
16
+
17
+ return toBooleanWithDefaultFalse(initialProps?.is_tablet);
18
+ };
19
+
20
+ export const useIsTablet = platformSelect({
21
+ ios: useIsTabletIos,
22
+ android: useIsTabletAndroid,
23
+ lg_tv: alwaysFalse,
24
+ samsung_tv: alwaysFalse,
25
+ android_tv: alwaysFalse,
26
+ tvos: alwaysFalse,
27
+ default: alwaysFalse,
28
+ });
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
3
- import { useIsTablet } from "./index";
3
+ import { useIsTablet } from "./useIsTablet";
4
4
 
5
5
  export const useMemoizedIsTablet = () => {
6
6
  const isTablet = useIsTablet();
@@ -1,6 +1,8 @@
1
1
  import { useWindowDimensions } from "react-native";
2
2
 
3
- export const useOrientation = (): string => {
3
+ type Return = "landscape" | "portrait";
4
+
5
+ export const useOrientation = (): Return => {
4
6
  const { width, height } = useWindowDimensions();
5
7
  const isLandscape = width > height;
6
8
 
@@ -12,9 +12,21 @@ const {
12
12
 
13
13
  const { reactHooksLogger } = require("../../logger");
14
14
 
15
+ let mockStore;
16
+
15
17
  describe("getInflatedDataSourceUrl", () => {
16
18
  beforeEach(() => {
17
19
  reactHooksLogger.warning.mockClear();
20
+
21
+ const {
22
+ appStore,
23
+ } = require("@applicaster/zapp-react-native-redux/AppStore");
24
+
25
+ mockStore = {
26
+ getState: jest.fn().mockReturnValue({ pipesEndpoints: {} }),
27
+ };
28
+
29
+ appStore.set(mockStore);
18
30
  });
19
31
 
20
32
  it("returns the url inflated with the right params", () => {
@@ -72,7 +84,8 @@ describe("getInflatedDataSourceUrl", () => {
72
84
  expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
73
85
 
74
86
  expect(reactHooksLogger.warning).toHaveBeenCalledWith({
75
- message: "Not enough data to fully inflate url",
87
+ message:
88
+ "Not enough data to fully inflate url: {{entry.id}} source: http://foo.com/show/seriesId={{seriesId}}",
76
89
  data: { source, contexts, mapping },
77
90
  });
78
91
  });
@@ -107,7 +120,8 @@ describe("getInflatedDataSourceUrl", () => {
107
120
  expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
108
121
 
109
122
  expect(reactHooksLogger.warning).toHaveBeenCalledWith({
110
- message: "Not enough data to fully inflate url",
123
+ message:
124
+ "Not enough data to fully inflate url: {{entry.extensions.groupName}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName}}",
111
125
  data: { source, contexts, mapping },
112
126
  });
113
127
  });
@@ -148,7 +162,8 @@ describe("getInflatedDataSourceUrl", () => {
148
162
  expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
149
163
 
150
164
  expect(reactHooksLogger.warning).toHaveBeenCalledWith({
151
- message: "Not enough data to fully inflate url",
165
+ message:
166
+ "Not enough data to fully inflate url: {{entry.extensions.groupName2}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName1}}?test={{groupName2}}",
152
167
  data: { source, contexts, mapping },
153
168
  });
154
169
  });
@@ -9,9 +9,15 @@ import {
9
9
 
10
10
  import { inflateString } from "../../stringUtils";
11
11
  import { useRoute } from "../navigation";
12
- import { isEnoughDataToInflateString } from "../../inflatedUrlUtils";
12
+ import { getMissingValues } from "../../inflatedUrlUtils";
13
13
  import { reactHooksLogger as logger } from "../logger";
14
14
  import { isTV } from "../../reactUtils";
15
+ import {
16
+ findEndpointForURL,
17
+ HTTP_METHODS,
18
+ } from "@applicaster/zapp-pipes-v2-client";
19
+ import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
20
+ import { ENDPOINT_TAGS } from "../../types";
15
21
 
16
22
  /**
17
23
  * will match any occurrence in a string of one or more word characters
@@ -105,24 +111,47 @@ export const getInflatedDataSourceUrl: GetInflatedDataSourceUrl = ({
105
111
  R.keys(mapping)
106
112
  );
107
113
 
108
- if (!isEnoughDataToInflateString(contexts, string)) {
109
- const message = "Not enough data to fully inflate url";
114
+ const missingValues = getMissingValues(contexts, string);
110
115
 
111
- // todo: add js only payload instead
112
- if (__DEV__) {
113
- logger.warning({
114
- message,
115
- data: { source, contexts, mapping },
116
- });
116
+ if (missingValues && missingValues.length > 0) {
117
+ const pipesEndpoints = appStore.get("pipesEndpoints");
118
+
119
+ const endpointURL = findEndpointForURL(
120
+ source,
121
+ pipesEndpoints,
122
+ HTTP_METHODS.GET
123
+ );
124
+
125
+ const tags = pipesEndpoints?.[endpointURL]?.tags;
126
+ const allowMissingKeys = tags?.includes(ENDPOINT_TAGS.allow_missing_keys);
127
+
128
+ if (!allowMissingKeys) {
129
+ const message = `Not enough data to fully inflate url: ${missingValues.join(
130
+ ","
131
+ )} source: ${source}`;
132
+
133
+ // todo: add js only payload instead
134
+ if (__DEV__) {
135
+ logger.warning({
136
+ message,
137
+ data: { source, contexts, mapping },
138
+ });
139
+ } else {
140
+ // todo: log values we've failed to resolve
141
+ logger.warning({
142
+ message,
143
+ });
144
+ }
145
+
146
+ // return null in order to prevent start fetching data with invalid url
147
+ return null;
117
148
  } else {
118
- // todo: log values we've failed to resolve
119
- logger.warning({
120
- message,
149
+ logger.debug({
150
+ message: `Ignoring missing values in url because ${
151
+ ENDPOINT_TAGS.allow_missing_keys
152
+ } endpoint tag, keys: ${missingValues.join(",")}`,
121
153
  });
122
154
  }
123
-
124
- // return null in order to prevent start fetching data with invalid url
125
- return null;
126
155
  }
127
156
 
128
157
  return inflateString({ string, data: contexts });