@applicaster/zapp-react-native-utils 14.0.0-alpha.8419134002 → 14.0.0-alpha.9848043301

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 (46) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +1 -1
  2. package/analyticsUtils/AnalyticsEvents/helper.ts +0 -81
  3. package/analyticsUtils/AnalyticsEvents/sendOnClickEvent.ts +4 -14
  4. package/analyticsUtils/__tests__/analyticsUtils.test.js +0 -14
  5. package/analyticsUtils/events.ts +0 -8
  6. package/appUtils/accessibilityManager/index.ts +3 -3
  7. package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
  8. package/arrayUtils/index.ts +2 -7
  9. package/configurationUtils/__tests__/configurationUtils.test.js +31 -0
  10. package/configurationUtils/index.ts +34 -63
  11. package/focusManager/FocusManager.ts +6 -4
  12. package/manifestUtils/{_internals/index.js → _internals.js} +25 -2
  13. package/manifestUtils/createConfig.js +1 -4
  14. package/manifestUtils/defaultManifestConfigurations/player.js +200 -1239
  15. package/manifestUtils/progressBar/__tests__/mobileProgressBar.test.js +30 -0
  16. package/package.json +2 -2
  17. package/playerUtils/__tests__/configurationUtils.test.ts +65 -1
  18. package/playerUtils/configurationGenerator.ts +2572 -0
  19. package/playerUtils/configurationUtils.ts +44 -0
  20. package/playerUtils/index.ts +51 -2
  21. package/playerUtils/useValidatePlayerConfig.tsx +19 -22
  22. package/reactHooks/feed/useBatchLoading.ts +3 -3
  23. package/reactHooks/feed/usePipesCacheReset.ts +1 -1
  24. package/reactHooks/navigation/useIsScreenActive.ts +5 -9
  25. package/reactHooks/screen/useScreenContext.ts +1 -1
  26. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +1 -2
  27. package/riverComponetsMeasurementProvider/index.tsx +1 -1
  28. package/services/js2native.ts +0 -1
  29. package/time/BackgroundTimer.ts +3 -5
  30. package/utils/index.ts +1 -16
  31. package/arrayUtils/__tests__/isEmptyArray.test.ts +0 -63
  32. package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +0 -144
  33. package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +0 -72
  34. package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +0 -110
  35. package/audioPlayerUtils/assets/index.ts +0 -2
  36. package/audioPlayerUtils/index.ts +0 -242
  37. package/conf/player/__tests__/selectors.test.ts +0 -34
  38. package/conf/player/selectors.ts +0 -10
  39. package/configurationUtils/__tests__/getMediaItems.test.ts +0 -65
  40. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +0 -34
  41. package/manifestUtils/_internals/getDefaultConfiguration.js +0 -28
  42. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +0 -54
  43. package/playerUtils/_internals/__tests__/utils.test.ts +0 -71
  44. package/playerUtils/_internals/index.ts +0 -1
  45. package/playerUtils/_internals/utils.ts +0 -31
  46. package/playerUtils/getPlayerActionButtons.ts +0 -17
@@ -25,7 +25,7 @@ import {
25
25
  resolveObjectValues,
26
26
  } from "../appUtils/contextKeysManager/contextResolver";
27
27
  import { useNavigation } from "../reactHooks";
28
- import { get } from "../utils";
28
+ import { get } from "lodash";
29
29
 
