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

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 (137) 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/README.md +1 -1
  5. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +188 -0
  6. package/appUtils/HooksManager/__tests__/hooksManager.test.js +2 -16
  7. package/appUtils/HooksManager/index.ts +45 -10
  8. package/appUtils/RiverFocusManager/{index.ts → index.js} +18 -25
  9. package/appUtils/accessibilityManager/const.ts +0 -4
  10. package/appUtils/accessibilityManager/utils.ts +1 -0
  11. package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +2 -7
  12. package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +0 -48
  13. package/appUtils/contextKeysManager/contextResolver.ts +22 -51
  14. package/appUtils/contextKeysManager/index.ts +10 -65
  15. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +0 -3
  16. package/appUtils/focusManager/index.ios.ts +4 -43
  17. package/appUtils/focusManagerAux/utils/index.ts +3 -5
  18. package/appUtils/focusManagerAux/utils/utils.ios.ts +3 -199
  19. package/appUtils/keyCodes/keys/keys.web.ts +4 -1
  20. package/appUtils/localizationsHelper.ts +4 -0
  21. package/appUtils/orientationHelper.ts +4 -2
  22. package/appUtils/platform/platformUtils.ts +1 -1
  23. package/appUtils/playerManager/index.ts +9 -0
  24. package/appUtils/playerManager/player.ts +1 -5
  25. package/appUtils/playerManager/playerNative.ts +22 -30
  26. package/appUtils/playerManager/usePlayer.tsx +5 -3
  27. package/appUtils/playerManager/usePlayerState.tsx +2 -14
  28. package/cellUtils/__tests__/cellUtils.test.ts +39 -0
  29. package/cellUtils/index.ts +11 -1
  30. package/cloudEventsUtils/index.ts +1 -65
  31. package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
  32. package/dateUtils/__tests__/dayjs.test.ts +0 -3
  33. package/dateUtils/index.ts +2 -0
  34. package/focusManager/aux/index.ts +1 -1
  35. package/headersUtils/__tests__/headersUtils.test.js +1 -11
  36. package/headersUtils/index.ts +1 -2
  37. package/manifestUtils/defaultManifestConfigurations/player.js +7 -75
  38. package/manifestUtils/tvAction/container/index.js +1 -1
  39. package/numberUtils/__tests__/toNumber.test.ts +0 -12
  40. package/numberUtils/index.ts +1 -19
  41. package/package.json +4 -4
  42. package/pluginUtils/index.ts +4 -9
  43. package/reactHooks/advertising/index.ts +2 -2
  44. package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
  45. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  46. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +2 -1
  47. package/reactHooks/casting/index.ts +1 -0
  48. package/reactHooks/casting/useIsCasting.tsx +57 -0
  49. package/reactHooks/cell-click/__tests__/index.test.js +3 -1
  50. package/reactHooks/componentsMap/index.ts +55 -0
  51. package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
  52. package/reactHooks/connection/__tests__/index.test.js +1 -1
  53. package/reactHooks/debugging/__tests__/index.test.js +4 -4
  54. package/reactHooks/device/useIsTablet.tsx +19 -14
  55. package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
  56. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +23 -32
  57. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
  58. package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +1 -4
  59. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +30 -42
  60. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +1 -1
  61. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +1 -1
  62. package/reactHooks/feed/useBatchLoading.ts +1 -7
  63. package/reactHooks/feed/useEntryScreenId.ts +2 -2
  64. package/reactHooks/feed/useInflatedUrl.ts +1 -1
  65. package/reactHooks/feed/usePipesCacheReset.ts +1 -3
  66. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -13
  67. package/reactHooks/layout/__tests__/index.test.tsx +1 -1
  68. package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
  69. package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.tsx → useDimensions.test.ts} +25 -105
  70. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
  71. package/reactHooks/navigation/__tests__/index.test.tsx +4 -2
  72. package/reactHooks/navigation/index.ts +6 -7
  73. package/reactHooks/navigation/useRoute.ts +6 -8
  74. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +10 -27
  75. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
  76. package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
  77. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
  78. package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
  79. package/reactHooks/resolvers/useCellResolver.ts +2 -6
  80. package/reactHooks/resolvers/useComponentResolver.ts +13 -9
  81. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
  82. package/reactHooks/screen/__tests__/useCurrentScreenIsHook.test.ts +103 -0
  83. package/reactHooks/screen/__tests__/useCurrentScreenIsStartupHook.test.ts +94 -0
  84. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
  85. package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
  86. package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +4 -12
  87. package/reactHooks/screen/index.ts +6 -0
  88. package/reactHooks/screen/useCurrentScreenIsHook.ts +9 -0
  89. package/reactHooks/screen/useCurrentScreenIsStartupHook.ts +8 -0
  90. package/reactHooks/screen/useIsStandaloneFullscreen.ts +12 -0
  91. package/reactHooks/screen/useTargetScreenData.ts +2 -4
  92. package/reactHooks/state/__tests__/useComponentScreenState.test.ts +246 -0
  93. package/reactHooks/state/index.ts +2 -0
  94. package/reactHooks/state/useComponentScreenState.ts +45 -0
  95. package/reactHooks/state/useRivers.ts +1 -1
  96. package/reactHooks/usePluginConfiguration.ts +2 -2
  97. package/reactHooks/utils/__tests__/index.test.js +1 -1
  98. package/rectUtils/index.ts +2 -2
  99. package/screenPickerUtils/index.ts +5 -0
  100. package/screenState/__tests__/index.test.ts +1 -1
  101. package/stringUtils/index.ts +1 -1
  102. package/testUtils/index.tsx +21 -30
  103. package/typeGuards/index.ts +0 -4
  104. package/utils/__tests__/clone.test.ts +158 -0
  105. package/utils/clone.ts +7 -0
  106. package/utils/index.ts +1 -13
  107. package/utils/path.ts +3 -6
  108. package/utils/pathOr.ts +1 -5
  109. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +1 -1
  110. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
  111. package/zappFrameworkUtils/loginPluginUtils.ts +1 -1
  112. package/adsUtils/__tests__/createVMAP.test.ts +0 -419
  113. package/appDataUtils/__tests__/urlScheme.test.ts +0 -678
  114. package/appUtils/focusManagerAux/utils/index.ios.ts +0 -122
  115. package/cloudEventsUtils/__tests__/index.test.ts +0 -529
  116. package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +0 -207
  117. package/errorUtils/__tests__/GeneralError.test.ts +0 -97
  118. package/errorUtils/__tests__/HttpStatusCode.test.ts +0 -344
  119. package/errorUtils/__tests__/MissingPluginError.test.ts +0 -113
  120. package/errorUtils/__tests__/NetworkError.test.ts +0 -202
  121. package/errorUtils/__tests__/getParsedResponse.test.ts +0 -188
  122. package/errorUtils/__tests__/invariant.test.ts +0 -112
  123. package/numberUtils/__tests__/toPositiveNumber.test.ts +0 -165
  124. package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +0 -537
  125. package/reactHooks/dev/__tests__/useReRenderLog.test.ts +0 -188
  126. package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +0 -580
  127. package/rectUtils/__tests__/index.test.ts +0 -549
  128. package/screenPickerUtils/__tests__/index.test.ts +0 -333
  129. package/searchUtils/const.ts +0 -7
  130. package/searchUtils/index.ts +0 -3
  131. package/time/__tests__/BackgroundTimer.test.ts +0 -156
  132. package/time/__tests__/Timer.test.ts +0 -236
  133. package/typeGuards/__tests__/isString.test.ts +0 -21
  134. package/utils/__tests__/mergeRight.test.ts +0 -48
  135. package/utils/__tests__/selectors.test.ts +0 -124
  136. package/utils/mergeRight.ts +0 -5
  137. 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}">
