@applicaster/zapp-react-native-utils 14.0.0-alpha.5219335081 → 14.0.0-alpha.5222319560
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.
- package/actionsExecutor/ActionExecutorContext.tsx +60 -84
- package/actionsExecutor/ScreenActions.ts +164 -0
- package/actionsExecutor/StorageActions.ts +110 -0
- package/actionsExecutor/feedDecorator.ts +171 -0
- package/actionsExecutor/screenResolver.ts +11 -0
- package/analyticsUtils/AnalyticsEvents/helper.ts +1 -1
- package/analyticsUtils/__tests__/analyticsUtils.test.js +0 -11
- package/appUtils/contextKeysManager/contextResolver.ts +42 -1
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -0
- package/appUtils/focusManager/index.ios.ts +10 -0
- package/appUtils/focusManager/index.ts +25 -16
- package/appUtils/focusManagerAux/utils/index.ts +18 -2
- package/configurationUtils/__tests__/manifestKeyParser.test.ts +0 -1
- package/package.json +2 -2
- package/reactHooks/cell-click/__tests__/index.test.js +3 -0
- package/reactHooks/cell-click/index.ts +8 -1
- package/reactHooks/debugging/__tests__/index.test.js +0 -1
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +71 -31
- package/reactHooks/feed/index.ts +2 -0
- package/reactHooks/feed/useBatchLoading.ts +14 -9
- package/reactHooks/feed/useFeedLoader.tsx +36 -38
- package/reactHooks/feed/useLoadPipesDataDispatch.ts +57 -0
- package/reactHooks/navigation/useRoute.ts +7 -2
- package/reactHooks/navigation/useScreenStateStore.ts +8 -0
- package/reactHooks/state/index.ts +1 -7
- package/reactHooks/state/useHomeRiver.ts +0 -31
- package/screenPickerUtils/index.ts +3 -6
- package/storage/ScreenSingleValueProvider.ts +204 -0
- package/storage/ScreenStateMultiSelectProvider.ts +293 -0
- package/storage/StorageMultiSelectProvider.ts +192 -0
- package/storage/StorageSingleSelectProvider.ts +108 -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
|
+
});
|
|
@@ -25,6 +25,7 @@ exports[`focusManager should be defined 1`] = `
|
|
|
25
25
|
"invokeHandler": [Function],
|
|
26
26
|
"isCurrentFocusOnTheTopScreen": [Function],
|
|
27
27
|
"isFocusDisabled": [Function],
|
|
28
|
+
"isFocusOn": [Function],
|
|
28
29
|
"isFocusOnContent": [Function],
|
|
29
30
|
"isFocusOnMenu": [Function],
|
|
30
31
|
"isGroupItemFocused": [Function],
|
|
@@ -66,6 +67,7 @@ exports[`focusManagerIOS should be defined 1`] = `
|
|
|
66
67
|
"getGroupRootById": [Function],
|
|
67
68
|
"getPreferredFocusChild": [Function],
|
|
68
69
|
"invokeHandler": [Function],
|
|
70
|
+
"isFocusOn": [Function],
|
|
69
71
|
"isGroupItemFocused": [Function],
|
|
70
72
|
"moveFocus": [Function],
|
|
71
73
|
"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
|
})();
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
isTabsScreenContentFocused,
|
|
21
21
|
isCurrentFocusOnContent,
|
|
22
22
|
isCurrentFocusOnMenu,
|
|
23
|
+
isCurrentFocusOn,
|
|
23
24
|
} from "../focusManagerAux/utils";
|
|
24
25
|
|
|
25
26
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
@@ -299,25 +300,25 @@ export const focusManager = (function () {
|
|
|
299
300
|
return isCurrentFocusOnMenu(currentFocusNode);
|
|
300
301
|
}
|
|
301
302
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
// Move focus to appropriate top navigation tab with context
|
|
304
|
+
function focusTopNavigation(isTabsScreen: boolean, item: ZappEntry) {
|
|
305
|
+
const landFocusTo = (id) => {
|
|
306
|
+
if (id) {
|
|
307
|
+
// set focus on selected menu item
|
|
308
|
+
const direction = undefined;
|
|
306
309
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
const context: FocusManager.FocusContext = {
|
|
311
|
+
source: "back",
|
|
312
|
+
preserveScroll: true,
|
|
313
|
+
};
|
|
311
314
|
|
|
312
|
-
|
|
315
|
+
logger.log({ message: "landFocusTo", data: { id } });
|
|
313
316
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
317
|
+
blur(direction);
|
|
318
|
+
setFocus(id, direction, context);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
318
321
|
|
|
319
|
-
// Move focus to appropriate top navigation tab with context
|
|
320
|
-
function focusTopNavigation(isTabsScreen: boolean, item: ZappEntry) {
|
|
321
322
|
if (isTabsScreen && isTabsScreenContentFocused(currentFocusNode)) {
|
|
322
323
|
const selectedTabId = findSelectedTabId(item);
|
|
323
324
|
|
|
@@ -604,6 +605,14 @@ export const focusManager = (function () {
|
|
|
604
605
|
return preferredFocus[0];
|
|
605
606
|
}
|
|
606
607
|
|
|
608
|
+
function isFocusOn(id): boolean {
|
|
609
|
+
return (
|
|
610
|
+
id &&
|
|
611
|
+
isCurrentFocusOnTheTopScreen() &&
|
|
612
|
+
isCurrentFocusOn(id, currentFocusNode)
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
|
|
607
616
|
/**
|
|
608
617
|
* this is the list of the functions available externally
|
|
609
618
|
* when importing the focus manager
|
|
@@ -634,9 +643,9 @@ export const focusManager = (function () {
|
|
|
634
643
|
recoverFocus,
|
|
635
644
|
isCurrentFocusOnTheTopScreen,
|
|
636
645
|
findPreferredFocusChild,
|
|
637
|
-
|
|
638
646
|
focusTopNavigation,
|
|
639
647
|
isFocusOnContent,
|
|
640
648
|
isFocusOnMenu,
|
|
649
|
+
isFocusOn,
|
|
641
650
|
};
|
|
642
651
|
})();
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "@applicaster/quick-brick-core/const";
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
|
-
|
|
15
|
+
getFocusableId,
|
|
16
16
|
SCREEN_PICKER_CONTAINER,
|
|
17
17
|
} from "@applicaster/zapp-react-native-utils/screenPickerUtils";
|
|
18
18
|
|
|
@@ -118,7 +118,7 @@ export const waitForContent = (focusableTree) => {
|
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
export const findSelectedTabId = (item: ZappEntry): string => {
|
|
121
|
-
const selectedTabId =
|
|
121
|
+
const selectedTabId = getFocusableId(item.id);
|
|
122
122
|
|
|
123
123
|
return selectedTabId;
|
|
124
124
|
};
|
|
@@ -186,3 +186,19 @@ export const isCurrentFocusOnContent = (node) => {
|
|
|
186
186
|
|
|
187
187
|
return isCurrentFocusOnContent(node.parent);
|
|
188
188
|
};
|
|
189
|
+
|
|
190
|
+
export const isCurrentFocusOn = (id, node) => {
|
|
191
|
+
if (!node) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (isRoot(node)) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (node?.id === id) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return isCurrentFocusOn(id, node.parent);
|
|
204
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "14.0.0-alpha.
|
|
3
|
+
"version": "14.0.0-alpha.5222319560",
|
|
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.
|
|
30
|
+
"@applicaster/applicaster-types": "14.0.0-alpha.5222319560",
|
|
31
31
|
"buffer": "^5.2.1",
|
|
32
32
|
"camelize": "^1.0.0",
|
|
33
33
|
"dayjs": "^1.11.10",
|
|
@@ -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
|
-
|
|
72
|
+
await waitFor(() => {
|
|
73
|
+
expect(actions).toHaveLength(2);
|
|
74
|
+
});
|
|
69
75
|
|
|
70
76
|
expect(actions[0]).toMatchObject({
|
|
71
77
|
type: "ZAPP_PIPES_REQUEST_START",
|
|
@@ -2,15 +2,12 @@ import { renderHook } from "@testing-library/react-hooks";
|
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
import * as zappPipesModule from "@applicaster/zapp-react-native-redux/ZappPipes";
|
|
4
4
|
import * as reactReduxModules from "react-redux";
|
|
5
|
-
import { Provider } from "react-redux";
|
|
6
5
|
import * as React from "react";
|
|
7
|
-
import configureStore from "redux-mock-store";
|
|
8
|
-
import thunk from "redux-thunk";
|
|
9
6
|
import * as useRouteHook from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
|
|
10
7
|
import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
|
|
11
8
|
import { useFeedLoader } from "../useFeedLoader";
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
import { WrappedWithProviders } from "../../../testUtils";
|
|
10
|
+
import { ScreenStateResolver } from "../../../appUtils/contextKeysManager/contextResolver";
|
|
14
11
|
|
|
15
12
|
jest.useFakeTimers({ legacyFakeTimers: true });
|
|
16
13
|
|
|
@@ -55,13 +52,15 @@ const mockZappPipesData = {
|
|
|
55
52
|
|
|
56
53
|
describe("useFeedLoader", () => {
|
|
57
54
|
describe("with cached feed url", () => {
|
|
58
|
-
const store =
|
|
55
|
+
const store = {
|
|
59
56
|
plugins: [],
|
|
60
57
|
zappPipes: { "test://testfakeurl": mockZappPipesData },
|
|
61
|
-
}
|
|
58
|
+
};
|
|
62
59
|
|
|
63
|
-
const wrapper: React.FC<any> = ({ children }) => (
|
|
64
|
-
<
|
|
60
|
+
const wrapper: React.FC<any> = ({ children, ...props }) => (
|
|
61
|
+
<WrappedWithProviders store={props.store || store}>
|
|
62
|
+
{children}
|
|
63
|
+
</WrappedWithProviders>
|
|
65
64
|
);
|
|
66
65
|
|
|
67
66
|
it("returns cached feed", () => {
|
|
@@ -110,8 +109,10 @@ describe("useFeedLoader", () => {
|
|
|
110
109
|
describe("without cached feeds", () => {
|
|
111
110
|
const feedUrl = "test://testfakeurl2";
|
|
112
111
|
|
|
113
|
-
const wrapper: React.FC<any> = ({ children,
|
|
114
|
-
<
|
|
112
|
+
const wrapper: React.FC<any> = ({ children, ...props }) => (
|
|
113
|
+
<WrappedWithProviders store={props.store}>
|
|
114
|
+
{children}
|
|
115
|
+
</WrappedWithProviders>
|
|
115
116
|
);
|
|
116
117
|
|
|
117
118
|
it("It loads data for new url and returns it", () => {
|
|
@@ -123,10 +124,10 @@ describe("useFeedLoader", () => {
|
|
|
123
124
|
.spyOn(zappPipesModule, "loadPipesData")
|
|
124
125
|
.mockImplementation(jest.fn());
|
|
125
126
|
|
|
126
|
-
const initialStore =
|
|
127
|
+
const initialStore = {
|
|
127
128
|
plugins: [],
|
|
128
129
|
zappPipes: { "test://testfakeurl": "foobar" },
|
|
129
|
-
}
|
|
130
|
+
};
|
|
130
131
|
|
|
131
132
|
const { result, rerender } = renderHook(
|
|
132
133
|
() => useFeedLoader({ feedUrl: "test://testfakeurl2" }),
|
|
@@ -135,15 +136,19 @@ describe("useFeedLoader", () => {
|
|
|
135
136
|
|
|
136
137
|
expect(result.current.data).toBeNull();
|
|
137
138
|
|
|
138
|
-
expect(loadPipesDataSpy).
|
|
139
|
+
expect(loadPipesDataSpy).toHaveBeenCalledWith(feedUrl, {
|
|
139
140
|
clearCache: true,
|
|
140
141
|
riverId: undefined,
|
|
142
|
+
callback: expect.any(Function),
|
|
143
|
+
resolvers: {
|
|
144
|
+
screen: expect.any(ScreenStateResolver),
|
|
145
|
+
},
|
|
141
146
|
});
|
|
142
147
|
|
|
143
|
-
const store2 =
|
|
148
|
+
const store2 = {
|
|
144
149
|
plugins: [],
|
|
145
150
|
zappPipes: { "test://testfakeurl2": mockZappPipesData },
|
|
146
|
-
}
|
|
151
|
+
};
|
|
147
152
|
|
|
148
153
|
rerender({ store: store2 });
|
|
149
154
|
|
|
@@ -164,10 +169,10 @@ describe("useFeedLoader", () => {
|
|
|
164
169
|
.spyOn(reactReduxModules, "useDispatch")
|
|
165
170
|
.mockImplementation(() => jest.fn());
|
|
166
171
|
|
|
167
|
-
const initialStore =
|
|
172
|
+
const initialStore = {
|
|
168
173
|
plugins: [],
|
|
169
174
|
zappPipes: { "test://testfakeurl": "foobar" },
|
|
170
|
-
}
|
|
175
|
+
};
|
|
171
176
|
|
|
172
177
|
const { result, rerender } = renderHook(
|
|
173
178
|
() => useFeedLoader({ feedUrl: "test://testfakeurl2" }),
|
|
@@ -176,15 +181,22 @@ describe("useFeedLoader", () => {
|
|
|
176
181
|
|
|
177
182
|
expect(result.current.data).toBeNull();
|
|
178
183
|
|
|
179
|
-
expect(loadPipesDataSpy).
|
|
184
|
+
expect(loadPipesDataSpy.mock.calls[0][0]).toBe(feedUrl);
|
|
185
|
+
|
|
186
|
+
expect(loadPipesDataSpy.mock.calls[0][1]).toMatchObject({
|
|
180
187
|
clearCache: true,
|
|
181
188
|
riverId: undefined,
|
|
189
|
+
resolvers: {
|
|
190
|
+
screen: {
|
|
191
|
+
screenStateStore: expect.any(Function),
|
|
192
|
+
},
|
|
193
|
+
},
|
|
182
194
|
});
|
|
183
195
|
|
|
184
|
-
const store2 =
|
|
196
|
+
const store2 = {
|
|
185
197
|
plugins: [],
|
|
186
198
|
zappPipes: { "test://testfakeurl2": mockZappPipesData },
|
|
187
|
-
}
|
|
199
|
+
};
|
|
188
200
|
|
|
189
201
|
rerender({ store: store2 });
|
|
190
202
|
|
|
@@ -197,8 +209,10 @@ describe("useFeedLoader", () => {
|
|
|
197
209
|
const feedUrl = "test://testfakeurl";
|
|
198
210
|
const feedUrlWithNext = "test://withnexttestfakeurl";
|
|
199
211
|
|
|
200
|
-
const wrapper: React.FC<any> = ({ children,
|
|
201
|
-
<
|
|
212
|
+
const wrapper: React.FC<any> = ({ children, ...props }) => (
|
|
213
|
+
<WrappedWithProviders store={props.store || {}}>
|
|
214
|
+
{children}
|
|
215
|
+
</WrappedWithProviders>
|
|
202
216
|
);
|
|
203
217
|
|
|
204
218
|
describe("reloadData", () => {
|
|
@@ -211,10 +225,10 @@ describe("useFeedLoader", () => {
|
|
|
211
225
|
.spyOn(reactReduxModules, "useDispatch")
|
|
212
226
|
.mockImplementation(() => jest.fn());
|
|
213
227
|
|
|
214
|
-
const initialStore =
|
|
228
|
+
const initialStore = {
|
|
215
229
|
plugins: [],
|
|
216
230
|
zappPipes: { [feedUrl]: "foobar" },
|
|
217
|
-
}
|
|
231
|
+
};
|
|
218
232
|
|
|
219
233
|
const { result } = renderHook(() => useFeedLoader({ feedUrl }), {
|
|
220
234
|
wrapper,
|
|
@@ -223,11 +237,24 @@ describe("useFeedLoader", () => {
|
|
|
223
237
|
|
|
224
238
|
const { reloadData } = result.current;
|
|
225
239
|
|
|
226
|
-
reloadData();
|
|
240
|
+
reloadData?.();
|
|
241
|
+
|
|
242
|
+
expect(loadPipesDataSpy).toHaveBeenCalled();
|
|
243
|
+
|
|
244
|
+
expect(
|
|
245
|
+
loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][0]
|
|
246
|
+
).toBe(feedUrl);
|
|
227
247
|
|
|
228
|
-
expect(
|
|
248
|
+
expect(
|
|
249
|
+
loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][1]
|
|
250
|
+
).toMatchObject({
|
|
229
251
|
clearCache: true,
|
|
230
252
|
silentRefresh: true,
|
|
253
|
+
resolvers: {
|
|
254
|
+
screen: {
|
|
255
|
+
screenStateStore: expect.any(Function),
|
|
256
|
+
},
|
|
257
|
+
},
|
|
231
258
|
});
|
|
232
259
|
|
|
233
260
|
loadPipesDataSpy.mockRestore();
|
|
@@ -247,10 +274,10 @@ describe("useFeedLoader", () => {
|
|
|
247
274
|
.spyOn(reactReduxModules, "useDispatch")
|
|
248
275
|
.mockImplementation(() => jest.fn());
|
|
249
276
|
|
|
250
|
-
const initialStore =
|
|
277
|
+
const initialStore = {
|
|
251
278
|
plugins: [],
|
|
252
279
|
zappPipes: { [feedUrlWithNext]: { data: { next: nextUrl } } },
|
|
253
|
-
}
|
|
280
|
+
};
|
|
254
281
|
|
|
255
282
|
const { result } = renderHook(
|
|
256
283
|
() => useFeedLoader({ feedUrl: feedUrlWithNext }),
|
|
@@ -262,11 +289,24 @@ describe("useFeedLoader", () => {
|
|
|
262
289
|
|
|
263
290
|
const { loadNext } = result.current;
|
|
264
291
|
|
|
265
|
-
loadNext();
|
|
292
|
+
loadNext?.();
|
|
293
|
+
|
|
294
|
+
expect(loadPipesDataSpy).toHaveBeenCalled();
|
|
295
|
+
|
|
296
|
+
expect(
|
|
297
|
+
loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][0]
|
|
298
|
+
).toBe(nextUrl);
|
|
266
299
|
|
|
267
|
-
expect(
|
|
300
|
+
expect(
|
|
301
|
+
loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][1]
|
|
302
|
+
).toMatchObject({
|
|
268
303
|
parentFeed: feedUrlWithNext,
|
|
269
304
|
silentRefresh: true,
|
|
305
|
+
resolvers: {
|
|
306
|
+
screen: {
|
|
307
|
+
screenStateStore: expect.any(Function),
|
|
308
|
+
},
|
|
309
|
+
},
|
|
270
310
|
});
|
|
271
311
|
|
|
272
312
|
loadPipesDataSpy.mockRestore();
|
package/reactHooks/feed/index.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { complement, compose, isNil, map, min, prop, take, uniq } from "ramda";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import {
|
|
4
|
-
ZappPipes,
|
|
5
|
-
useAppDispatch,
|
|
6
|
-
useZappPipesFeed,
|
|
7
|
-
} from "@applicaster/zapp-react-native-redux";
|
|
3
|
+
import { useZappPipesFeed } from "@applicaster/zapp-react-native-redux";
|
|
8
4
|
import { isNilOrEmpty } from "../../reactUtils/helpers";
|
|
9
5
|
import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
10
6
|
import {
|
|
11
7
|
getInflatedDataSourceUrl,
|
|
12
8
|
getSearchContext,
|
|
9
|
+
useLoadPipesDataDispatch,
|
|
13
10
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
14
11
|
import { isGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
|
|
15
12
|
import { useScreenContext } from "../screen";
|
|
@@ -65,7 +62,6 @@ export const useBatchLoading = (
|
|
|
65
62
|
componentsToRender: { data?: ZappDataSource; component_type: string }[],
|
|
66
63
|
options: Options
|
|
67
64
|
) => {
|
|
68
|
-
const dispatch = useAppDispatch();
|
|
69
65
|
const { screen: screenContext, entry: entryContext } = useScreenContext();
|
|
70
66
|
const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
|
|
71
67
|
const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false);
|
|
@@ -122,6 +118,8 @@ export const useBatchLoading = (
|
|
|
122
118
|
|
|
123
119
|
const feeds = useZappPipesFeed(feedUrls);
|
|
124
120
|
|
|
121
|
+
const loadPipesDataDispatcher = useLoadPipesDataDispatch();
|
|
122
|
+
|
|
125
123
|
// dispatch loadPipesData for each feed that is not loaded
|
|
126
124
|
const runBatchLoading = React.useCallback(() => {
|
|
127
125
|
batchComponents.forEach((rawData: any) => {
|
|
@@ -140,13 +138,20 @@ export const useBatchLoading = (
|
|
|
140
138
|
|
|
141
139
|
if (mappedFeedUrl) {
|
|
142
140
|
// 4. load data
|
|
143
|
-
return
|
|
144
|
-
|
|
141
|
+
return loadPipesDataDispatcher(
|
|
142
|
+
mappedFeedUrl,
|
|
143
|
+
{
|
|
144
|
+
riverId: options.riverId,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
withResolvers: true,
|
|
148
|
+
withScreenRouteMapping: true,
|
|
149
|
+
}
|
|
145
150
|
);
|
|
146
151
|
}
|
|
147
152
|
}
|
|
148
153
|
});
|
|
149
|
-
}, [feedUrls, feeds]);
|
|
154
|
+
}, [feedUrls, feeds, loadPipesDataDispatcher]);
|
|
150
155
|
|
|
151
156
|
React.useEffect(() => {
|
|
152
157
|
runBatchLoading();
|