@applicaster/zapp-react-native-utils 15.0.0-rc.99 → 15.1.0-alpha.2915689988

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 (160) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +51 -5
  2. package/actionsExecutor/feedDecorator.ts +6 -6
  3. package/adsUtils/index.ts +2 -2
  4. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -2
  5. package/analyticsUtils/PlayerAnalyticsManager.ts +11 -1
  6. package/analyticsUtils/README.md +1 -1
  7. package/analyticsUtils/__tests__/analyticsMapper.test.ts +35 -0
  8. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testACP_events.json +1 -1
  9. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testBlockUnlistedParams_rules.json +1 -1
  10. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_events.json +4 -0
  11. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_rules.json +6 -0
  12. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEventRegex_events.json +3 -9
  13. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_events.json +18 -0
  14. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_rules.json +16 -0
  15. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_events.json +4 -0
  16. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_rules.json +6 -0
  17. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_events.json +4 -0
  18. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_rules.json +6 -0
  19. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_events.json +4 -0
  20. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_rules.json +6 -0
  21. package/analyticsUtils/__tests__/fixtures/index.js +20 -0
  22. package/analyticsUtils/analyticsMapper.ts +4 -1
  23. package/analyticsUtils/playerAnalyticsTracker.ts +26 -3
  24. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +188 -0
  25. package/appUtils/HooksManager/__tests__/hooksManager.test.js +2 -16
  26. package/appUtils/HooksManager/index.ts +47 -17
  27. package/appUtils/RiverFocusManager/{index.ts → index.js} +18 -25
  28. package/appUtils/accessibilityManager/const.ts +0 -4
  29. package/appUtils/accessibilityManager/utils.ts +1 -0
  30. package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +2 -7
  31. package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +0 -48
  32. package/appUtils/contextKeysManager/contextResolver.ts +22 -51
  33. package/appUtils/contextKeysManager/index.ts +10 -65
  34. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +0 -3
  35. package/appUtils/focusManager/index.ios.ts +4 -43
  36. package/appUtils/focusManagerAux/utils/index.ts +3 -5
  37. package/appUtils/focusManagerAux/utils/utils.ios.ts +3 -199
  38. package/appUtils/keyCodes/keys/keys.web.ts +4 -1
  39. package/appUtils/localizationsHelper.ts +4 -0
  40. package/appUtils/orientationHelper.ts +4 -2
  41. package/appUtils/platform/platformUtils.ts +1 -1
  42. package/appUtils/playerManager/__tests__/playerFactory.test.ts +150 -0
  43. package/appUtils/playerManager/index.ts +9 -0
  44. package/appUtils/playerManager/player.ts +1 -5
  45. package/appUtils/playerManager/playerFactory.ts +17 -34
  46. package/appUtils/playerManager/playerNative.ts +22 -30
  47. package/appUtils/playerManager/usePlayer.tsx +5 -3
  48. package/appUtils/playerManager/usePlayerState.tsx +2 -14
  49. package/cellUtils/__tests__/cellUtils.test.ts +39 -0
  50. package/cellUtils/index.ts +11 -1
  51. package/cloudEventsUtils/index.ts +1 -65
  52. package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
  53. package/dateUtils/__tests__/dayjs.test.ts +0 -3
  54. package/dateUtils/index.ts +2 -0
  55. package/focusManager/aux/index.ts +1 -1
  56. package/headersUtils/__tests__/headersUtils.test.js +1 -11
  57. package/headersUtils/index.ts +1 -2
  58. package/manifestUtils/defaultManifestConfigurations/player.js +7 -75
  59. package/manifestUtils/tvAction/container/index.js +1 -1
  60. package/numberUtils/__tests__/toNumber.test.ts +0 -12
  61. package/numberUtils/index.ts +1 -19
  62. package/package.json +4 -4
  63. package/playerUtils/index.ts +24 -2
  64. package/pluginUtils/index.ts +4 -9
  65. package/reactHooks/advertising/index.ts +2 -2
  66. package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
  67. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  68. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +2 -1
  69. package/reactHooks/casting/index.ts +1 -0
  70. package/reactHooks/casting/useIsCasting.tsx +57 -0
  71. package/reactHooks/cell-click/__tests__/index.test.js +3 -1
  72. package/reactHooks/componentsMap/index.ts +55 -0
  73. package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
  74. package/reactHooks/connection/__tests__/index.test.js +1 -1
  75. package/reactHooks/debugging/__tests__/index.test.js +4 -4
  76. package/reactHooks/device/useIsTablet.tsx +19 -14
  77. package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
  78. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +23 -32
  79. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
  80. package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +1 -4
  81. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +30 -42
  82. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +1 -1
  83. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +1 -1
  84. package/reactHooks/feed/useBatchLoading.ts +1 -7
  85. package/reactHooks/feed/useEntryScreenId.ts +2 -2
  86. package/reactHooks/feed/useInflatedUrl.ts +1 -1
  87. package/reactHooks/feed/usePipesCacheReset.ts +1 -3
  88. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -13
  89. package/reactHooks/layout/__tests__/index.test.tsx +1 -1
  90. package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
  91. package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.tsx → useDimensions.test.ts} +25 -105
  92. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
  93. package/reactHooks/navigation/__tests__/index.test.tsx +4 -2
  94. package/reactHooks/navigation/index.ts +6 -7
  95. package/reactHooks/navigation/useRoute.ts +6 -8
  96. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +10 -27
  97. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
  98. package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
  99. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
  100. package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
  101. package/reactHooks/resolvers/useCellResolver.ts +2 -6
  102. package/reactHooks/resolvers/useComponentResolver.ts +13 -9
  103. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
  104. package/reactHooks/screen/__tests__/useCurrentScreenIsHook.test.ts +103 -0
  105. package/reactHooks/screen/__tests__/useCurrentScreenIsStartupHook.test.ts +94 -0
  106. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
  107. package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
  108. package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +4 -12
  109. package/reactHooks/screen/index.ts +6 -0
  110. package/reactHooks/screen/useCurrentScreenIsHook.ts +9 -0
  111. package/reactHooks/screen/useCurrentScreenIsStartupHook.ts +8 -0
  112. package/reactHooks/screen/useIsStandaloneFullscreen.ts +12 -0
  113. package/reactHooks/screen/useTargetScreenData.ts +2 -4
  114. package/reactHooks/state/__tests__/useComponentScreenState.test.ts +246 -0
  115. package/reactHooks/state/index.ts +2 -0
  116. package/reactHooks/state/useComponentScreenState.ts +45 -0
  117. package/reactHooks/state/useRivers.ts +1 -1
  118. package/reactHooks/usePluginConfiguration.ts +2 -2
  119. package/reactHooks/utils/__tests__/index.test.js +1 -1
  120. package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +22 -4
  121. package/rectUtils/index.ts +2 -2
  122. package/screenPickerUtils/index.ts +5 -0
  123. package/screenState/__tests__/index.test.ts +1 -1
  124. package/stringUtils/index.ts +1 -1
  125. package/testUtils/index.tsx +21 -30
  126. package/typeGuards/index.ts +0 -4
  127. package/utils/__tests__/clone.test.ts +158 -0
  128. package/utils/clone.ts +7 -0
  129. package/utils/index.ts +1 -13
  130. package/utils/path.ts +3 -6
  131. package/utils/pathOr.ts +1 -5
  132. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +2 -2
  133. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
  134. package/zappFrameworkUtils/loginPluginUtils.ts +1 -1
  135. package/adsUtils/__tests__/createVMAP.test.ts +0 -419
  136. package/appDataUtils/__tests__/urlScheme.test.ts +0 -678
  137. package/appUtils/focusManagerAux/utils/index.ios.ts +0 -122
  138. package/cloudEventsUtils/__tests__/index.test.ts +0 -529
  139. package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +0 -207
  140. package/errorUtils/__tests__/GeneralError.test.ts +0 -97
  141. package/errorUtils/__tests__/HttpStatusCode.test.ts +0 -344
  142. package/errorUtils/__tests__/MissingPluginError.test.ts +0 -113
  143. package/errorUtils/__tests__/NetworkError.test.ts +0 -202
  144. package/errorUtils/__tests__/getParsedResponse.test.ts +0 -188
  145. package/errorUtils/__tests__/invariant.test.ts +0 -112
  146. package/numberUtils/__tests__/toPositiveNumber.test.ts +0 -165
  147. package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +0 -537
  148. package/reactHooks/dev/__tests__/useReRenderLog.test.ts +0 -188
  149. package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +0 -580
  150. package/rectUtils/__tests__/index.test.ts +0 -549
  151. package/screenPickerUtils/__tests__/index.test.ts +0 -333
  152. package/searchUtils/const.ts +0 -7
  153. package/searchUtils/index.ts +0 -3
  154. package/time/__tests__/BackgroundTimer.test.ts +0 -156
  155. package/time/__tests__/Timer.test.ts +0 -236
  156. package/typeGuards/__tests__/isString.test.ts +0 -21
  157. package/utils/__tests__/mergeRight.test.ts +0 -48
  158. package/utils/__tests__/selectors.test.ts +0 -124
  159. package/utils/mergeRight.ts +0 -5
  160. package/utils/selectors.ts +0 -46