@@ -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(
@@ -1,5 +1,193 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`HooksManager when target route is playable executes the player hook 1`] = `
4
+ [
5
+ {
6
+ "callback": [Function],
7
+ "hookPlugin": Hook {
8
+ "configuration": {
9
+ "config_field": "bar",
10
+ },
11
+ "identifier": "headless_player_hook",
12
+ "lastHook": true,
13
+ "manager": {
14
+ "executeHook": [Function],
15
+ "handleHooks": [Function],
16
+ "presentScreenHook": [Function],
17
+ "runInBackground": [Function],
18
+ "subscriber": {
19
+ "handlers": {
20
+ "complete": [
21
+ [MockFunction] {
22
+ "calls": [
23
+ [
24
+ {
25
+ "callback": [Function],
26
+ "hookPlugin": [Circular],
27
+ "payload": {
28
+ "foo": "bar",
29
+ },
30
+ },
31
+ [Function],
32
+ ],
33
+ ],
34
+ "results": [
35
+ {
36
+ "type": "return",
37
+ "value": undefined,
38
+ },
39
+ ],
40
+ },
41
+ ],
42
+ "presentScreenHook": [
43
+ [MockFunction],
44
+ ],
45
+ "success": [
46
+ [MockFunction] {
47
+ "calls": [
48
+ [Circular],
49
+ ],
50
+ "results": [
51
+ {
52
+ "type": "return",
53
+ "value": undefined,
54
+ },
55
+ ],
56
+ },
57
+ ],
58
+ },
59
+ "invokeHandler": [Function],
60
+ "on": [Function],
61
+ "removeHandler": [Function],
62
+ },
63
+ },
64
+ "module": {
65
+ "hasPlayerHook": true,
66
+ "run": [MockFunction] {
67
+ "calls": [
68
+ [
69
+ {
70
+ "foo": "bar",
71
+ },
72
+ [Function],
73
+ {
74
+ "config_field": "bar",
75
+ },
76
+ ],
77
+ ],
78
+ "results": [
79
+ {
80
+ "type": "return",
81
+ "value": undefined,
82
+ },
83
+ ],
84
+ },
85
+ },
86
+ "state": 7,
87
+ "weight": 1,
88
+ },
89
+ "payload": {
90
+ "foo": "bar",
91
+ },
92
+ },
93
+ [Function],
94
+ ]
95
+ `;
96
+
97
+ exports[`HooksManager when target route is playable executes the player hook 2`] = `
98
+ [
99
+ {
100
+ "callback": [Function],
101
+ "hookPlugin": Hook {
102
+ "configuration": {
103
+ "config_field": "bar",
104
+ },
105
+ "identifier": "headless_player_hook",
106
+ "lastHook": true,
107
+ "manager": {
108
+ "executeHook": [Function],
109
+ "handleHooks": [Function],
110
+ "presentScreenHook": [Function],
111
+ "runInBackground": [Function],
112
+ "subscriber": {
113
+ "handlers": {
114
+ "complete": [
115
+ [MockFunction] {
116
+ "calls": [
117
+ [Circular],
118
+ ],
119
+ "results": [
120
+ {
121
+ "type": "return",
122
+ "value": undefined,
123
+ },
124
+ ],
125
+ },
126
+ ],
127
+ "presentScreenHook": [
128
+ [MockFunction],
129
+ ],
130
+ "success": [
131
+ [MockFunction] {
132
+ "calls": [
133
+ [
134
+ {
135
+ "callback": [Function],
136
+ "hookPlugin": [Circular],
137
+ "payload": {
138
+ "foo": "bar",
139
+ },
140
+ },
141
+ [Function],
142
+ ],
143
+ ],
144
+ "results": [
145
+ {
146
+ "type": "return",
147
+ "value": undefined,
148
+ },
149
+ ],
150
+ },
151
+ ],
152
+ },
153
+ "invokeHandler": [Function],
154
+ "on": [Function],
155
+ "removeHandler": [Function],
156
+ },
157
+ },
158
+ "module": {
159
+ "hasPlayerHook": true,
160
+ "run": [MockFunction] {
161
+ "calls": [
162
+ [
163
+ {
164
+ "foo": "bar",
165
+ },
166
+ [Function],
167
+ {
168
+ "config_field": "bar",
169
+ },
170
+ ],
171
+ ],
172
+ "results": [
173
+ {
174
+ "type": "return",
175
+ "value": undefined,
176
+ },
177
+ ],
178
+ },
179
+ },
180
+ "state": 7,
181
+ "weight": 1,
182
+ },
183
+ "payload": {
184
+ "foo": "bar",
185
+ },
186
+ },
187
+ [Function],
188
+ ]
189
+ `;
190
+
3
191
  exports[`HooksManager when there are preload hooks hook with screen: executes 1`] = `
4
192
  [
5
193
  {
@@ -116,23 +116,9 @@ describe("HooksManager", () => {
116
116
  headlessPlayerHook.configuration
117
117
  );
118
118
 
119
- expect(successHandler).toHaveBeenCalledTimes(1);
120
- expect(successHandler.mock.calls[0][0].payload).toEqual(payload);
121
-
122
- expect(successHandler.mock.calls[0][0].hookPlugin.identifier).toBe(
123
- "headless_player_hook"
124
- );
125
-
126
- expect(typeof successHandler.mock.calls[0][1]).toBe("function");
127
-
128
- expect(completeHandler).toHaveBeenCalledTimes(1);
129
- expect(completeHandler.mock.calls[0][0].payload).toEqual(payload);
130
-
131
- expect(completeHandler.mock.calls[0][0].hookPlugin.identifier).toBe(
132
- "headless_player_hook"
133
- );
119
+ expect(successHandler.mock.calls[0]).toMatchSnapshot();
134
120
 
135
- expect(typeof completeHandler.mock.calls[0][1]).toBe("function");
121
+ expect(completeHandler.mock.calls[0]).toMatchSnapshot();
136
122
  });
137
123
  });
138
124
 
@@ -11,6 +11,10 @@ import { HOOKS_EVENTS, HOOKS_TYPE } from "./constants";
11
11
 
12
12
  import { hooksManagerLogger } from "./logger";
13
13
  import { HookManager, HookManagerArgs } from "./types";
14
+ import {
15
+ actionExecutor,
16
+ ActionResult,
17
+ } from "../../actionsExecutor/ActionExecutor";
14
18
 
15
19
  /**
16
20
  * orders the hooks according to their weight
@@ -230,7 +234,7 @@ export function HooksManager({
230
234
  function completeHook(hookPlugin, payload, callback) {
231
235
  logHookEvent(
232
236
  hooksManagerLogger.info,
233
- `completeHook: hook sequence completed successfully: ${hookPlugin.identifier}`,
237
+ `completeHook: hook sequence completed successfully: ${hookPlugin["identifier"]}`,
234
238
  {
235
239
  payload,
236
240
  hook: hookPlugin,
@@ -276,10 +280,12 @@ export function HooksManager({
276
280
  if (hookPlugin.isCancelled()) {
277
281
  logHookEvent(
278
282
  hooksManagerLogger.info,
279
- `hookCallback: hook was cancelled: ${hookPlugin.identifier}`,
283
+ `hookCallback: hook was cancelled: ${hookPlugin["identifier"]}`,
280
284
  {}
281
285
  );
282
286
 
287
+ actionExecutor.unregisterAction("finishHook");
288
+
283
289
  return;
284
290
  }
285
291
 
@@ -294,6 +300,8 @@ export function HooksManager({
294
300
  }
295
301
  );
296
302
 
303
+ actionExecutor.unregisterAction("finishHook");
304
+
297
305
  return hookPlugin.setStateAndNotify(HOOKS_EVENTS.ERROR, {
298
306
  error,
299
307
  hookPlugin,
@@ -305,7 +313,7 @@ export function HooksManager({
305
313
  if (!success) {
306
314
  logHookEvent(
307
315
  hooksManagerLogger.info,
308
- `hookCallback: hook was cancelled: ${hookPlugin.identifier}`,
316
+ `hookCallback: hook was cancelled: ${hookPlugin["identifier"]}`,
309
317
  {
310
318
  payload,
311
319
  hook: hookPlugin,
@@ -316,6 +324,8 @@ export function HooksManager({
316
324
  // TODO: Temporary hack to pass getLoginProtocol to other plugins to refresh in case token expired, need be deleted later
317
325
  delete payload.getLoginProtocol;
318
326
 
327
+ actionExecutor.unregisterAction("finishHook");
328
+
319
329
  hookPlugin.setStateAndNotify(HOOKS_EVENTS.CANCEL, {
320
330
  hookPlugin,
321
331
  payload,
@@ -334,7 +344,7 @@ export function HooksManager({
334
344
  if (isHookInHomescreen && isHookFlowBlocker && cancelled) {
335
345
  logHookEvent(
336
346
  hooksManagerLogger.info,
337
- `hookCallback: send app to background, cancelled flow blocker hook ${hookPlugin.identifier} on home screen`,
347
+ `hookCallback: send app to background, cancelled flow blocker hook ${hookPlugin["identifier"]} on home screen`,
338
348
  {
339
349
  payload,
340
350
  hook: hookPlugin,
@@ -349,7 +359,7 @@ export function HooksManager({
349
359
  } else {
350
360
  logHookEvent(
351
361
  hooksManagerLogger.info,
352
- `hookCallback: hook successfully finished: ${hookPlugin.identifier}`,
362
+ `hookCallback: hook successfully finished: ${hookPlugin["identifier"]}`,
353
363
  {
354
364
  payload,
355
365
  hook: hookPlugin,
@@ -359,7 +369,7 @@ export function HooksManager({
359
369
  if (!callback) {
360
370
  logHookEvent(
361
371
  hooksManagerLogger.warn,
362
- `hookCallback: ${hookPlugin.identifier} is missing \`callback\`, using hookCallback(default one)`,
372
+ `hookCallback: ${hookPlugin["identifier"]} is missing \`callback\`, using hookCallback(default one)`,
363
373
  {
364
374
  hookPlugin,
365
375
  }
