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

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 (54) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +55 -6
  2. package/actionsExecutor/consts.ts +4 -0
  3. package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
  4. package/appUtils/__tests__/allZappLocales.ts +79 -0
  5. package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
  6. package/appUtils/accessibilityManager/const.ts +18 -0
  7. package/appUtils/accessibilityManager/index.ts +4 -1
  8. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
  9. package/appUtils/focusManager/index.ios.ts +14 -4
  10. package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
  11. package/appUtils/focusManager/utils/index.ts +5 -0
  12. package/appUtils/localizationsHelper.ts +10 -2
  13. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
  14. package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
  15. package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
  16. package/arrayUtils/index.ts +7 -2
  17. package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
  18. package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +72 -0
  19. package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +110 -0
  20. package/audioPlayerUtils/assets/index.ts +2 -0
  21. package/audioPlayerUtils/index.ts +242 -0
  22. package/cellUtils/index.ts +9 -5
  23. package/componentsUtils/index.ts +8 -1
  24. package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
  25. package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
  26. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
  27. package/configurationUtils/index.ts +63 -34
  28. package/localizationUtils/index.ts +3 -3
  29. package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
  30. package/manifestUtils/defaultManifestConfigurations/player.js +47 -9
  31. package/manifestUtils/index.js +2 -0
  32. package/manifestUtils/keys.js +27 -2
  33. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +1 -2
  34. package/package.json +2 -2
  35. package/playerUtils/configurationGenerator.ts +0 -16
  36. package/playerUtils/index.ts +23 -0
  37. package/reactHooks/app/useAppState.ts +2 -2
  38. package/reactHooks/feed/useBatchLoading.ts +12 -14
  39. package/reactHooks/navigation/{useGetTabBarHeight.ts → getTabBarHeight.ts} +1 -1
  40. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +10 -3
  41. package/reactHooks/navigation/useNavigationPluginData.ts +8 -4
  42. package/reactHooks/navigation/useNavigationType.ts +4 -2
  43. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
  44. package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
  45. package/reactHooks/state/README.md +79 -0
  46. package/reactHooks/state/ZStoreProvider.tsx +71 -0
  47. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
  48. package/reactHooks/state/index.ts +2 -0
  49. package/reactHooks/useListenEventBusEvent.ts +1 -1
  50. package/reactUtils/index.ts +9 -0
  51. package/time/BackgroundTimer.ts +5 -3
  52. package/typeGuards/index.ts +3 -0
  53. package/utils/index.ts +12 -1
  54. package/zappFrameworkUtils/localStorageHelper.ts +32 -10
@@ -1,11 +1,8 @@
1
- import * as R from "ramda";
2
1
  import {
3
2
  populateConfigurationValues,
4
- imageSrcFromMediaItem,
5
3
  getBoolFromConfigValue,
6
4
  remapUpdatedKeys,
7
5
  } from "../";
8
- import { entry } from "./testEntry";
9
6
 
