@applicaster/zapp-react-native-utils 14.0.0-alpha.4104749434 → 14.0.0-alpha.4138342511

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 (43) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +60 -84
  2. package/actionsExecutor/ScreenActions.ts +164 -0
  3. package/actionsExecutor/StorageActions.ts +110 -0
  4. package/actionsExecutor/feedDecorator.ts +171 -0
  5. package/actionsExecutor/screenResolver.ts +11 -0
  6. package/analyticsUtils/AnalyticsEvents/helper.ts +1 -1
  7. package/analyticsUtils/__tests__/analyticsUtils.test.js +0 -11
  8. package/appUtils/contextKeysManager/contextResolver.ts +42 -1
  9. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -0
  10. package/appUtils/focusManager/index.ios.ts +10 -0
  11. package/appUtils/focusManager/index.ts +11 -0
  12. package/appUtils/focusManager/treeDataStructure/Tree/index.js +1 -1
  13. package/appUtils/focusManagerAux/utils/index.ts +18 -0
  14. package/configurationUtils/__tests__/manifestKeyParser.test.ts +0 -1
  15. package/navigationUtils/__tests__/mapContentTypesToRivers.test.ts +130 -0
  16. package/navigationUtils/index.ts +6 -4
  17. package/package.json +2 -3
  18. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +3 -1
  19. package/reactHooks/cell-click/__tests__/index.test.js +3 -0
  20. package/reactHooks/cell-click/index.ts +8 -1
  21. package/reactHooks/debugging/__tests__/index.test.js +0 -1
  22. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
  23. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +36 -15
  24. package/reactHooks/feed/index.ts +2 -0
  25. package/reactHooks/feed/useBatchLoading.ts +15 -8
  26. package/reactHooks/feed/useFeedLoader.tsx +36 -34
  27. package/reactHooks/feed/useLoadPipesDataDispatch.ts +57 -0
  28. package/reactHooks/feed/usePipesCacheReset.ts +2 -2
  29. package/reactHooks/flatList/useSequentialRenderItem.tsx +3 -3
  30. package/reactHooks/layout/__tests__/index.test.tsx +3 -1
  31. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +34 -36
  32. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -3
  33. package/reactHooks/layout/useLayoutVersion.ts +5 -5
  34. package/reactHooks/navigation/useRoute.ts +7 -2
  35. package/reactHooks/navigation/useScreenStateStore.ts +8 -0
  36. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +4 -0
  37. package/reactHooks/state/useRivers.ts +7 -8
  38. package/storage/ScreenSingleValueProvider.ts +204 -0
  39. package/storage/ScreenStateMultiSelectProvider.ts +293 -0
  40. package/storage/StorageMultiSelectProvider.ts +192 -0
  41. package/storage/StorageSingleSelectProvider.ts +108 -0
  42. package/time/BackgroundTimer.ts +1 -1
  43. package/utils/index.ts +2 -0
@@ -1,10 +1,13 @@
1
1
  import { ContextKeysManager } from "./index";
2
2
  import * as R from "ramda";
3
+ import * as _ from "lodash";
4
+ import { useScreenStateStore } from "../../reactHooks/navigation/useScreenStateStore";
3
5
 