@@ -23,9 +23,12 @@ import {
23
23
  EntryResolver,
24
24
  resolveObjectValues,
25
25
  } from "../appUtils/contextKeysManager/contextResolver";
26
- import { useNavigation, useRivers } from "../reactHooks";
26
+ import { useNavigation } from "../reactHooks";
27
27
 
28
- import { useContentTypes } from "@applicaster/zapp-react-native-redux/hooks";
28
+ import {
29
+ useContentTypes,
30
+ usePickFromState,
31
+ } from "@applicaster/zapp-react-native-redux/hooks";
29
32
  import { useSubscriberFor } from "../reactHooks/useSubscriberFor";
30
33
  import { APP_EVENTS } from "../appUtils/events";
31
34
  import {
@@ -164,7 +167,7 @@ const prepareDefaultActions = (actionExecutor) => {
164
167
 
165
168
  const entry = context?.entry || {};
166
169
  const entryResolver = new EntryResolver(entry);
167
- const screenData = context?.screenStateStore.getState().data || {};
170
+ const screenData = context?.screenStateStore?.getState().data || {};
168
171
  const screenResolver = new EntryResolver(screenData || {});
169
172
 
170
173
  const data =
@@ -275,7 +278,7 @@ export function withActionExecutor(Component) {
275
278
 
276
279
  return function ActionExecutorComponent(props: Props) {
277
280
  const navigator = useNavigation();
278
- const rivers = useRivers();
281
+ const { rivers } = usePickFromState(["rivers"]);
279
282
  const contentTypes = useContentTypes();
280
283
 
281
284
  const handlers = useMemo(() => {
@@ -315,6 +318,44 @@ export function withActionExecutor(Component) {
315
318
  return ActionResult.Error;
316
319
  }
317
320
 
321
+ const navigationAction = action.options?.navigationAction;
322
+ const entrySource = action.options?.entry;
323
+
324
+ const entry = entrySource
325
+ ? entrySource === "@{entry/}"
326
+ ? context?.entry
327
+ : entrySource
328
+ : null;
329
+
330
+ if (entry) {
331
+ if (typeof entry !== "object") {
332
+ log_error(
333
+ `navigateToScreen: entry option is not an object, entry: ${entry}`
334
+ );
335
+
336
+ return ActionResult.Error;
337
+ }
338
+
339
+ log_info(
340
+ `navigateToScreen: navigating to screen type: ${screenType} with entry id: ${entry.id}`
341
+ );
342
+
343
+ const overriddenEntry = {
344
+ ...entry,
345
+ type: {
346
+ value: screenType,
347
+ },
348
+ };
349
+
350
+ if (navigationAction === "push") {
351
+ navigator.push(overriddenEntry);
352
+ } else {
353
+ navigator.replace(overriddenEntry);
354
+ }
355
+
356
+ return ActionResult.Success;
357
+ }
358
+
318
359
  const screenId = contentTypes?.[screenType]?.screen_id || null;
319
360
 
320
361
  if (!screenId) {
@@ -334,7 +375,12 @@ export function withActionExecutor(Component) {
334
375
  }
335
376
 
336
377
  context?.callback?.({ success: false, error: null, abort: true });
337
- navigator.replace(river);
378
+
379
+ if (navigationAction === "push") {
380
+ navigator.push(river);
381
+ } else {
382
+ navigator.replace(river);
383
+ }
338
384
 
339
385
  return ActionResult.Success;
340
386
  }
@@ -27,7 +27,7 @@ function makeMultiSelect(feed: ZappFeed, key, decoratedFeed) {
27
27
  );
28
28
 
29
29
  const behavior = {
30
- ...feed.extensions?.behavior,
30
+ ...feed.extensions?.["behavior"],
31
31
  select_mode: "multi",
32
32
  current_selection: `@{${scope}/${key}}`,
33
33
  };
@@ -75,7 +75,7 @@ function makeSingleSelect(feed: ZappFeed, key, decoratedFeed) {
75
75
  );
76
76
 
77
77
  const behavior = {
78
- ...feed.extensions?.behavior,
78
+ ...feed.extensions?.["behavior"],
79
79
  select_mode: "single",
80
80
  current_selection: `@{${scope}/${key}}`,
81
81
  };
@@ -141,11 +141,11 @@ function makeSingleSelect(feed: ZappFeed, key, decoratedFeed) {
141
141
  }
142
142
 
143
143
  export const decorateFeed = (feed: ZappFeed) => {
144
- if (!(feed.extensions?.role === "preference_editor")) {
144
+ if (!(feed.extensions?.["role"] === "preference_editor")) {
145
145
  return feed;
146
146
  }
147
147
 
148
- const key = feed.extensions?.preference_editor_options?.key;
148
+ const key = feed.extensions?.["preference_editor_options"]?.["key"];
149
149
 
150
150
  if (!key) {
151
151
  log_error(
@@ -160,8 +160,8 @@ export const decorateFeed = (feed: ZappFeed) => {
160
160
  const decoratedFeed = R.clone(feed);
161
161
 
162
162
  const isSingleSelect =
163
- (feed.extensions?.preference_editor_options?.select_mode ||
164
- feed.extensions?.behavior?.select_mode) === "single";
163
+ (feed.extensions?.["preference_editor_options"]?.select_mode ||
164
+ feed.extensions?.["behavior"]?.select_mode) === "single";
165
165
 
166
166
  if (isSingleSelect) {
167
167
  return makeSingleSelect(feed, key, decoratedFeed);
package/adsUtils/index.ts CHANGED
@@ -33,10 +33,10 @@ function convertOffset(offset: any): string {
33
33
  }
34
34
 
35
35
  function createAdBreak(ad: AdMap): string {
36
- const offset = ad.offset;
36
+ const offset = ad["offset"];
37
37
  const id = offset.toString();
38
38
  const timestamp = convertOffset(offset);
39
- const url = ad.ad_url.toString().trim();
39
+ const url = ad["ad_url"].toString().trim();
40
40
 
41
41
  return `
42
42
  <vmap:AdBreak timeOffset="${timestamp}" breakType="linear" breakId="break-${id}">
@@ -6,9 +6,9 @@ import { postAnalyticEvent } from "../manager";
6
6
  import { ANALYTICS_CORE_EVENTS } from "../events";
7
7
 
8
8
  type AnalyticsDefaultHelperProperties = {
9
- analyticsScreenData: AnalyticsScreenProperties;
9
+ analyticsScreenData?: AnalyticsScreenProperties;
10
10
  extraProps: any;
11
- props;
11
+ props?: any;
12
12
  };
13
13
 
14
14
  export const sendMenuClickEvent = ({
@@ -23,6 +23,15 @@ class PlayerAnalyticsTrackerFactory {
23
23
  return new PlayerAnalyticsTracker();
24
24
  }
25
25
  }
26
+
27
+ onPlayerRegistered(tracker: PlayerAnalyticsTrackerI, player: Player) {
28
+ tracker.setPlayer(player);
29
+ tracker.onPlayerPresented();
30
+ }
31
+
32
+ onPlayerUnRegistered(tracker?: PlayerAnalyticsTrackerI) {
33
+ tracker?.onPlayerClosed();
34
+ }
26
35
  }
27
36
 
28
37
  export class PlayerAnalyticsManager implements PlayerLifecycleListener {
@@ -70,7 +79,7 @@ export class PlayerAnalyticsManager implements PlayerLifecycleListener {
70
79
  }
71
80
 
72
81
  const tracker = this.getTracker(player.playerId);
73
- tracker.setPlayer(player);
82
+ this.trackerFactory.onPlayerRegistered(tracker, player);
74
83
 
75
84
  const analyticsListener = new AnalyticPlayerListener({
76
85
  analyticsTracker: tracker,
@@ -84,6 +93,7 @@ export class PlayerAnalyticsManager implements PlayerLifecycleListener {
84
93
  };
85
94
 
86
95
  onUnRegistered = (player: Player) => {
96
+ this.trackerFactory.onPlayerUnRegistered(this.trackers[player.playerId]);
87
97
  player.removeListener(AnalyticPlayerListener.analyticsListenerID);
88
98
  delete this.trackers[player.playerId];
89
99
  };
@@ -388,7 +388,7 @@ export function AnalyticsProvider(props: ComponentWithChildrenProps) {
388
388
 
389
389
  ```ts
390
390
  export function useAnalytics(props: any): any {
391
- const appData = useAppData();
391
+ const { appData } = usePickFromState(["appData"]);
392
392
  const getAnalyticsFunctions = React.useContext(AnalyticsContext);
393
393
 
394
394
  const analyticsFunctions = React.useMemo(
@@ -74,4 +74,39 @@ describe("Analytics Mapper", () => {
74
74
  const rules = fixtures.ACPEventRules;
75
75
  await setConfigRunMapTest(rules, fixtures.ACPEventEvents);
76
76
  });
77
+
78
+ it("Does not let an ignore rule short-circuit later events", async () => {
79
+ await setConfigRunMapTest(
80
+ fixtures.ignoreOrderingRules,
81
+ fixtures.ignoreOrderingEvents
82
+ );
83
+ });
84
+
85
+ it("Keeps the original name when rename is empty", async () => {
86
+ await setConfigRunMapTest(
87
+ fixtures.emptyRenameRules,
88
+ fixtures.emptyRenameEvents
89
+ );
90
+ });
91
+
92
+ it("Renames via a regex with no named groups", async () => {
93
+ await setConfigRunMapTest(
94
+ fixtures.regexNonNamedFullMatchRules,
95
+ fixtures.regexNonNamedFullMatchEvents
96
+ );
97
+ });
98
+
99
+ it("Substitutes every named group in a regex rename", async () => {
100
+ await setConfigRunMapTest(
101
+ fixtures.regexMultiGroupRenameRules,
102
+ fixtures.regexMultiGroupRenameEvents
103
+ );
104
+ });
105
+
106
+ it("Does not fall back to event match when a regex rule does not match", async () => {
107
+ await setConfigRunMapTest(
108
+ fixtures.regexEventNoFallbackRules,
109
+ fixtures.regexEventNoFallbackEvents
110
+ );
111
+ });
77
112
  });
@@ -23,7 +23,7 @@
23
23
  "second": {
24
24
  "name": "EventName",
25
25
  "params": {
26
- "acp": 1
26
+ "acp": "1"
27
27
  }
28
28
  }
29
29
  }
@@ -12,4 +12,4 @@
12
12
  }
13
13
  ],
14
14
  "strategy": "allowUnlisted"
15
- }
15
+ }
@@ -0,0 +1,4 @@
1
+ [
2
+ { "first": { "name": "keep_me", "params": {} },
3
+ "second": { "name": "keep_me", "params": {} } }
4
+ ]
@@ -0,0 +1,6 @@
1
+ {
2
+ "rules": [
3
+ { "event": "keep_me", "ignore": false, "rename": "", "strategy": "allowUnlisted" }
4
+ ],
5
+ "strategy": "allowUnlisted"
6
+ }
@@ -2,17 +2,11 @@
2
2
  {
3
3
  "first": {
4
4
  "name": "Screen viewed: Ads & redirects",
5
- "params": {
6
- "ignored_param": "removed_value",
7
- "kept_param": "kept_value"
8
- }
5
+ "params": {}
9
6
  },
10
7
  "second": {
11
8
  "name": "Screen Ads & redirects",
12
- "params": {
13
- "ignored_param": "removed_value",
14
- "kept_param": "kept_value"
15
- }
9
+ "params": {}
16
10
  }
17
11
  }
18
- ]
12
+ ]
@@ -0,0 +1,18 @@
1
+ [
2
+ {
3
+ "first": {
4
+ "name": "screen_view",
5
+ "params": {}
6
+ },
7
+ "second": {
8
+ "name": "page_view",
9
+ "params": {}
10
+ }
11
+ },
12
+ {
13
+ "first": {
14
+ "name": "player_presented",
15
+ "params": {}
16
+ }
17
+ }
18
+ ]
@@ -0,0 +1,16 @@
1
+ {
2
+ "rules": [
3
+ {
4
+ "event": "player_presented",
5
+ "ignore": true,
6
+ "strategy": "allowUnlisted"
7
+ },
8
+ {
9
+ "event": "screen_view",
10
+ "ignore": false,
11
+ "rename": "page_view",
12
+ "strategy": "allowUnlisted"
13
+ }
14
+ ],
15
+ "strategy": "allowUnlisted"
16
+ }
@@ -0,0 +1,4 @@
1
+ [
2
+ { "first": { "name": "my_event", "params": {} },
3
+ "second": { "name": "my_event", "params": {} } }
4
+ ]
@@ -0,0 +1,6 @@
1
+ {
2
+ "rules": [
3
+ { "event": "my_event", "ignore": false, "rename": "should_not_apply", "regex": "wont_match_.*", "strategy": "allowUnlisted" }
4
+ ],
5
+ "strategy": "allowUnlisted"
6
+ }
@@ -0,0 +1,4 @@
1
+ [
2
+ { "first": { "name": "play-video", "params": {} },
3
+ "second": { "name": "play_video", "params": {} } }
4
+ ]
@@ -0,0 +1,6 @@
1
+ {
2
+ "rules": [
3
+ { "ignore": false, "rename": "$<first>_$<second>", "regex": "(?<first>[a-z]+)-(?<second>[a-z]+)", "strategy": "allowUnlisted" }
4
+ ],
5
+ "strategy": "allowUnlisted"
6
+ }
@@ -0,0 +1,4 @@
1
+ [
2
+ { "first": { "name": "screen_view", "params": {} },
3
+ "second": { "name": "renamed", "params": {} } }
4
+ ]
@@ -0,0 +1,6 @@
1
+ {
2
+ "rules": [
3
+ { "ignore": false, "rename": "renamed", "regex": "screen_view", "strategy": "allowUnlisted" }
4
+ ],
5
+ "strategy": "allowUnlisted"
6
+ }
@@ -14,6 +14,16 @@ import blockUnlistedEventEvents from "./analytics_mapper_testBlockUnlistedEvents
14
14
  import blockUnlistedEventRules from "./analytics_mapper_testBlockUnlistedEvents_rules.json";
15
15
  import ACPEventEvents from "./analytics_mapper_testACP_events.json";
16
16
  import ACPEventRules from "./analytics_mapper_testACP_rules.json";
17
+ import emptyRenameRules from "./analytics_mapper_testEmptyRenameKeepsName_rules.json";
18
+ import emptyRenameEvents from "./analytics_mapper_testEmptyRenameKeepsName_events.json";
19
+ import ignoreOrderingRules from "./analytics_mapper_testIgnoreOrdering_rules.json";
20
+ import ignoreOrderingEvents from "./analytics_mapper_testIgnoreOrdering_events.json";
21
+ import regexEventNoFallbackRules from "./analytics_mapper_testRegexEventNoFallback_rules.json";
22
+ import regexEventNoFallbackEvents from "./analytics_mapper_testRegexEventNoFallback_events.json";
23
+ import regexMultiGroupRenameRules from "./analytics_mapper_testRegexMultiGroupRename_rules.json";
24
+ import regexMultiGroupRenameEvents from "./analytics_mapper_testRegexMultiGroupRename_events.json";
25
+ import regexNonNamedFullMatchRules from "./analytics_mapper_testRegexNonNamedFullMatch_rules.json";
26
+ import regexNonNamedFullMatchEvents from "./analytics_mapper_testRegexNonNamedFullMatch_events.json";
17
27
 
18
28
  export const fixtures = {
19
29
  renameRules,
@@ -32,4 +42,14 @@ export const fixtures = {
32
42
  blockUnlistedEventRules,
33
43
  ACPEventEvents,
34
44
  ACPEventRules,
45
+ emptyRenameRules,
46
+ emptyRenameEvents,
47
+ ignoreOrderingRules,
48
+ ignoreOrderingEvents,
49
+ regexEventNoFallbackRules,
50
+ regexEventNoFallbackEvents,
51
+ regexMultiGroupRenameRules,
52
+ regexMultiGroupRenameEvents,
53
+ regexNonNamedFullMatchRules,
54
+ regexNonNamedFullMatchEvents,
35
55
  };
@@ -76,7 +76,10 @@ class StateHolder {
76
76
  delete this.pluckedParams[prefix];
77
77
  }
78
78
 
79
- return this._cap?.[suffix];
79
+ // Stringify so values match the native iOS/Android mappers (which coerce
80
+ // analyticsCustomProperties values to strings) — keeps GA4 data consistent
81
+ // across web/LG/Samsung and native platforms.
82
+ return this._cap?.[suffix]?.toString() ?? null;
80
83
  }
81
84
 
82
85
  private async getFromLocalAndSession(
@@ -4,8 +4,14 @@ import { isTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
4
4
  import { postAnalyticEvent } from "./manager";
5
5
  import { isLive } from "../playerUtils";
6
6
  import { extensionsEvents } from "./AnalyticsEvents/helper";
7
- import { AD_EVENT, EVENT_TYPES, PLAYER_DISPLAY_STATES } from "./events";
7
+ import {
8
+ AD_EVENT,
9
+ EVENT_TYPES,
10
+ GENERAL_EVENT,
11
+ PLAYER_DISPLAY_STATES,
12
+ } from "./events";
8
13
  import { Player } from "../appUtils/playerManager/player";
14
+ import { isNil } from "@applicaster/zapp-react-native-utils/utils";
9
15
 
10
16
  export interface PlayerAnalyticsTrackerI {
11
17
  handleAnalyticEvent(
@@ -13,6 +19,8 @@ export interface PlayerAnalyticsTrackerI {
13
19
  eventData?: QuickBrickPlayer.EventData
14
20
  ): void;
15
21
  setPlayer(player: Player): void;
22
+ onPlayerPresented(): void;
23
+ onPlayerClosed(): void;
16
24
  }
17
25
 
18
26
  export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
@@ -26,6 +34,15 @@ export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
26
34
  this.player = player;
27
35
  }
28
36
 
37
+ onPlayerPresented() {
38
+ this.handleAnalyticEvent(GENERAL_EVENT.player_presented);
39
+ }
40
+
41
+ onPlayerClosed() {
42
+ this.handleAnalyticEvent(GENERAL_EVENT.session_end);
43
+ this.handleAnalyticEvent(GENERAL_EVENT.player_closed);
44
+ }
45
+
29
46
  isAdBreak(event: string): boolean {
30
47
  const adBreakEvents = [
31
48
  AD_EVENT.ad_break_start,
@@ -180,7 +197,8 @@ export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
180
197
  this.entry?.extensions?.free || this.entry?.extensions?.isFree;
181
198
 
182
199
  const title = this.entry?.title;
183
- const streamType = isLive(this.entry) ? "live" : "vod"; // Todo: determine other types, channel, podcast, aod
200
+ const isStreamLive = isLive(this.entry);
201
+ const streamType = isStreamLive ? "live" : "vod"; // Todo: determine other types, channel, podcast, aod
184
202
 
185
203
  const currentPosition = this.getCurrentPosition(
186
204
  event,
@@ -201,6 +219,7 @@ export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
201
219
  );
202
220
 
203
221
  const playerState = this.getPlayerState();
222
+ const playerType = this.player?.playerPluginId;
204
223
 
205
224
  const analyticsCustomProperties = extensionsEvents(this.entry?.extensions);
206
225
 
@@ -210,7 +229,11 @@ export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
210
229
  name: title,
211
230
  media_type: mediaType,
212
231
  stream_type: streamType,
213
- duration: mediaDuration,
232
+ player_type: playerType,
233
+ // We do not pass duration for live streams anymore (it's now seekableDuration),
234
+ // but we can't mark it as optional in Analytics Mapper yet,
235
+ // so we set it to -1 for live streams
236
+ duration: isStreamLive && isNil(mediaDuration) ? -1 : mediaDuration,
214
237
  current_position: currentPosition,
215
238
  player_state: playerState,
216
239
  stream_format: contentType,