@@ -381,6 +391,27 @@ export function HooksManager({
381
391
  };
382
392
  }
383
393
 
394
+ function registerFinishHookAction(payload, callback) {
395
+ // Ensure no stale finishHook remains (e.g. presentUI re-entry from runInBackground)
396
+ actionExecutor.unregisterAction("finishHook");
397
+
398
+ actionExecutor.registerAction("finishHook", async (action: ActionType) => {
399
+ const { success, errorMessage, abort } = action.options;
400
+
401
+ actionExecutor.unregisterAction("finishHook");
402
+
403
+ if (errorMessage) {
404
+ callback({ success, error: new Error(errorMessage), payload, abort });
405
+ } else {
406
+ callback({ success, error: null, payload, abort });
407
+ }
408
+
409
+ hooksManagerLogger.info("finishHook action executed, finishing flow");
410
+
411
+ return ActionResult.Success;
412
+ });
413
+ }
414
+
384
415
  /**
385
416
  * presents a screen hook by triggering an event invoking the handler with
386
417
  * the appropriate route & payload
@@ -401,13 +432,15 @@ export function HooksManager({
401
432
 
402
433
  logHookEvent(
403
434
  hooksManagerLogger.info,
404
- `presentScreenHook: Presenting screen hook: ${hookPlugin.identifier}`,
435
+ `presentScreenHook: Presenting screen hook: ${hookPlugin["identifier"]}`,
405
436
  {
406
437
  hook: hookPlugin,
407
438
  payload,
408
439
  }
409
440
  );
410
441
 
442
+ registerFinishHookAction(payload, callback);
443
+
411
444
  hookPlugin.setStateAndNotify(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, {
412
445
  hookPlugin,
413
446
  route: targetScreenRoute,
@@ -421,7 +454,7 @@ export function HooksManager({
421
454
  hooksManager.executeHook = function (hookPlugin, payload, callback) {
422
455
  logHookEvent(
423
456
  hooksManagerLogger.info,
424
- `executeHook: ${hookPlugin.identifier}`,
457
+ `executeHook: ${hookPlugin["identifier"]}`,
425
458
  {
426
459
  hook: hookPlugin,
427
460
  payload,
@@ -433,7 +466,7 @@ export function HooksManager({
433
466
  } catch (error) {
434
467
  logHookEvent(
435
468
  hooksManagerLogger.error,
436
- `executeHook: error executing hook: ${hookPlugin.identifier} error: ${error.message}`,
469
+ `executeHook: error executing hook: ${hookPlugin["identifier"]} error: ${error.message}`,
437
470
  {
438
471
  hook: hookPlugin,
439
472
  payload,
@@ -460,13 +493,15 @@ export function HooksManager({
460
493
  try {
461
494
  logHookEvent(
462
495
  hooksManagerLogger.info,
463
- `runInBackground: Executing hook: ${hookPlugin.identifier}`,
496
+ `runInBackground: Executing hook: ${hookPlugin["identifier"]}`,
464
497
  {
465
498
  hook: hookPlugin,
466
499
  payload,
467
500
  }
468
501
  );
469
502
 
503
+ registerFinishHookAction(payload, callback);
504
+
470
505
  hookPlugin.module.runInBackground(
471
506
  payload,
472
507
  callback,
@@ -1,31 +1,11 @@
1
- import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
2
- import { QUICK_BRICK_CONTENT } from "@applicaster/quick-brick-core/const";
3
- import { isNil, isEmpty } from "@applicaster/zapp-react-native-utils/utils";
4
- import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
1
+ import * as R from "ramda";
2
+
3
+ import { focusManager } from "../focusManager";
5
4
 
6
5
  let riverFocusData = {};
7
6
  let initialyPresentedScreenFocused = false;
8
7
 
9
8
  export const riverFocusManager = (function () {
10
- /**
11
- * Create unique key that will be used for save focused group data inside specific screen
12
- * @param {{ screenId: string, isInsideContainer: boolean }}
13
- * screenId Unique Id of the screen from layout.json
14
- * isInsideContainer If this screen a screen picker child
15
- *
16
- */
17
- function screenFocusableGroupId({
18
- screenId,
19
- isInsideContainer,
20
- }: {
21
- screenId: string;
22
- isInsideContainer: Option<boolean>;
23
- }) {
24
- return `${QUICK_BRICK_CONTENT}-${screenId}${
25
- isNil(isInsideContainer) ? "" : "-isInsideContainer"
26
- }`;
27
- }
28
-
29
9
  function setScreenFocusableData({
30
10
  screenFocusableGroupId,
31
11
  groupId,
@@ -98,8 +78,8 @@ export const riverFocusManager = (function () {
98
78
  }) {
99
79
  // Check if screen should be focused
100
80
  const shouldFocus =
101
- (initialyPresentedScreenFocused === false && isEmpty(riverFocusData)) ||
102
- isNotNil(riverFocusData[screenFocusableGroupId]) ||
81
+ (initialyPresentedScreenFocused === false && R.isEmpty(riverFocusData)) ||
82
+ R.compose(R.not, R.isNil)(riverFocusData[screenFocusableGroupId]) ||
103
83
  isDeepLink;
104
84
 
105
85
  // TODO: Uncommit it to start fixing bug where selection wrong item
@@ -138,6 +118,19 @@ export const riverFocusManager = (function () {
138
118
  }
139
119
  }
140
120
 
121
+ /**
122
+ * Create unique key that will be used for save focused group data inside specific screen
123
+ * @param {{ screenId: string, isInsideContainer: boolean }}
124
+ * screenId Unique Id of the screen from layout.json
125
+ * isInsideContainer If this screen a screen picker child
126
+ *
127
+ */
128
+ function screenFocusableGroupId({ screenId, isInsideContainer }) {
129
+ return `RiverFocusableGroup-${screenId}${
130
+ R.isNil(isInsideContainer) ? "" : "-isInsideContainer"
131
+ }`;
132
+ }
133
+
141
134
  return {
142
135
  setScreenFocusableData,
143
136
  clearAllScreensData,
@@ -31,10 +31,6 @@ export const BUTTON_ACCESSIBILITY_KEYS = {
31
31
  hint: "accessibility_close_mini_hint",
32
32
  },
33
33
  },
34
- back_to_live: {
35
- label: "back_to_live_label",
36
- hint: "",
37
- },
38
34
  maximize: {
39
35
  label: "accessibility_maximize_label",
40
36
  hint: "accessibility_maximize_hint",
@@ -35,6 +35,7 @@ export function calculateReadingTime(
35
35
  return 0;
36
36
  }
37
37
 
38
+ // Count words (split on whitespace and punctuation, keep alnum boundaries)
38
39
  const words = trimmed
39
40
  .split(/(?<=\d)(?=[a-zA-Z])|(?<=[a-zA-Z])(?=\d)|[^\w\s]+|\s+/)
40
41
  .filter(Boolean).length;
@@ -1,12 +1,7 @@
1
- import { NativeModules } from "react-native";
2
1
  import { ContextKeysManager } from "../..";
3
2
 
4
3
  describe("Context Keys Manager - getKeys", () => {
5
- beforeEach(() => {
6
- delete NativeModules.ContextResolverBridge;
7
- });
8
-
9
- it("returns null if all keys are invalid 1", async () => {
4
+ it("returns null if all keys are invalid", async () => {
10
5
  // setup
11
6
  const keys = [null];
12
7
 
@@ -29,7 +24,7 @@ describe("Context Keys Manager - getKeys", () => {
29
24
  expect(getKey.mock.calls).toEqual([[null]]);
30
25
  });
31
26
 
32
- it("returns null if all keys are invalid 2", async () => {
27
+ it("returns null if all keys are invalid", async () => {
33
28
  // setup
34
29
  const keys = [null, undefined];
35
30