10
7
  describe("getBoolFromConfigValue", () => {
11
8
  it('returns true if value is "true" string', () => {
@@ -57,34 +54,6 @@ describe("getBoolFromConfigValue", () => {
57
54
  });
58
55
  });
59
56
 
60
- describe("imageSrcFromMediaItem", () => {
61
- describe("returns the src value of first media_item", () => {
62
- it("when the matching key is found and the src is not empty", () => {
63
- const result = imageSrcFromMediaItem(entry, ["logo_thumbnail"]);
64
-
65
- expect(result).toEqual(entry.media_group[1].media_item[0].src);
66
- expect(result).not.toEqual("");
67
- });
68
- });
69
-
70
- it("returns a media item with the 'image_base' key as a fallback", () => {
71
- const result = imageSrcFromMediaItem(entry, ["does_not_exist"]);
72
- const fallback = entry.media_group[0].media_item[0];
73
- expect(result).toEqual(fallback.src);
74
- expect(fallback.key).toBe("image_base");
75
- });
76
-
77
- it("returns undefined if the key was found but the source was empty", () => {
78
- const badEntry = R.set(
79
- R.lensPath(["media_group", 0, "media_item", 0, "src"]),
80
- "",
81
- entry
82
- );
83
-
84
- expect(imageSrcFromMediaItem(badEntry, ["image_base"])).toBeUndefined();
85
- });
86
- });
87
-
88
57
  describe("populateConfigurationValues", () => {
89
58
  it("transforms and returns the valid values", () => {
90
59
  const fields = [
@@ -0,0 +1,65 @@
1
+ import { getMediaItems } from "..";
2
+ import { entry as baseEntry } from "./testEntry";
3
+
4
+ describe("getMediaItems", () => {
5
+ it("returns both image and thumbnail media items", () => {
6
+ const items = getMediaItems(baseEntry);
7
+ expect(items).toHaveLength(2);
8
+ expect(items[0].key).toBe("image_base");
9
+ expect(items[1].key).toBe("logo_thumbnail");
10
+ });
11
+
12
+ it("returns only image media items if no thumbnail present", () => {
13
+ const entry = {
14
+ ...baseEntry,
15
+ media_group: [
16
+ {
17
+ type: "image",
18
+ media_item: [
19
+ { key: "image_base", src: "img.png" },
20
+ { key: "other", src: "other.png" },
21
+ ],
22
+ },
23
+ ],
24
+ };
25
+
26
+ const items = getMediaItems(entry);
27
+ expect(items).toHaveLength(2);
28
+ expect(items[0].key).toBe("image_base");
29
+ expect(items[1].key).toBe("other");
30
+ });
31
+
32
+ it("returns only thumbnail media items if no image present", () => {
33
+ const entry = {
34
+ ...baseEntry,
35
+ media_group: [
36
+ {
37
+ type: "thumbnail",
38
+ media_item: [{ key: "thumb1", src: "thumb1.png" }],
39
+ },
40
+ ],
41
+ };
42
+
43
+ const items = getMediaItems(entry);
44
+ expect(items).toHaveLength(1);
45
+ expect(items[0].key).toBe("thumb1");
46
+ });
47
+
48
+ it("returns undefined if no media_group present", () => {
49
+ const entry = { ...baseEntry };
50
+ delete entry.media_group;
51
+ expect(getMediaItems(entry)).toBeUndefined();
52
+ });
53
+
54
+ it("returns undefined if media_group is present but has no image or thumbnail", () => {
55
+ const entry = {
56
+ ...baseEntry,
57
+ media_group: [
58
+ { type: "audio", media_item: [{ key: "audio1", src: "audio1.mp3" }] },
59
+ ],
60
+ };
61
+
62
+ const items = getMediaItems(entry);
63
+ expect(items).toBeUndefined();
64
+ });
65
+ });
@@ -0,0 +1,34 @@
1
+ import * as R from "ramda";
2
+ import { imageSrcFromMediaItem } from "../";
3
+ import { entry } from "./testEntry";
4
+
5
+ describe("imageSrcFromMediaItem", () => {
6
+ it("when the matching key is found and the src is not empty", () => {
7
+ const result = imageSrcFromMediaItem(entry as ZappEntry, [
8
+ "logo_thumbnail",
9
+ ]);
10
+
11
+ expect(result).toEqual(entry.media_group[1].media_item[0].src);
12
+ expect(result).not.toEqual("");
13
+ });
14
+
15
+ it("returns a media item with the 'image_base' key as a fallback", () => {
16
+ const result = imageSrcFromMediaItem(entry as ZappEntry, [
17
+ "does_not_exist",
18
+ ]);
19
+
20
+ const fallback = entry.media_group[0].media_item[0];
21
+ expect(result).toEqual(fallback.src);
22
+ expect(fallback.key).toBe("image_base");
23
+ });
24
+
25
+ it("returns undefined if the key was found but the source was empty", () => {
26
+ const badEntry: ZappEntry = R.set(
27
+ R.lensPath(["media_group", 0, "media_item", 0, "src"]),
28
+ "",
29
+ entry
30
+ );
31
+
32
+ expect(imageSrcFromMediaItem(badEntry, ["image_base"])).toBeUndefined();
33
+ });
34
+ });
@@ -1,6 +1,12 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  import * as R from "ramda";
3
3
  import { Platform } from "react-native";
4
+ import {
5
+ isFilledArray,
6
+ isEmptyArray,
7
+ } from "@applicaster/zapp-react-native-utils/arrayUtils";
8
+ import { isEmpty } from "@applicaster/zapp-react-native-utils/utils";
9
+
4
10
  import { applyTransform } from "../transform";
5
11
  import { ManifestField } from "../types";
6
12
  import { isNilOrEmpty } from "../reactUtils/helpers";
@@ -122,54 +128,77 @@ export function flattenFields(
122
128
  }
123
129
 
124
130
  /**
125
- * Retrieves the value of the "src" in the first media_item
126
- * that has the matching key provided in args.
127
- * Fallbacks: "image_base" key, or first media_item that has any "src"
128
- * @param {Object} entry Single entry from a feed
129
- * @param {Array} arg Array with a single element - the key of the media item
130
- * from which the "src" should be retrieved
131
- * @returns {?String} Value of "src", usually a URI
131
+ * Retrieves all media items from the entry's media_group that are of type "image" or "thumbnail".
132
+ *
133
+ * @param {ZappEntry} entry - The entry object containing the media_group array.
134
+ * @returns {Option<ZappMediaItem[]>} An array of media items of type "image" or "thumbnail", or undefined if no media_group is present.
135
+ */
136
+ export function getMediaItems(entry: ZappEntry): Option<ZappMediaItem[]> {
137
+ const mediaGroup = entry?.media_group || [];
138
+
139
+ if (isEmptyArray(mediaGroup)) {
140
+ return undefined;
141
+ }
142
+
143
+ const mediaItems = mediaGroup
144
+ .filter((group) => ["image", "thumbnail"].includes(group.type))
145
+ .flatMap((group) =>
146
+ Array.isArray(group.media_item) ? group.media_item : [group.media_item]
147
+ );
148
+
149
+ if (isFilledArray(mediaItems)) {
150
+ return mediaItems;
151
+ }
152
+
153
+ return undefined;
154
+ }
155
+
156
+ /**
157
+ * Retrieves the "src" value from a media item in the entry's media group,
158
+ * based on a provided key, with fallback logic.
159
+ *
160
+ * Fallback order:
161
+ * 1. Attempts to find a media item with the specified key (or "image_base" if none provided).
162
+ * 2. If not found, attempts to find a media item with the key "image_base".
163
+ * 3. If still not found, falls back to the first available media item.
164
+ *
165
+ * Special handling: If the resolved item's "src" is an empty string, returns undefined,
166
+ * since empty URIs are invalid in some platforms (e.g., React Native).
167
+ *
168
+ * @param {ZappEntry} entry - The entry object containing a media group.
169
+ * @param {string[] | unknown} arg - A single-element array containing the key to look up, or any unknown value.
170
+ * @returns {?string} The "src" URI from the matched media item, or undefined if not found or empty.
132
171
  */
133
172
  export function imageSrcFromMediaItem(
134
173
  entry: ZappEntry,
135
174
  arg: string[] | unknown
136
- ) {
175
+ ): Option<string> {
137
176
  const args: unknown = R.unless(Array.isArray, Array)(arg || []);
138
177
  const imageKey: string = args?.[0] || "image_base"; // always a single key in this function
139
- const mediaGroup = R.path<ZappMediaGroup[]>(["media_group"], entry);
140
178
 
141
- if (!mediaGroup) {
179
+ const mediaItems = getMediaItems(entry);
180
+
181
+ if (!mediaItems) {
142
182
  return undefined;
143
183
  }
144
184
 
145
- const hasTypeImageOrThumbnail = R.either(
146
- R.propEq("type", "image"),
147
- R.propEq("type", "thumbnail")
148
- );
149
-
150
- const pickMediaItemProp = R.prop<ZappMediaItem>("media_item");
185
+ // Try to find the item with the given key
186
+ let foundItem = mediaItems.find((item) => item.key === imageKey);
151
187
 
152
- const mediaItems = R.compose(
153
- R.flatten,
154
- R.map(pickMediaItemProp),
155
- R.filter(hasTypeImageOrThumbnail)
156
- )(mediaGroup);
188
+ // If not found and key was not "image_base", try to find "image_base"
189
+ if (!foundItem && imageKey !== "image_base") {
190
+ foundItem = mediaItems.find((item) => item.key === "image_base");
191
+ }
157
192
 
158
- if (!mediaItems) {
159
- return undefined;
193
+ // If still not found, default to first item
194
+ if (!foundItem) {
195
+ foundItem = mediaItems[0];
160
196
  }
161
197
 
162
- const src = R.compose(
163
- R.prop("src"),
164
- R.defaultTo(R.head(mediaItems)),
165
- R.when(R.isNil, () => R.find(R.propEq("key", "image_base"), mediaItems)),
166
- R.find(R.propEq("key", imageKey))
167
- )(mediaItems);
168
-
169
- // Special case for react native - uri cannot be an empty string (yellow warning).
170
- // R.isEmpty is tailored specifically for checks like these,
171
- // it returns false for undefined values.
172
- return R.isEmpty(src) ? undefined : src;
198
+ const src = foundItem?.src;
199
+
200
+ // React Native quirk: empty string is invalid for URIs
201
+ return isEmpty(src) ? undefined : src;
173
202
  }
174
203
 
175
204
  /**
@@ -1,8 +1,8 @@
1
1
  import { ViewStyle } from "react-native";
2
2
 
3
3
  import { useMemo } from "react";
4
- import { getUILanguage } from "../appUtils/localizationsHelper";
5
- import { utilsLogger } from "../logger";
4
+ import { getUILanguage } from "@applicaster/zapp-react-native-utils/appUtils/localizationsHelper";
5
+ import { utilsLogger } from "@applicaster/zapp-react-native-utils/logger";
6
6
 
7
7
  export {
8
8
  getLocale,
@@ -10,7 +10,7 @@ export {
10
10
  getCountryCode,
11
11
  getLocalizations,
12
12
  getUILanguage,
13
- } from "../appUtils/localizationsHelper";
13
+ } from "@applicaster/zapp-react-native-utils/appUtils/localizationsHelper";
14
14
 
15
15
  const RTL_LOCALES = [
16
16
  "ar", // Arabic
@@ -251,6 +251,19 @@ const generalContent = () => ({
251
251
  key: "screen_title",
252
252
  initial_value: null,
253
253
  },
254
+ {
255
+ type: "text_input",
256
+ label: "Maximum Selection Reached - Alert Description",
257
+ key: "msg_maximum_selection_reached_message",
258
+ initial_value:
259
+ "You’ve reached the maximum number of items you can select.",
260
+ },
261
+ {
262
+ type: "text_input",
263
+ label: "Maximum Selection Reached - Alert Button",
264
+ key: "msg_maximum_selection_reached_message_ok_button",
265
+ initial_value: "Got it",
266
+ },
254
267
  ],
255
268
  },
256
269
  advertising: {
@@ -42,14 +42,6 @@ function getPlayerConfiguration({ platform }) {
42
42
  label_tooltip:
43
43
  "Enable if you want to have adaptive background gradient based on audio background image.",
44
44
  },
45
- {
46
- key: "audio_player_background_image_query",
47
- label: "Audio Player Image Query",
48
- initial_value: "",
49
- type: "text_input",
50
- label_tooltip:
51
- "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.",
52
- },
53
45
  {
54
46
  key: "audio_player_background_image_default_color",
55
47
  label: "Audio Player Background Image Default Color",
@@ -2339,7 +2331,53 @@ function getPlayerConfiguration({ platform }) {
2339
2331
  type: "number_input",
2340
2332
  label: "Android TV Font Size",
2341
2333
  key: "android_tv_audio_player_summary_font_size",
2342
- initial_value: 25,
2334
+ initial_value: 22,
2335
+ },
2336
+ ]
2337
+ ),
2338
+ fieldsGroup(
2339
+ "Audio Player Background",
2340
+ "This section allows you to configure the audio player layout",
2341
+ [
2342
+ {
2343
+ key: "audio_player_background_color",
2344
+ label: "Background Color",
2345
+ label_tooltip:
2346
+ "Set a background color using hex format and opacity. If left empty, the default color from the app’s theme will be used.",
2347
+ type: "color_picker_rgba",
2348
+ initial_value: "rgba(17, 17, 17, 1)",
2349
+ },
2350
+ {
2351
+ key: "audio_player_background_image",
2352
+ label: "Background Image",
2353
+ label_tooltip:
2354
+ "Upload a background image for the audio player. If no image is provided, the background color will be used as a fallback.",
2355
+ type: "uploader",
2356
+ default: "",
2357
+ },
2358
+ {
2359
+ key: "audio_player_background_image_overlay",
2360
+ label: "Background Image Overlay",
2361
+ label_tooltip:
2362
+ "Add a semi-transparent color overlay to improve text readability over the background image.",
2363
+ type: "color_picker_rgba",
2364
+ initial_value: "rgba(17, 17, 17, 0.5)",
2365
+ },
2366
+ {
2367
+ type: "text_input",
2368
+ label: "Item Image Key",
2369
+ key: "audio_player_artwork_image_key",
2370
+ label_tooltip:
2371
+ "Define the key used to fetch the item’s image. If left empty, a default image will be shown.",
2372
+ initial_value: "image_base",
2373
+ },
2374
+ {
2375
+ key: "audio_player_artwork_border_radius",
2376
+ label: "Item Image Corner Radius",
2377
+ label_tooltip:
2378
+ "Set the corner radius (in pixels) for item images. Leave empty for sharp corners (0px).",
2379
+ type: "number_input",
2380
+ initial_value: 16,
2343
2381
  },
2344
2382
  ]
2345
2383
  ),
@@ -8,6 +8,7 @@ const {
8
8
 
9
9
  const { tvActionButtonsContainer } = require("./tvAction/container");
10
10
  const { tvActionButton } = require("./tvAction/button");
11
+ const { compact } = require("./_internals");
11
12
 
12
13
  const { spacingKey, absolutePositionElement } = require("./containers");
13
14
 
@@ -46,4 +47,5 @@ module.exports = {
46
47
  tvProgressBar,
47
48
  secondaryImage,
48
49
  getUpdatedSecondaryImageKeys,
50
+ compact,
49
51
  };
@@ -21,7 +21,30 @@ const ZAPPIFEST_FIELDS = {
21
21
  },
22
22
  };
23
23
 
24
- const TV_PLATFORMS = ["tvOS", "android", "LG", "samsung", "roku", "vizio"];
24
+ const TV_PLATFORMS_FOR_FONTS = [
25
+ "tvOS",
26
+ "android",
27
+ "LG",
28
+ "samsung",
29
+ "roku",
30
+ "vizio",
31
+ ];
32
+
33
+ const MOBILE_PLATFORMS = [
34
+ "ios",
35
+ "ios_for_quickbrick",
36
+ "android",
37
+ "android_for_quickbrick",
38
+ ];
39
+
40
+ const TV_PLATFORMS = [
41
+ "android_tv_for_quickbrick",
42
+ "amazon_fire_tv_for_quickbrick",
43
+ "tvos_for_quickbrick",
44
+ "samsung_tv",
45
+ "lg_tv",
46
+ "vizio",
47
+ ];
25
48
 
26
49
  const FontPropsForTVPlatform = (platform) => ({
27
50
  [`${R.toLower(platform)}FontFamily`]: {
@@ -92,7 +115,7 @@ const TV_FONT_KEY_DATA = {
92
115
  name: "focused font color",
93
116
  type: ZAPPIFEST_FIELDS.color_picker,
94
117
  },
95
- ...R.reduce(addTVFontsForPlatform, {}, TV_PLATFORMS),
118
+ ...R.reduce(addTVFontsForPlatform, {}, TV_PLATFORMS_FOR_FONTS),
96
119
  textAlignment: {
97
120
  name: "text alignment",
98
121
  type: ZAPPIFEST_FIELDS.select,
@@ -2083,4 +2106,6 @@ module.exports = {
2083
2106
  TV_CELL_BADGE_FIELDS,
2084
2107
  progressBarFields,
2085
2108
  secondaryImageFields,
2109
+ TV_PLATFORMS,
2110
+ MOBILE_PLATFORMS,
2086
2111
  };
@@ -8,7 +8,6 @@ const {
8
8
  conditional_horizontal_type,
9
9
  conditional_vertical_type,
10
10
  conditional_horizontal_type_item_fixed,
11
- conditional_horizontal_type_item_dynamic,
12
11
  conditional_horizontal_asset_on,
13
12
  conditional_horizontal_display_type_fixed,
14
13
  } = require("./utils");
@@ -48,7 +47,7 @@ const componentConfigurationFields = [
48
47
  { text: "Right", value: "right" },
49
48
  { text: "Center", value: "center" },
50
49
  ],
51
- ...conditional_horizontal_type_item_dynamic,
50
+ ...conditional_horizontal_type,
52
51
  },
53
52
  {
54
53
  type: "select",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.0-rc.99",
3
+ "version": "13.0.0",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "13.0.0-rc.99",
30
+ "@applicaster/applicaster-types": "13.0.0",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -39,22 +39,6 @@ function getPlayerConfiguration({ platform }) {
39
39
  label_tooltip:
40
40
  "Disable if you don't want the content to have a right orientation",
41
41
  },
42
- {
43
- key: "magic_background",
44
- label: "Enable Magic Background",
45
- initial_value: false,
46
- type: "switch",
47
- label_tooltip:
48
- "Enable if you want to have adaptive background gradient based on audio background image.",
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
- },
58
42
  {
59
43
  key: "audio_player_background_image_default_color",
60
44
  label: "Audio Player Background Image Default Color",
@@ -1,5 +1,9 @@
1
1
  import * as R from "ramda";
2
2
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
3
+ import { isString } from "@applicaster/zapp-react-native-utils/typeGuards";
4
+ import { isFilledArray } from "@applicaster/zapp-react-native-utils/arrayUtils";
5
+ import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
6
+
3
7
  import { getBoolFromConfigValue } from "../configurationUtils";
4
8
 
5
9
  /**
@@ -72,3 +76,22 @@ function isLiveByManager(): boolean {
72
76
  export function isLive(entry: ZappEntry): boolean {
73
77
  return isEntryLive(entry) || isLiveByManager();
74
78
  }
79
+
80
+ export const isAudioItem = (item: Option<ZappEntry>) => {
81
+ if (
82
+ isString(item?.content?.type) &&
83
+ item?.content?.type?.includes?.("audio")
84
+ ) {
85
+ return true;
86
+ }
87
+
88
+ if (isString(item?.type?.value) && item?.type?.value?.includes?.("audio")) {
89
+ return true;
90
+ }
91
+
92
+ return false;
93
+ };
94
+
95
+ export const isInlineTV = (screenData) => {
96
+ return isTV() && isFilledArray(screenData?.ui_components);
97
+ };
@@ -14,10 +14,10 @@ export function useAppState(returnIsActive) {
14
14
  };
15
15
 
16
16
  React.useEffect(() => {
17
- AppState.addEventListener("change", setAppState);
17
+ const subscription = AppState.addEventListener("change", setAppState);
18
18
 
19
19
  return () => {
20
- AppState.removeEventListener("change", setAppState);
20
+ subscription.remove();
21
21
  };
22
22
  }, []);
23
23
 
@@ -1,7 +1,7 @@
1
- import { isNil, complement, compose, map, min, prop, take, uniq } from "ramda";
1
+ import { complement, compose, isNil, map, min, prop, take, uniq } from "ramda";
2
2
  import { useDispatch } from "react-redux";
3
3
  import * as React from "react";
4
- import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds";
4
+ import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks";
5
5
  import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
6
6
  import { isNilOrEmpty } from "../../reactUtils/helpers";
7
7
  import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
@@ -9,6 +9,7 @@ import {
9
9
  getInflatedDataSourceUrl,
10
10
  getSearchContext,
11
11
  } from "@applicaster/zapp-react-native-utils/reactHooks";
12
+ import { isGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
12
13
  import { useScreenContext } from "../screen/useScreenContext";
13
14
 
14
15
  type Options = {
@@ -46,9 +47,7 @@ const filterEmptyData = (data) => {
46
47
  };
47
48
 
48
49
  const getData = (rawData) =>
49
- rawData.component_type === "gallery-qb"
50
- ? rawData.ui_components[0].data
51
- : rawData.data;
50
+ isGallery(rawData) ? rawData.ui_components[0].data : rawData.data;
52
51
 
53
52
  const extractData = compose(uniq, map(getData));
54
53
 
@@ -70,13 +69,12 @@ export const useBatchLoading = (
70
69
  const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false);
71
70
 
72
71
  // if first component is gallery-qb, take only one component for initial load
73
- const takeSize =
74
- componentsToRender?.[0]?.component_type === "gallery-qb"
75
- ? 1
76
- : min(
77
- options.initialBatchSize ?? DEFAULT_BATCH_SIZE,
78
- componentsToRender.length
79
- );
72
+ const takeSize = isGallery(componentsToRender?.[0])
73
+ ? 1
74
+ : min(
75
+ options.initialBatchSize ?? DEFAULT_BATCH_SIZE,
76
+ componentsToRender.length
77
+ );
80
78
 
81
79
  const takeBatchSize = React.useCallback(take(takeSize), [takeSize]);
82
80
 
@@ -146,11 +144,11 @@ export const useBatchLoading = (
146
144
  }
147
145
  }
148
146
  });
149
- }, [feedUrls]);
147
+ }, [feedUrls, feeds]);
150
148
 
151
149
  React.useEffect(() => {
152
150
  runBatchLoading();
153
- }, []);
151
+ }, [runBatchLoading]); // Adding runBatchLoading as a dependency to ensure that it reloads feeds when clearPipesData is called
154
152
 
155
153
  React.useEffect(() => {
156
154
  // check if all feeds are ready and set hasEverBeenReady to true
@@ -3,7 +3,7 @@ import { platformSelect } from "../../reactUtils";
3
3
  const TAB_BAR_HEIGHT_IOS = 49;
4
4
  const TAB_BAR_HEIGHT_ANDROID = 56;
5
5
 
6
- export const useGetTabBarHeight = () =>
6
+ export const getTabBarHeight = () =>
7
7
  platformSelect({
8
8
  ios: TAB_BAR_HEIGHT_IOS,
9
9
  android: TAB_BAR_HEIGHT_ANDROID,
@@ -1,12 +1,19 @@
1
1
  import { useGetNavBarTopBorderWidth } from "./useGetNavBarTopBorderWidth";
2
- import { useGetTabBarHeight } from "./useGetTabBarHeight";
2
+ import { getTabBarHeight } from "./getTabBarHeight";
3
+ import { useNavigation } from "./useNavigation";
3
4
  import { MenuTypes, useNavigationType } from "./useNavigationType";
5
+ import { useNavigationPluginData } from "./useNavigationPluginData";
4
6
 
5
7
  export const useGetBottomTabBarHeight = (): number => {
8
+ const { activeRiver } = useNavigation();
9
+
10
+ const navigationPluginData = useNavigationPluginData(activeRiver);
11
+ const navigationType = useNavigationType(navigationPluginData);
12
+
6
13
  const topBorderWidth = useGetNavBarTopBorderWidth();
7
- const tabBarHeight = useGetTabBarHeight();
14
+ const tabBarHeight = getTabBarHeight();
8
15
 
9
- const isBottomBarNavigation = useNavigationType() === MenuTypes.bottomTabBar;
16
+ const isBottomBarNavigation = navigationType === MenuTypes.bottomTabBar;
10
17
 
11
18
  return !isBottomBarNavigation ? 0 : tabBarHeight + topBorderWidth;
12
19
  };
@@ -1,13 +1,17 @@
1
1
  import { useRoute } from "./useRoute";
2
2
 
3
- export const useNavigationPluginData = (): ZappNavigation | undefined => {
3
+ export const useNavigationPluginData = (
4
+ screenData?: LegacyNavigationScreenData | null
5
+ ): ZappNavigation | undefined => {
4
6
  const {
5
7
  screenData: useRouteScreenData,
6
8
  }: { screenData: QuickBrickNavigationData | null } = useRoute();
7
9
 
8
- const navigations = useRouteScreenData?.targetScreen
9
- ? (useRouteScreenData.targetScreen as ZappRiver).navigations
10
- : (useRouteScreenData as ZappRiver).navigations;
10
+ const activeScreenData = screenData ?? useRouteScreenData;
11
+
12
+ const navigations = activeScreenData?.targetScreen
13
+ ? (activeScreenData.targetScreen as ZappRiver).navigations
14
+ : (activeScreenData as ZappRiver).navigations;
11
15
 
12
16
  const navigationMenu = navigations?.find((nav) => nav.category === "menu");
13
17