30
30
  import {
31
31
  useContentTypes,
@@ -4,12 +4,9 @@ import {
4
4
  ANALYTICS_COMPONENT_EVENTS,
5
5
  ANALYTICS_CORE_EVENTS,
6
6
  ANALYTICS_ENTRY_EVENTS,
7
- ANALYTICS_PREFERENCES_EVENTS,
8
7
  DOWNLOADS_EVENTS,
9
8
  } from "../events";
10
9
  import { isEmptyOrNil } from "../../cellUtils";
11
- import { get } from "lodash";
12
- import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
13
10
 
14
11
  export enum OfflineItemState {
15
12
  notExist = "NOT_EXISTS",
@@ -105,84 +102,6 @@ export function eventForComponent(
105
102
  return analyticsProps;
106
103
  }
107
104
 
108
- /**
109
- * Checks if an item is currently selected in localStorage based on its actions
110
- * @param item - The item to check
111
- * @returns boolean indicating if the item is currently selected
112
- */
113
- function isItemPreviouslySelected(item: any): boolean {
114
- const actions = item?.extensions?.tap_actions?.actions;
115
-
116
- if (!actions) {
117
- return false;
118
- }
119
-
120
- const localStorageAction = actions.find(
121
- (action) => action?.type === "localStorageToggleFlag"
122
- );
123
-
124
- if (!localStorageAction?.options?.key) {
125
- return false;
126
- }
127
-
128
- const keyNamespace = localStorageAction.options.key;
129
-
130
- const tag = localStorageAction.options?.selector
131
- ? get(item, localStorageAction.options.selector)
132
- : (item.extensions?.tag ?? item.id);
133
-
134
- if (!tag) {
135
- return false;
136
- }
137
-
138
- try {
139
- const multiSelectProvider =
140
- StorageMultiSelectProvider.getProvider(keyNamespace);
141
-
142
- const selectedItems = multiSelectProvider.getSelectedItems();
143
-
144
- return selectedItems.includes(tag);
145
- } catch (error) {
146
- return false;
147
- }
148
- }
149
-
150
- export function getLocalStorageSetPayload(extraProps) {
151
- const { item } = extraProps;
152
-
153
- const hasLocalStorageSetAction = item?.extensions?.tap_actions?.actions?.some(
154
- (action) => action?.type === "localStorageSet"
155
- );
156
-
157
- if (!hasLocalStorageSetAction) {
158
- return null;
159
- }
160
-
161
- return {
162
- [ANALYTICS_PREFERENCES_EVENTS.ITEM_SELECTED_STATUS]: true,
163
- };
164
- }
165
-
166
- export function getLocalStorageToggleFlagPayload(extraProps) {
167
- const { item } = extraProps;
168
-
169
- const hasLocalStorageToggleAction =
170
- item?.extensions?.tap_actions?.actions?.some(
171
- (action) => action?.type === "localStorageToggleFlag"
172
- );
173
-
174
- if (!hasLocalStorageToggleAction) {
175
- return null;
176
- }
177
-
178
- const previouslySelected = isItemPreviouslySelected(item);
179
-
180
- return {
181
- [ANALYTICS_PREFERENCES_EVENTS.ITEM_SELECTED_STATUS]: !previouslySelected,
182
- [ANALYTICS_PREFERENCES_EVENTS.PREVIOUS_SELECTED_STATE]: previouslySelected,
183
- };
184
- }
185
-
186
105
  export function playEventForType(item) {
187
106
  const itemType = item?.type && item.type?.value;
188
107
 
@@ -1,13 +1,13 @@
1
1
  import { log_error, log_debug } from "../logger";
2
- import { ANALYTICS_CORE_EVENTS, ACTION_TYPE } from "../events";
2
+
3
+ import { ANALYTICS_CORE_EVENTS } from "../events";
4
+
3
5
  import { postAnalyticEvent } from "../manager";
4
6
  import {
5
7
  replaceAnalyticsPropsNils,
6
8
  eventForEntry,
7
9
  eventForComponent,
8
10
  extensionsEvents,
9
- getLocalStorageSetPayload,
10
- getLocalStorageToggleFlagPayload,
11
11
  } from "./helper";
12
12
 
13
13
  declare type AnalyticsDefaultHelperProperties = {
@@ -26,16 +26,7 @@ export const sendOnClickEvent = ({
26
26
  const castedExtraProps: ExtraProps = extraProps;
27
27
  const componentData = component || extraProps.component;
28
28
  const data = zappPipesData || extraProps.zappPipesData;
29
-
30
- const actionCellPayload =
31
- extraProps?.item?.type?.value === ACTION_TYPE
32
- ? getLocalStorageSetPayload(extraProps) ||
33
- getLocalStorageToggleFlagPayload(extraProps)
34
- : null;
35
-
36
- const eventName = actionCellPayload
37
- ? ANALYTICS_CORE_EVENTS.TAP_SELECTABLE_CELL
38
- : ANALYTICS_CORE_EVENTS.TAP_CELL;
29
+ const eventName = ANALYTICS_CORE_EVENTS.TAP_CELL;
39
30
 
40
31
  if (!analyticsScreenData) {
41
32
  log_error(
@@ -53,7 +44,6 @@ export const sendOnClickEvent = ({
53
44
  ...replaceAnalyticsPropsNils({
54
45
  ...analyticsScreenData,
55
46
  }),
56
- ...actionCellPayload,
57
47
  };
58
48
 
59
49
  if (analyticsCustomProperties) {
@@ -3,22 +3,8 @@ import { ANALYTICS_CORE_EVENTS } from "../events";
3
3
 
4
4
  jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
5
5
  isWeb: jest.fn(),
6
- platformSelect: jest.fn(
7
- (options) => options.android || options.ios || options.web
8
- ),
9
6
  }));
10
7
 
11
- jest.mock(
12
- "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider",
13
- () => ({
14
- StorageMultiSelectProvider: {
15
- getProvider: jest.fn(() => ({
16
- getSelectedItems: jest.fn(() => []),
17
- })),
18
- },
19
- })
20
- );
21
-
22
8
  const mock_postAnalyticEvent = jest.fn();
23
9
  const mock_startAnalyticsTimedEvent = jest.fn();
24
10
  const mock_endAnalyticsTimedEvent = jest.fn();
@@ -17,11 +17,8 @@ export const SCREEN_VIEW_EVENTS = {
17
17
  TIME_ON_SCREEN: "time_on_screen",
18
18
  };
19
19
 
20
- export const ACTION_TYPE = "action";
21
-
22
20
  export const TAPPING_EVENTS = {
23
21
  TAP_CELL: "tap_cell",
24
- TAP_SELECTABLE_CELL: "tap_selectable_cell",
25
22
  TAP_MENU: "tap_menu",
26
23
  TAP_NAVBAR_BACK_BUTTON: "tap_navbar_back_button",
27
24
  };
@@ -102,11 +99,6 @@ export const ANALYTICS_COMPONENT_EVENTS = {
102
99
  COMPONENT_SOURCE: "component_source",
103
100
  };
104
101
 
105
- export const ANALYTICS_PREFERENCES_EVENTS = {
106
- ITEM_SELECTED_STATUS: "item_selected_status",
107
- PREVIOUS_SELECTED_STATE: "previous_selected_state",
108
- };
109
-
110
102
  // ---------------- EVENTS ---------------------
111
103
  export const AD_EVENT = {
112
104
  ad_break_start: "player_ad_break_start",
@@ -1,9 +1,9 @@
1
1
  import { BehaviorSubject } from "rxjs";
2
2
  import { accessibilityManagerLogger as logger } from "./logger";
3
- import { TTSManager } from "../platform";
3
+ import { TTSManager } from "../platform/platformUtils";
4
4
  import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
5
5
  import { AccessibilityRole } from "react-native";
6
- import { toString } from "../../utils";
6
+ import _ from "lodash";
7
7
 
8
8
  export class AccessibilityManager {
9
9
  private static _instance: AccessibilityManager | null = null;
@@ -137,7 +137,7 @@ export class AccessibilityManager {
137
137
  }
138
138
 
139
139
  public getButtonAccessibilityProps(name: string): AccessibilityProps {
140
- const buttonName = toString(name);
140
+ const buttonName = _.toString(name);
141
141
 
142
142
  const buttonConfig = BUTTON_ACCESSIBILITY_KEYS[buttonName];
143
143
 
@@ -32,7 +32,7 @@ describe("isFilledArray", () => {
32
32
  });
33
33
 
34
34
  it("NaN is not array", () => {
35
- const value = NaN;
35
+ const value = "";
36
36
 
37
37
  expect(isFilledArray(value)).toBe(false);
38
38
  });
@@ -99,17 +99,12 @@ export const makeListOf = (value: unknown, size: number): number[] => {
99
99
 
100
100
  /** Checks if a value is a non-empty array */
101
101
  export function isFilledArray(value: unknown): boolean {
102
- return Array.isArray(value) && value.length > 0;
103
- }
104
-
105
- /** Checks if a value is a empty array */
106
- export function isEmptyArray(value: unknown): boolean {
107
- return Array.isArray(value) && value.length === 0;
102
+ return R.is(Array, value) && R.length(value) > 0;
108
103
  }
109
104
 
110
105
  // get random item from the list
111
106
  export const sample = (xs: unknown[]): unknown => {
112
- invariant(Array.isArray(xs), `input value is not an array: ${xs}`);
107
+ invariant(R.is(Array, xs), `input value is not a array: ${xs}`);
113
108
  invariant(isFilledArray(xs), `input array is empty: ${xs}`);
114
109
 
115
110
  const index = Math.floor(Math.random() * xs.length);
@@ -1,8 +1,11 @@
1
+ import * as R from "ramda";
1
2
  import {
2
3
  populateConfigurationValues,
4
+ imageSrcFromMediaItem,
3
5
  getBoolFromConfigValue,
4
6
  remapUpdatedKeys,
5
7
  } from "../";
8
+ import { entry } from "./testEntry";
6
9
 
7
10
  describe("getBoolFromConfigValue", () => {
8
11
  it('returns true if value is "true" string', () => {
@@ -54,6 +57,34 @@ describe("getBoolFromConfigValue", () => {
54
57
  });
55
58
  });
56
59
 
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
+
57
88
  describe("populateConfigurationValues", () => {
58
89
  it("transforms and returns the valid values", () => {
59
90
  const fields = [
@@ -1,12 +1,6 @@
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
-
10
4
  import { applyTransform } from "../transform";
11
5
  import { ManifestField } from "../types";
12
6
  import { isNilOrEmpty } from "../reactUtils/helpers";
@@ -128,77 +122,54 @@ export function flattenFields(
128
122
  }
129
123
 
130
124
  /**
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.
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
171
132
  */
172
133
  export function imageSrcFromMediaItem(
173
134
  entry: ZappEntry,
174
135
  arg: string[] | unknown
175
- ): Option<string> {
136
+ ) {
176
137
  const args: unknown = R.unless(Array.isArray, Array)(arg || []);
177
138
  const imageKey: string = args?.[0] || "image_base"; // always a single key in this function
139
+ const mediaGroup = R.path<ZappMediaGroup[]>(["media_group"], entry);
178
140
 
179
- const mediaItems = getMediaItems(entry);
180
-
181
- if (!mediaItems) {
141
+ if (!mediaGroup) {
182
142
  return undefined;
183
143
  }
184
144
 
185
- // Try to find the item with the given key
186
- let foundItem = mediaItems.find((item) => item.key === imageKey);
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");
187
151
 
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
- }
152
+ const mediaItems = R.compose(
153
+ R.flatten,
154
+ R.map(pickMediaItemProp),
155
+ R.filter(hasTypeImageOrThumbnail)
156
+ )(mediaGroup);
192
157
 
193
- // If still not found, default to first item
194
- if (!foundItem) {
195
- foundItem = mediaItems[0];
158
+ if (!mediaItems) {
159
+ return undefined;
196
160
  }
197
161
 
198
- const src = foundItem?.src;
199
-
200
- // React Native quirk: empty string is invalid for URIs
201
- return isEmpty(src) ? undefined : src;
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;
202
173
  }
203
174
 
204
175
  /**
@@ -243,10 +243,12 @@ class FocusManager {
243
243
  }
244
244
 
245
245
  blurPrevious(options?: FocusManager.Android.CallbackOptions) {
246
- FocusManager.instance.prevFocused?.onBlur?.(
247
- FocusManager.instance.prevFocused,
248
- options ?? {} // Adding fallback to avoid potential regression caused by #7509
249
- );
246
+ if (options) {
247
+ FocusManager.instance.prevFocused?.onBlur?.(
248
+ FocusManager.instance.prevFocused,
249
+ options
250
+ );
251
+ }
250
252
  }
251
253
 
252
254
  onDisableFocusChange = (id) => {
@@ -1,7 +1,6 @@
1
1
  const R = require("ramda");
2
2
  const camelize = require("camelize");
3
-
4
- const { getDefaultConfiguration } = require("./getDefaultConfiguration");
3
+ const { defaultConfigurations } = require("./defaultManifestConfigurations");
5
4
 
6
5
  const toSnakeCase = R.compose(R.replace(/\s/g, "_"), R.toLower, R.trim);
7
6
 
@@ -174,6 +173,30 @@ function generateFieldsFromDefaultsWithoutPrefixedLabel(
174
173
  )(fields);
175
174
  }
176
175
 
176
+ /**
177
+ * returns default configuration keys for provided plugin type
178
+ * @param {('general-content'|'player')} pluginType
179
+ * @param options manifest generator information
180
+ * @param {string} options.version manifest version
181
+ * @param {string} options.platform qb platform value
182
+ */
183
+ function getDefaultConfiguration(pluginType, options) {
184
+ const defConfig = R.compose(
185
+ R.unless(R.isNil, (fn) => fn(options)),
186
+ R.propOr(null, pluginType)
187
+ )(defaultConfigurations);
188
+
189
+ if (!defConfig) {
190
+ const availableKeys = R.keys(defaultConfigurations);
191
+
192
+ const message = `Requested key "${pluginType}" doesn't exist in the default configuration\nAvailable keys: ${availableKeys}`;
193
+ // eslint-disable-next-line no-console
194
+ console.warn(message);
195
+ }
196
+
197
+ return defConfig;
198
+ }
199
+
177
200
  module.exports = {
178
201
  toSnakeCase,
179
202
  toCamelCase,
@@ -1,8 +1,5 @@
1
1
  const R = require("ramda");
2
-
3
- const {
4
- getDefaultConfiguration,
5
- } = require("./_internals/getDefaultConfiguration");
2
+ const { getDefaultConfiguration } = require("./_internals");
6
3
 
7
4
  const indexByKey = R.indexBy((obj) => R.prop(obj.group ? "label" : "key", obj));
8
5
  const propIfExist = R.curry((prop) => R.when(R.prop(prop), R.prop(prop)));