4
- interface IResolver {
6
+ export interface IResolver {
5
7
  resolve: (string) => Promise<string | number | object>;
6
8
  }
7
9
 
10
+ // TODO: Rename to ObjectKeyResolver or similar
8
11
  export class EntryResolver implements IResolver {
9
12
  entry: ZappEntry;
10
13
 
@@ -21,6 +24,28 @@ export class EntryResolver implements IResolver {
21
24
  }
22
25
  }
23
26
 
27
+ // TODO: Move to proper place
28
+
29
+ export class ScreenStateResolver implements IResolver {
30
+ constructor(
31
+ private screenStateStore: ReturnType<typeof useScreenStateStore>
32
+ ) {}
33
+
34
+ async resolve(key: string) {
35
+ const screenState = this.screenStateStore.getState().data;
36
+
37
+ if (!key || key.length === 0) {
38
+ return screenState;
39
+ }
40
+
41
+ if (key.includes(".")) {
42
+ return R.view(R.lensPath(key.split(".")), screenState);
43
+ }
44
+
45
+ return screenState?.[key];
46
+ }
47
+ }
48
+
24
49
  export class ContextResolver implements IResolver {
25
50
  resolve = async (compositeKey: string) =>
26
51
  ContextKeysManager.instance.getKey(compositeKey);
@@ -64,3 +89,19 @@ export const resolveObjectValues = async (
64
89
 
65
90
  return Object.fromEntries(resolvedEntries);
66
91
  };
92
+
93
+ export const extractAtValues = _.memoize((input: any): string[] => {
94
+ return _.flatMapDeep(input, (value: any) => {
95
+ if (_.isString(value)) {
96
+ const matches = value.match(/@\{([^}]*)\}/g);
97
+
98
+ return matches ? matches.map((match) => match.slice(2, -1)) : [];
99
+ }
100
+
101
+ if (_.isObject(value)) {
102
+ return extractAtValues(_.values(value));
103
+ }
104
+
105
+ return [];
106
+ });
107
+ });
@@ -24,6 +24,7 @@ exports[`focusManager should be defined 1`] = `
24
24
  "invokeHandler": [Function],
25
25
  "isCurrentFocusOnTheTopScreen": [Function],
26
26
  "isFocusDisabled": [Function],
27
+ "isFocusOn": [Function],
27
28
  "isGroupItemFocused": [Function],
28
29
  "longPress": [Function],
29
30
  "moveFocus": [Function],
@@ -63,6 +64,7 @@ exports[`focusManagerIOS should be defined 1`] = `
63
64
  "getGroupRootById": [Function],
64
65
  "getPreferredFocusChild": [Function],
65
66
  "invokeHandler": [Function],
67
+ "isFocusOn": [Function],
66
68
  "isGroupItemFocused": [Function],
67
69
  "moveFocus": [Function],
68
70
  "on": [Function],
@@ -1,6 +1,7 @@
1
1
  import { NativeModules } from "react-native";
2
2
  import * as R from "ramda";
3
3
 
4
+ import { isCurrentFocusOn } from "../focusManagerAux/utils";
4
5
  import { Tree } from "./treeDataStructure/Tree";
5
6
  import { findFocusableNode } from "./treeDataStructure/Utils";
6
7
  import { subscriber } from "../../functionUtils";
@@ -391,6 +392,14 @@ export const focusManager = (function () {
391
392
  return node;
392
393
  }
393
394
 
395
+ function isFocusOn(id): boolean {
396
+ const currentFocusNode = focusableTree.findInTree(
397
+ getCurrentFocus()?.props?.id
398
+ );
399
+
400
+ return id && isCurrentFocusOn(id, currentFocusNode);
401
+ }
402
+
394
403
  return {
395
404
  on,
396
405
  invokeHandler,
@@ -412,5 +421,6 @@ export const focusManager = (function () {
412
421
  getGroupRootById,
413
422
  isGroupItemFocused,
414
423
  getPreferredFocusChild,
424
+ isFocusOn,
415
425
  };
416
426
  })();
@@ -14,6 +14,8 @@ import { subscriber } from "../../functionUtils";
14
14
  import { coreLogger } from "../../logger";
15
15
  import { ACTION } from "./utils/enums";
16
16
 
17
+ import { isCurrentFocusOn } from "../focusManagerAux/utils";
18
+
17
19
  const logger = coreLogger.addSubsystem("focusManager");
18
20
 
19
21
  const isFocusEnabled = (focusableItem): boolean => {
@@ -546,6 +548,14 @@ export const focusManager = (function () {
546
548
  return preferredFocus[0];
547
549
  }
548
550
 
551
+ function isFocusOn(id): boolean {
552
+ return (
553
+ id &&
554
+ isCurrentFocusOnTheTopScreen() &&
555
+ isCurrentFocusOn(id, currentFocusNode)
556
+ );
557
+ }
558
+
549
559
  /**
550
560
  * this is the list of the functions available externally
551
561
  * when importing the focus manager
@@ -576,5 +586,6 @@ export const focusManager = (function () {
576
586
  recoverFocus,
577
587
  isCurrentFocusOnTheTopScreen,
578
588
  findPreferredFocusChild,
589
+ isFocusOn,
579
590
  };
580
591
  })();
@@ -142,7 +142,7 @@ export class Tree {
142
142
  this.hasGroupID(node)
143
143
  ? "Make sure that there are no id duplicates inside the " +
144
144
  existingNode.parent.id +
145
- " group."
145
+ " group. This can as well happen when the component is re-mounted"
146
146
  : ""
147
147
  }`,
148
148
  });
@@ -9,6 +9,8 @@ import {
9
9
  // run this check too often could lead to performance penalty on low-end devices
10
10
  const HOW_OFTEN_TO_CHECK_CONDITION = 300; // ms
11
11
 
12
+ const isRoot = (node) => node?.id === "root";
13
+
12
14
  type Props = {
13
15
  maxTimeout: number;
14
16
  conditionFn: () => boolean;
@@ -99,3 +101,19 @@ export const waitForContent = (focusableTree) => {
99
101
  conditionFn: contentHasAnyChildren,
100
102
  });
101
103
  };
104
+
105
+ export const isCurrentFocusOn = (id, node) => {
106
+ if (!node) {
107
+ return false;
108
+ }
109
+
110
+ if (isRoot(node)) {
111
+ return false;
112
+ }
113
+
114
+ if (node?.id === id) {
115
+ return true;
116
+ }
117
+
118
+ return isCurrentFocusOn(id, node.parent);
119
+ };
@@ -1,6 +1,5 @@
1
1
  import { getAllSpecificStyles } from "../manifestKeyParser";
2
2
 
3
- // Mock the dependencies
4
3
  jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
5
4
  platformSelect: jest.fn((platforms) => platforms.samsung_tv), // Default to samsung for tests
6
5
  }));
@@ -0,0 +1,130 @@
1
+ import { mapContentTypesToRivers } from "../index";
2
+
3
+ describe("mapContentTypesToRivers", () => {
4
+ it("should return the correct content types mapped to rivers", () => {
5
+ const state = {
6
+ rivers: {
7
+ "river-1": {
8
+ plugin_type: "river",
9
+ },
10
+ },
11
+ contentTypes: {
12
+ "content-type-1": {
13
+ screen_id: "river-1",
14
+ },
15
+ },
16
+ };
17
+
18
+ const result = mapContentTypesToRivers(state);
19
+
20
+ expect(result).toEqual({
21
+ "content-type-1": {
22
+ screenType: "river",
23
+ screen_id: "river-1",
24
+ },
25
+ });
26
+ });
27
+
28
+ it("should return null if contentTypes is undefined", () => {
29
+ const state = {
30
+ rivers: {
31
+ "river-1": {
32
+ plugin_type: "river",
33
+ },
34
+ },
35
+ // contentTypes is missing
36
+ };
37
+
38
+ const result = mapContentTypesToRivers(state);
39
+
40
+ expect(result).toBeNull();
41
+ });
42
+
43
+ it("should skip content types whose screen does not exist in rivers", () => {
44
+ const state = {
45
+ rivers: {
46
+ "river-1": {
47
+ plugin_type: "river",
48
+ },
49
+ },
50
+ contentTypes: {
51
+ "content-type-1": {
52
+ screen_id: "river-1",
53
+ },
54
+ "content-type-2": {
55
+ screen_id: "river-2", // river-2 does not exist
56
+ },
57
+ },
58
+ };
59
+
60
+ const result = mapContentTypesToRivers(state);
61
+
62
+ expect(result).toEqual({
63
+ "content-type-1": {
64
+ screenType: "river",
65
+ screen_id: "river-1",
66
+ },
67
+ });
68
+
69
+ // result is not null, but may be undefined for missing keys
70
+ expect(result && result["content-type-2"]).toBeUndefined();
71
+ });
72
+
73
+ it("should use 'type' if 'plugin_type' is not present in river", () => {
74
+ const state = {
75
+ rivers: {
76
+ "river-1": {
77
+ type: "custom-type",
78
+ },
79
+ },
80
+ contentTypes: {
81
+ "content-type-1": {
82
+ screen_id: "river-1",
83
+ },
84
+ },
85
+ };
86
+
87
+ const result = mapContentTypesToRivers(state);
88
+
89
+ expect(result).toEqual({
90
+ "content-type-1": {
91
+ screenType: "custom-type",
92
+ screen_id: "river-1",
93
+ },
94
+ });
95
+ });
96
+
97
+ it("should skip content types if neither plugin_type nor type is present in river", () => {
98
+ const state = {
99
+ rivers: {
100
+ "river-1": {
101
+ // no plugin_type or type
102
+ },
103
+ },
104
+ contentTypes: {
105
+ "content-type-1": {
106
+ screen_id: "river-1",
107
+ },
108
+ },
109
+ };
110
+
111
+ const result = mapContentTypesToRivers(state);
112
+
113
+ expect(result).toEqual({});
114
+ });
115
+
116
+ it("should handle empty contentTypes object", () => {
117
+ const state = {
118
+ rivers: {
119
+ "river-1": {
120
+ plugin_type: "river",
121
+ },
122
+ },
123
+ contentTypes: {},
124
+ };
125
+
126
+ const result = mapContentTypesToRivers(state);
127
+
128
+ expect(result).toEqual({});
129
+ });
130
+ });
@@ -13,6 +13,7 @@ import {
13
13
  isPlayable,
14
14
  isV2River,
15
15
  } from "./itemTypeMatchers";
16
+ import { RootState } from "@applicaster/zapp-react-native-redux/store";
16
17
 
17
18
  type PathAttribute = {
18
19
  screenType: string;
@@ -377,10 +378,11 @@ export const usesVideoModal = (
377
378
  return targetScreenConfiguration?.styles?.use_video_modal;
378
379
  };
379
380
 
380
- export const mapContentTypesToRivers = ({
381
- rivers,
382
- contentTypes,
383
- }): ZappContentTypesMapped | null => {
381
+ export const mapContentTypesToRivers = (
382
+ state: Partial<RootState>
383
+ ): ZappContentTypesMapped | null => {
384
+ const { rivers, contentTypes } = state;
385
+
384
386
  if (!contentTypes) {
385
387
  return null;
386
388
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "14.0.0-alpha.4104749434",
3
+ "version": "14.0.0-alpha.4138342511",
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": "14.0.0-alpha.4104749434",
30
+ "@applicaster/applicaster-types": "14.0.0-alpha.4138342511",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -38,7 +38,6 @@
38
38
  "peerDependencies": {
39
39
  "@applicaster/zapp-pipes-v2-client": "*",
40
40
  "@react-native-community/netinfo": "*",
41
- "immer": "*",
42
41
  "react": "*",
43
42
  "react-native": "*",
44
43
  "uglify-js": "*",
@@ -23,7 +23,9 @@ jest.mock(
23
23
 
24
24
  jest.useFakeTimers();
25
25
 
26
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation");
26
+ jest.mock(
27
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation"
28
+ );
27
29
 
28
30
  const mockStore = configureStore();
29
31
 
@@ -26,6 +26,9 @@ jest.mock("@applicaster/zapp-react-native-utils/analyticsUtils/", () => ({
26
26
  }));
27
27
 
28
28
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
29
+ ...jest.requireActual(
30
+ "@applicaster/zapp-react-native-utils/reactHooks/screen"
31
+ ),
29
32
  useTargetScreenData: jest.fn(() => ({})),
30
33
  useCurrentScreenData: jest.fn(() => ({})),
31
34
  }));
@@ -16,7 +16,8 @@ import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/acti
16
16
  import { isFunction, noop } from "../../functionUtils";
17
17
  import { useSendAnalyticsOnPress } from "../analytics";
18
18
  import { logOnPress, warnEmptyContentType } from "./helpers";
19
- import { useCurrentScreenData } from "../screen";
19
+ import { useCurrentScreenData, useScreenContext } from "../screen";
20
+ import { useScreenStateStore } from "../navigation/useScreenStateStore";
20
21
 
21
22
  /**
22
23
  * If onCellTap is defined execute the function and
@@ -42,10 +43,12 @@ export const useCellClick = ({
42
43
  }: Props): onPressReturnFn => {
43
44
  const { push, currentRoute } = useNavigation();
44
45
  const { pathname } = useRoute();
46
+ const screenStateStore = useScreenStateStore();
45
47
 
46
48
  const onCellTap: Option<Function> = React.useContext(CellTapContext);
47
49
  const actionExecutor = React.useContext(ActionExecutorContext);
48
50
  const screenData = useCurrentScreenData();
51
+ const screenState = useScreenContext()?.options;
49
52
 
50
53
  const cellSelectable = toBooleanWithDefaultTrue(
51
54
  component?.rules?.component_cells_selectable
@@ -83,6 +86,9 @@ export const useCellClick = ({
83
86
  await actionExecutor?.handleEntryActions(selectedItem, {
84
87
  component,
85
88
  screenData,
89
+ screenState,
90
+ screenRoute: pathname,
91
+ screenStateStore,
86
92
  });
87
93
  }
88
94
 
@@ -117,6 +123,7 @@ export const useCellClick = ({
117
123
  push,
118
124
  sendAnalyticsOnPress,
119
125
  screenData,
126
+ screenState,
120
127
  ]
121
128
  );
122
129
 
@@ -12,7 +12,6 @@ describe("Debug utils", () => {
12
12
  // Clear the timers object
13
13
  Object.keys(timers).forEach((key) => delete timers[key]);
14
14
 
15
- // Mock performance.now()
16
15
  // eslint-disable-next-line no-undef
17
16
  performanceNowMock = jest.spyOn(performance, "now");
18
17
  performanceNowMock.mockReturnValue(0); // Initial value
@@ -2,12 +2,16 @@ import { renderHook } from "@testing-library/react-hooks";
2
2
  import { allFeedsIsReady, useBatchLoading } from "../useBatchLoading";
3
3
  import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
4
4
  import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
5
+ import { waitFor } from "@testing-library/react-native";
5
6
 
6
7
  jest.mock("../../navigation");
7
8
 
8
9
  jest.mock(
9
10
  "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext",
10
11
  () => ({
12
+ ...jest.requireActual(
13
+ "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext"
14
+ ),
11
15
  useScreenContext: jest.fn().mockReturnValue({ screen: {}, entry: {} }),
12
16
  })
13
17
  );
@@ -33,7 +37,7 @@ describe("useBatchLoading", () => {
33
37
  jest.clearAllMocks();
34
38
  });
35
39
 
36
- it("loadPipesData start loading not started requests", () => {
40
+ it("loadPipesData start loading not started requests", async () => {
37
41
  const store = {
38
42
  zappPipes: {
39
43
  url1: {
@@ -65,7 +69,9 @@ describe("useBatchLoading", () => {
65
69
 
66
70
  const actions = (appStore.getStore() as any).getActions();
67
71
 
68
- expect(actions).toHaveLength(2);
72
+ await waitFor(() => {
73
+ expect(actions).toHaveLength(2);
74
+ });
69
75
 
70
76
  expect(actions[0]).toMatchObject({
71
77
  type: "ZAPP_PIPES_REQUEST_START",
@@ -4,13 +4,11 @@ import * as zappPipesModule from "@applicaster/zapp-react-native-redux/ZappPipes
4
4
  import * as reactReduxModules from "react-redux";
5
5
  import { Provider } from "react-redux";
6
6
  import * as React from "react";
7
- import configureStore from "redux-mock-store";
8
- import thunk from "redux-thunk";
9
7
  import * as useRouteHook from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
10
8
  import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
11
9
  import { useFeedLoader } from "../useFeedLoader";
12
-
13
- const mockStore = configureStore([thunk]);
10
+ import { WrappedWithProviders } from "../../../testUtils";
11
+ import { ScreenStateResolver } from "../../../appUtils/contextKeysManager/contextResolver";
14
12
 
15
13
  jest.useFakeTimers({ legacyFakeTimers: true });
16
14
 
@@ -55,13 +53,15 @@ const mockZappPipesData = {
55
53
 
56
54
  describe("useFeedLoader", () => {
57
55
  describe("with cached feed url", () => {
58
- const store = mockStore({
56
+ const store = {
59
57
  plugins: [],
60
58
  zappPipes: { "test://testfakeurl": mockZappPipesData },
61
- });
59
+ };
62
60
 
63
- const wrapper: React.FC<any> = ({ children }) => (
64
- <Provider store={store}>{children}</Provider>
61
+ const wrapper: React.FC<any> = ({ children, ...props }) => (
62
+ <WrappedWithProviders store={props.store || store}>
63
+ {children}
64
+ </WrappedWithProviders>
65
65
  );
66
66
 
67
67
  it("returns cached feed", () => {
@@ -110,8 +110,10 @@ describe("useFeedLoader", () => {
110
110
  describe("without cached feeds", () => {
111
111
  const feedUrl = "test://testfakeurl2";
112
112
 
113
- const wrapper: React.FC<any> = ({ children, store }) => (
114
- <Provider store={store}>{children}</Provider>
113
+ const wrapper: React.FC<any> = ({ children, ...props }) => (
114
+ <WrappedWithProviders store={props.store}>
115
+ {children}
116
+ </WrappedWithProviders>
115
117
  );
116
118
 
117
119
  it("It loads data for new url and returns it", () => {
@@ -123,10 +125,10 @@ describe("useFeedLoader", () => {
123
125
  .spyOn(zappPipesModule, "loadPipesData")
124
126
  .mockImplementation(jest.fn());
125
127
 
126
- const initialStore = mockStore({
128
+ const initialStore = {
127
129
  plugins: [],
128
130
  zappPipes: { "test://testfakeurl": "foobar" },
129
- });
131
+ };
130
132
 
131
133
  const { result, rerender } = renderHook(
132
134
  () => useFeedLoader({ feedUrl: "test://testfakeurl2" }),
@@ -135,15 +137,19 @@ describe("useFeedLoader", () => {
135
137
 
136
138
  expect(result.current.data).toBeNull();
137
139
 
138
- expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
140
+ expect(loadPipesDataSpy).toHaveBeenCalledWith(feedUrl, {
139
141
  clearCache: true,
140
142
  riverId: undefined,
143
+ callback: expect.any(Function),
144
+ resolvers: {
145
+ screen: expect.any(ScreenStateResolver),
146
+ },
141
147
  });
142
148
 
143
- const store2 = mockStore({
149
+ const store2 = {
144
150
  plugins: [],
145
151
  zappPipes: { "test://testfakeurl2": mockZappPipesData },
146
- });
152
+ };
147
153
 
148
154
  rerender({ store: store2 });
149
155
 
@@ -179,6 +185,11 @@ describe("useFeedLoader", () => {
179
185
  expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
180
186
  clearCache: true,
181
187
  riverId: undefined,
188
+ resolvers: {
189
+ screen: {
190
+ screenStateStore: undefined,
191
+ },
192
+ },
182
193
  });
183
194
 
184
195
  const store2 = mockStore({
@@ -228,6 +239,11 @@ describe("useFeedLoader", () => {
228
239
  expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
229
240
  clearCache: true,
230
241
  silentRefresh: true,
242
+ resolvers: {
243
+ screen: {
244
+ screenStateStore: undefined,
245
+ },
246
+ },
231
247
  });
232
248
 
233
249
  loadPipesDataSpy.mockRestore();
@@ -267,6 +283,11 @@ describe("useFeedLoader", () => {
267
283
  expect(loadPipesDataSpy).toBeCalledWith(nextUrl, {
268
284
  parentFeed: feedUrlWithNext,
269
285
  silentRefresh: true,
286
+ resolvers: {
287
+ screen: {
288
+ screenStateStore: undefined,
289
+ },
290
+ },
270
291
  });
271
292
 
272
293
  loadPipesDataSpy.mockRestore();
@@ -11,3 +11,5 @@ export { useBuildPipesUrl } from "./useBuildPipesUrl";
11
11
  export { usePipesCacheReset } from "./usePipesCacheReset";
12
12
 
13
13
  export { useBatchLoading } from "./useBatchLoading";
14
+
15
+ export { useLoadPipesDataDispatch } from "./useLoadPipesDataDispatch";
@@ -1,13 +1,12 @@
1
1
  import { complement, compose, isNil, map, min, prop, take, uniq } from "ramda";
2
- import { useDispatch } from "react-redux";
3
2
  import * as React from "react";
4
- import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks";
5
- import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
3
+ import { useZappPipesFeed } from "@applicaster/zapp-react-native-redux";
6
4
  import { isNilOrEmpty } from "../../reactUtils/helpers";
7
5
  import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
8
6
  import {
9
7
  getInflatedDataSourceUrl,
10
8
  getSearchContext,
9
+ useLoadPipesDataDispatch,
11
10
  } from "@applicaster/zapp-react-native-utils/reactHooks";
12
11
  import { isGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
13
12
  import { useScreenContext } from "../screen";
@@ -63,7 +62,6 @@ export const useBatchLoading = (
63
62
  componentsToRender: { data?: ZappDataSource; component_type: string }[],
64
63
  options: Options
65
64
  ) => {
66
- const dispatch = useDispatch();
67
65
  const { screen: screenContext, entry: entryContext } = useScreenContext();
68
66
  const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
69
67
  const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false);
@@ -118,7 +116,9 @@ export const useBatchLoading = (
118
116
  []
119
117
  );
120
118
 
121
- const feeds = useZappPipesFeeds(feedUrls);
119
+ const feeds = useZappPipesFeed(feedUrls);
120
+
121
+ const loadPipesDataDispatcher = useLoadPipesDataDispatch();
122
122
 
123
123
  // dispatch loadPipesData for each feed that is not loaded
124
124
  const runBatchLoading = React.useCallback(() => {
@@ -138,13 +138,20 @@ export const useBatchLoading = (
138
138
 
139
139
  if (mappedFeedUrl) {
140
140
  // 4. load data
141
- return dispatch(
142
- loadPipesData(mappedFeedUrl, { riverId: options.riverId })
141
+ return loadPipesDataDispatcher(
142
+ mappedFeedUrl,
143
+ {
144
+ riverId: options.riverId,
145
+ },
146
+ {
147
+ withResolvers: true,
148
+ withScreenRouteMapping: true,
149
+ }
143
150
  );
144
151
  }
145
152
  }
146
153
  });
147
- }, [feedUrls, feeds]);
154
+ }, [feedUrls, feeds, loadPipesDataDispatcher]);
148
155
 
149
156
  React.useEffect(() => {
150
157
  runBatchLoading();