@applicaster/zapp-react-native-utils 12.0.0-alpha.4433422595 → 12.0.0-alpha.4440903139
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/analyticsUtils/AnalyticPlayerListener.ts +141 -0
- package/analyticsUtils/AnalyticsEvents/index.ts +13 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +12 -0
- package/analyticsUtils/index.tsx +12 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/accessibilityManager/const.ts +50 -0
- package/appUtils/accessibilityManager/hooks.ts +40 -0
- package/appUtils/accessibilityManager/index.ts +166 -0
- package/appUtils/accessibilityManager/logger.ts +5 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
- package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
- package/appUtils/focusManager/__tests__/index.test.ts +2 -3
- package/appUtils/focusManager/index.ts +76 -78
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +31 -16
- package/appUtils/focusManager/utils/index.ts +100 -0
- package/appUtils/focusManagerAux/index.ts +76 -0
- package/appUtils/focusManagerAux/utils/index.ts +100 -0
- package/appUtils/index.js +2 -0
- package/appUtils/keyCodes/keys.web.js +8 -0
- package/appUtils/keyCodes/keysByPlatform.js +11 -1
- package/appUtils/platform/const.ts +25 -0
- package/appUtils/platform/index.ts +3 -0
- package/appUtils/platform/platformUtils.ts +218 -0
- package/appUtils/playerManager/conts.ts +1 -0
- package/appUtils/playerManager/index.ts +20 -32
- package/appUtils/playerManager/nativePlayerManager.ts +12 -0
- package/appUtils/playerManager/player.ts +10 -3
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
- package/appUtils/playerManager/playerNative.ts +11 -15
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +8 -2
- package/appUtils/playerManager/usePlayerState.tsx +19 -34
- package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
- package/arrayUtils/index.ts +16 -0
- package/booleanUtils/index.ts +4 -4
- package/cellUtils/__tests__/cellUtils.test.ts +9 -20
- package/cellUtils/index.ts +44 -17
- package/dateUtils/index.ts +11 -0
- package/focusManager/FocusManager.ts +20 -5
- package/focusManager/__tests__/FocusManager.test.ts +0 -1
- package/functionUtils/index.ts +38 -0
- package/headersUtils/index.ts +1 -0
- package/index.d.ts +15 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/keyboardState/index.ts +0 -0
- package/localizationUtils/__tests__/localizationUtils.test.ts +72 -53
- package/localizationUtils/index.ts +13 -22
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +123 -1
- package/manifestUtils/keys.js +2 -1
- package/navigationUtils/index.ts +49 -1
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/__tests__/toNumber.test.ts +2 -2
- package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +66 -0
- package/numberUtils/__tests__/toPositiveNumberWithDefaultZero.test.ts +66 -0
- package/numberUtils/index.ts +27 -16
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +123 -1
- package/playerUtils/imaScriptDom.ts +59 -0
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +16 -1
- package/reactHooks/cell-click/__tests__/index.test.js +13 -4
- package/reactHooks/cell-click/index.ts +28 -14
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useBatchLoading.ts +52 -28
- package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +43 -29
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/types/index.ts +1 -0
- package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +7 -2
- package/reactHooks/navigation/__mocks__/index.ts +13 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +11 -8
- package/reactHooks/navigation/index.ts +48 -65
- package/reactHooks/player/useAutoSeek.tsx +9 -7
- package/reactHooks/resolvers/useCellResolver.ts +5 -1
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
- package/reactHooks/screen/index.ts +2 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
- package/reactHooks/screen/useScreenContext.ts +3 -3
- package/reactHooks/useConfiguration.ts +2 -2
- package/reactUtils/index.ts +104 -0
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +65 -30
- package/types.ts +4 -0
- package/appUtils/focusManager/FocusableEvents.ts +0 -8
- package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
- package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
|
@@ -4,38 +4,77 @@ import {
|
|
|
4
4
|
timers,
|
|
5
5
|
} from "@applicaster/zapp-react-native-utils/reactHooks/debugging";
|
|
6
6
|
|
|
7
|
-
jest.useFakeTimers();
|
|
8
|
-
|
|
9
7
|
describe("Debug utils", () => {
|
|
8
|
+
let performanceNowMock;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.useFakeTimers();
|
|
12
|
+
// Clear the timers object
|
|
13
|
+
Object.keys(timers).forEach((key) => delete timers[key]);
|
|
14
|
+
|
|
15
|
+
// Mock performance.now()
|
|
16
|
+
// eslint-disable-next-line no-undef
|
|
17
|
+
performanceNowMock = jest.spyOn(performance, "now");
|
|
18
|
+
performanceNowMock.mockReturnValue(0); // Initial value
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
jest.useRealTimers();
|
|
23
|
+
jest.restoreAllMocks();
|
|
24
|
+
performanceNowMock.mockRestore();
|
|
25
|
+
});
|
|
26
|
+
|
|
10
27
|
describe("Unit Test: time", () => {
|
|
11
28
|
it("should create new timer if timer with a label does not exist", () => {
|
|
29
|
+
performanceNowMock.mockReturnValue(1000);
|
|
12
30
|
time("timer1");
|
|
13
31
|
expect(timers["timer1"]).toBeDefined();
|
|
32
|
+
expect(timers["timer1"].startTime).toBe(1000);
|
|
14
33
|
});
|
|
15
34
|
|
|
16
35
|
it("should update start time of timer if timer with a label exists", () => {
|
|
36
|
+
// First call to time()
|
|
37
|
+
performanceNowMock.mockReturnValue(1000);
|
|
38
|
+
time("timer1");
|
|
17
39
|
const previousStartTime = timers["timer1"].startTime;
|
|
18
|
-
|
|
40
|
+
|
|
41
|
+
// Second call to time()
|
|
42
|
+
performanceNowMock.mockReturnValue(2000);
|
|
19
43
|
time("timer1");
|
|
20
44
|
const currentStartTime = timers["timer1"].startTime;
|
|
21
|
-
|
|
45
|
+
|
|
46
|
+
expect(previousStartTime).toBe(1000);
|
|
47
|
+
expect(currentStartTime).toBe(2000);
|
|
22
48
|
});
|
|
23
49
|
});
|
|
24
50
|
|
|
25
51
|
describe("Unit Test: timeEnd", () => {
|
|
26
|
-
beforeEach(() =>
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
jest.spyOn(console, "log").mockImplementation(() => {});
|
|
54
|
+
});
|
|
27
55
|
|
|
28
56
|
it("should log elapsed time", () => {
|
|
29
57
|
const logSpy = jest.spyOn(console, "log");
|
|
30
58
|
|
|
59
|
+
performanceNowMock.mockReturnValue(1000);
|
|
60
|
+
time("timer1");
|
|
61
|
+
|
|
62
|
+
performanceNowMock.mockReturnValue(2000);
|
|
31
63
|
timeEnd("timer1");
|
|
32
64
|
|
|
33
|
-
expect(logSpy).
|
|
65
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
66
|
+
"timer1: 1000.000ms 2",
|
|
67
|
+
expect.any(Object)
|
|
68
|
+
);
|
|
34
69
|
});
|
|
35
70
|
|
|
36
71
|
it("should not log elapsed time if accumulative: true in settings", () => {
|
|
37
72
|
const logSpy = jest.spyOn(console, "log");
|
|
38
73
|
|
|
74
|
+
performanceNowMock.mockReturnValue(1000);
|
|
75
|
+
time("timer1");
|
|
76
|
+
|
|
77
|
+
performanceNowMock.mockReturnValue(2000);
|
|
39
78
|
timeEnd("timer1", { accumulative: true });
|
|
40
79
|
|
|
41
80
|
expect(logSpy).not.toHaveBeenCalled();
|
|
@@ -43,21 +82,52 @@ describe("Debug utils", () => {
|
|
|
43
82
|
});
|
|
44
83
|
|
|
45
84
|
describe("Integration Test: time and timeEnd", () => {
|
|
46
|
-
beforeEach(() =>
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
jest.spyOn(console, "log").mockImplementation(() => {});
|
|
87
|
+
});
|
|
47
88
|
|
|
48
89
|
it("should log elapsed time on timeEnd call", () => {
|
|
49
90
|
const logSpy = jest.spyOn(console, "log");
|
|
91
|
+
|
|
92
|
+
performanceNowMock.mockReturnValue(1000);
|
|
50
93
|
time("timer1");
|
|
94
|
+
|
|
95
|
+
performanceNowMock.mockReturnValue(2000);
|
|
51
96
|
timeEnd("timer1");
|
|
52
|
-
|
|
97
|
+
|
|
98
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
99
|
+
"timer1: 1000.000ms 2",
|
|
100
|
+
expect.objectContaining({
|
|
101
|
+
label: "timer1",
|
|
102
|
+
settings: undefined,
|
|
103
|
+
})
|
|
104
|
+
);
|
|
53
105
|
});
|
|
54
106
|
|
|
55
|
-
it("should log total and average time
|
|
107
|
+
it("should log total and average time when accumulative timing is complete", () => {
|
|
56
108
|
const logSpy = jest.spyOn(console, "log");
|
|
109
|
+
|
|
110
|
+
// First timing
|
|
111
|
+
performanceNowMock.mockReturnValue(1000);
|
|
57
112
|
time("timer2");
|
|
113
|
+
|
|
114
|
+
performanceNowMock.mockReturnValue(2000);
|
|
58
115
|
timeEnd("timer2", { accumulative: true });
|
|
59
|
-
|
|
60
|
-
|
|
116
|
+
|
|
117
|
+
// Second timing
|
|
118
|
+
performanceNowMock.mockReturnValue(3000);
|
|
119
|
+
time("timer2");
|
|
120
|
+
|
|
121
|
+
performanceNowMock.mockReturnValue(5000);
|
|
122
|
+
timeEnd("timer2"); // This will log because timer is accumulative (callCount > 1)
|
|
123
|
+
|
|
124
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
125
|
+
"timer2: 2000.000ms 2",
|
|
126
|
+
expect.objectContaining({
|
|
127
|
+
label: "timer2",
|
|
128
|
+
settings: undefined,
|
|
129
|
+
})
|
|
130
|
+
);
|
|
61
131
|
});
|
|
62
132
|
});
|
|
63
133
|
});
|
|
@@ -77,8 +77,6 @@ describe("useBatchLoading", () => {
|
|
|
77
77
|
|
|
78
78
|
const actions = store.getActions();
|
|
79
79
|
|
|
80
|
-
expect(useDispatchSpy).toBeCalledTimes(1);
|
|
81
|
-
|
|
82
80
|
expect(actions).toHaveLength(2);
|
|
83
81
|
|
|
84
82
|
expect(actions[0]).toMatchObject({
|
|
@@ -129,6 +127,8 @@ describe("useBatchLoading", () => {
|
|
|
129
127
|
// ... more items
|
|
130
128
|
];
|
|
131
129
|
|
|
130
|
+
expect(useDispatchSpy).toBeCalledTimes(0);
|
|
131
|
+
|
|
132
132
|
renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
|
|
133
133
|
wrapper,
|
|
134
134
|
initialProps: { store },
|
|
@@ -136,8 +136,6 @@ describe("useBatchLoading", () => {
|
|
|
136
136
|
|
|
137
137
|
const actions = store.getActions();
|
|
138
138
|
|
|
139
|
-
expect(useDispatchSpy).toBeCalledTimes(1);
|
|
140
|
-
|
|
141
139
|
expect(actions).toHaveLength(1);
|
|
142
140
|
|
|
143
141
|
expect(actions[0]).toMatchObject({
|
|
@@ -172,8 +170,6 @@ describe("useBatchLoading", () => {
|
|
|
172
170
|
initialProps: { store },
|
|
173
171
|
});
|
|
174
172
|
|
|
175
|
-
expect(useDispatchSpy).toBeCalledTimes(1);
|
|
176
|
-
|
|
177
173
|
const actions = store.getActions();
|
|
178
174
|
|
|
179
175
|
expect(actions).toHaveLength(3);
|
|
@@ -218,6 +214,73 @@ describe("useBatchLoading", () => {
|
|
|
218
214
|
|
|
219
215
|
expect(result.current).toBe(true);
|
|
220
216
|
});
|
|
217
|
+
|
|
218
|
+
it("gallery-qb: loadPipesData should be called only once for first component in the gallery", () => {
|
|
219
|
+
const store = mockStore({
|
|
220
|
+
zappPipes: {},
|
|
221
|
+
test: "true",
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
useDispatchSpy.mockReturnValue(store.dispatch);
|
|
225
|
+
|
|
226
|
+
const initialBatchSize = 3;
|
|
227
|
+
const riverId = "123";
|
|
228
|
+
|
|
229
|
+
const data = [
|
|
230
|
+
{
|
|
231
|
+
component_type: "gallery-qb",
|
|
232
|
+
ui_components: [{ data: { source: "url1" } }],
|
|
233
|
+
},
|
|
234
|
+
{ data: { source: "url2" } },
|
|
235
|
+
{ data: { source: "url3" } },
|
|
236
|
+
{ data: { source: "url4" } },
|
|
237
|
+
{ data: { source: "url5" } },
|
|
238
|
+
{ data: { source: "url6" } },
|
|
239
|
+
// ... more items
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
|
|
243
|
+
wrapper,
|
|
244
|
+
initialProps: { store },
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const actions = store.getActions();
|
|
248
|
+
|
|
249
|
+
expect(actions).toHaveLength(1);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("gallery-qb: initial batch ready when all initial items loaded", () => {
|
|
253
|
+
const store = mockStore({
|
|
254
|
+
zappPipes: {
|
|
255
|
+
url1: {
|
|
256
|
+
loading: false,
|
|
257
|
+
error: false,
|
|
258
|
+
data: {},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
useDispatchSpy.mockReturnValue(store.dispatch);
|
|
264
|
+
|
|
265
|
+
const initialBatchSize = 3;
|
|
266
|
+
const riverId = "123";
|
|
267
|
+
|
|
268
|
+
const data: Partial<ZappUIComponent>[] = [
|
|
269
|
+
{
|
|
270
|
+
component_type: "gallery-qb",
|
|
271
|
+
ui_components: [{ data: { source: "url1" } }],
|
|
272
|
+
},
|
|
273
|
+
{ data: { source: "url2" } },
|
|
274
|
+
{ data: { source: "url3" } },
|
|
275
|
+
];
|
|
276
|
+
|
|
277
|
+
const { result } = renderHook(
|
|
278
|
+
() => useBatchLoading(data, { initialBatchSize, riverId }),
|
|
279
|
+
{ wrapper, initialProps: { store } }
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
expect(result.current).toBe(true);
|
|
283
|
+
});
|
|
221
284
|
});
|
|
222
285
|
|
|
223
286
|
describe("allFeedsIsReady", () => {
|
|
@@ -1,77 +1,26 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { renderHook } from "@testing-library/react-hooks";
|
|
3
|
-
import configureStore from "redux-mock-store";
|
|
4
|
-
import thunk from "redux-thunk";
|
|
5
|
-
import { Provider } from "react-redux";
|
|
6
|
-
|
|
7
2
|
import { encodeParams } from "@applicaster/zapp-pipes-v2-client/src/utils";
|
|
8
3
|
|
|
9
|
-
const mockStore = configureStore([thunk]);
|
|
10
|
-
|
|
11
|
-
jest.useFakeTimers();
|
|
12
|
-
|
|
13
4
|
const appData = {
|
|
14
5
|
platform: "foo",
|
|
15
6
|
version_name: "1.2.0",
|
|
16
7
|
sdk_version: "2.0.0",
|
|
17
8
|
};
|
|
18
9
|
|
|
19
|
-
|
|
20
|
-
"http://foo.com/show": {
|
|
21
|
-
context_obj: [
|
|
22
|
-
{ type: "ctx", key: "platform" },
|
|
23
|
-
{ type: "query", mapper: "version", key: "appVersion" },
|
|
24
|
-
{ type: "header", mapper: "SDK", key: "sdkVersion" },
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const store_mock = { appData, pipesEndpoints };
|
|
30
|
-
|
|
31
|
-
const defaultStore = mockStore({
|
|
32
|
-
pipesEndpoints,
|
|
33
|
-
appData,
|
|
34
|
-
});
|
|
10
|
+
let INFLATED_URL;
|
|
35
11
|
|
|
36
|
-
|
|
37
|
-
|
|
12
|
+
jest.mock(
|
|
13
|
+
"@applicaster/zapp-react-native-utils/reactHooks/feed/useInflatedUrl",
|
|
14
|
+
() => ({
|
|
15
|
+
useInflatedUrl: () => INFLATED_URL,
|
|
16
|
+
})
|
|
38
17
|
);
|
|
39
18
|
|
|
40
|
-
const navigator = {
|
|
41
|
-
currentRoute: "/river/A1234",
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
jest.mock("../../navigation", () => ({
|
|
45
|
-
useNavigation: jest.fn(() => navigator),
|
|
46
|
-
useRoute: jest.fn(() => ({})),
|
|
47
|
-
}));
|
|
48
|
-
|
|
49
|
-
const contexts = {
|
|
50
|
-
entry: { id: "show-1" },
|
|
51
|
-
search: {},
|
|
52
|
-
screen: {},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const contextMock = (type) => ({
|
|
56
|
-
useZappPipesContext: () => [contexts[type] || {}],
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
jest.mock("@applicaster/zapp-react-native-ui-components/Contexts", () => ({
|
|
60
|
-
ZappPipesEntryContext: contextMock("entry"),
|
|
61
|
-
ZappPipesSearchContext: contextMock("search"),
|
|
62
|
-
ZappPipesScreenContext: contextMock("screen"),
|
|
63
|
-
}));
|
|
64
|
-
|
|
65
|
-
jest.mock("@applicaster/zapp-react-native-redux/AppStore", () => ({
|
|
66
|
-
appStore: {
|
|
67
|
-
get: jest.fn((prop) => store_mock[prop]),
|
|
68
|
-
},
|
|
69
|
-
}));
|
|
70
|
-
|
|
71
19
|
const { useBuildPipesUrl } = require("../useBuildPipesUrl");
|
|
72
20
|
|
|
73
21
|
describe("useBuildPipesUrl", () => {
|
|
74
22
|
it("returns the proper url", async () => {
|
|
23
|
+
INFLATED_URL = "http://foo.com/show/show-1";
|
|
75
24
|
const url = "http://foo.com/show/{{showId}}";
|
|
76
25
|
|
|
77
26
|
const mapping = {
|
|
@@ -81,12 +30,20 @@ describe("useBuildPipesUrl", () => {
|
|
|
81
30
|
},
|
|
82
31
|
};
|
|
83
32
|
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
33
|
+
const buildResult = {
|
|
34
|
+
params: {
|
|
35
|
+
ctx: encodeParams({ platform: appData.platform }),
|
|
36
|
+
version: appData.version_name,
|
|
37
|
+
},
|
|
38
|
+
headers: { SDK: appData.sdk_version },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const mockedRequestBuilder = {
|
|
42
|
+
build: jest.fn(() => Promise.resolve(buildResult)),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const { result, waitForNextUpdate } = renderHook(() =>
|
|
46
|
+
useBuildPipesUrl({ url, mapping, requestBuilder: mockedRequestBuilder })
|
|
90
47
|
);
|
|
91
48
|
|
|
92
49
|
expect(result.current.url).toBe("http://foo.com/show/show-1");
|
|
@@ -108,6 +65,7 @@ describe("useBuildPipesUrl", () => {
|
|
|
108
65
|
});
|
|
109
66
|
|
|
110
67
|
it("returns empty object if url is not in endpoints", async () => {
|
|
68
|
+
INFLATED_URL = "http://foobar.com/show-1";
|
|
111
69
|
const url = "http://foobar.com/{{showId}}";
|
|
112
70
|
|
|
113
71
|
const mapping = {
|
|
@@ -117,12 +75,14 @@ describe("useBuildPipesUrl", () => {
|
|
|
117
75
|
},
|
|
118
76
|
};
|
|
119
77
|
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
78
|
+
const buildResult = {};
|
|
79
|
+
|
|
80
|
+
const mockedRequestBuilder = {
|
|
81
|
+
build: jest.fn(() => Promise.resolve(buildResult)),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const { result, waitForNextUpdate } = renderHook(() =>
|
|
85
|
+
useBuildPipesUrl({ url, mapping, requestBuilder: mockedRequestBuilder })
|
|
126
86
|
);
|
|
127
87
|
|
|
128
88
|
expect(result.current.url).toBe("http://foobar.com/show-1");
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { renderHook } from "@testing-library/react-hooks";
|
|
2
2
|
|
|
3
|
-
import { useFeedRefresh, feedRefreshLogger } from "../";
|
|
4
|
-
|
|
5
3
|
jest.useFakeTimers();
|
|
6
4
|
|
|
5
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
6
|
+
useIsScreenActive: () => true,
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const { useFeedRefresh, feedRefreshLogger } = require("..");
|
|
10
|
+
|
|
7
11
|
describe("useFeedRefresh", () => {
|
|
8
12
|
const reloadData = jest.fn();
|
|
9
13
|
|
|
@@ -12,9 +12,21 @@ const {
|
|
|
12
12
|
|
|
13
13
|
const { reactHooksLogger } = require("../../logger");
|
|
14
14
|
|
|
15
|
+
let mockStore;
|
|
16
|
+
|
|
15
17
|
describe("getInflatedDataSourceUrl", () => {
|
|
16
18
|
beforeEach(() => {
|
|
17
19
|
reactHooksLogger.warning.mockClear();
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
appStore,
|
|
23
|
+
} = require("@applicaster/zapp-react-native-redux/AppStore");
|
|
24
|
+
|
|
25
|
+
mockStore = {
|
|
26
|
+
getState: jest.fn().mockReturnValue({ pipesEndpoints: {} }),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
appStore.set(mockStore);
|
|
18
30
|
});
|
|
19
31
|
|
|
20
32
|
it("returns the url inflated with the right params", () => {
|
|
@@ -72,7 +84,8 @@ describe("getInflatedDataSourceUrl", () => {
|
|
|
72
84
|
expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
|
|
73
85
|
|
|
74
86
|
expect(reactHooksLogger.warning).toHaveBeenCalledWith({
|
|
75
|
-
message:
|
|
87
|
+
message:
|
|
88
|
+
"Not enough data to fully inflate url: {{entry.id}} source: http://foo.com/show/seriesId={{seriesId}}",
|
|
76
89
|
data: { source, contexts, mapping },
|
|
77
90
|
});
|
|
78
91
|
});
|
|
@@ -107,7 +120,8 @@ describe("getInflatedDataSourceUrl", () => {
|
|
|
107
120
|
expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
|
|
108
121
|
|
|
109
122
|
expect(reactHooksLogger.warning).toHaveBeenCalledWith({
|
|
110
|
-
message:
|
|
123
|
+
message:
|
|
124
|
+
"Not enough data to fully inflate url: {{entry.extensions.groupName}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName}}",
|
|
111
125
|
data: { source, contexts, mapping },
|
|
112
126
|
});
|
|
113
127
|
});
|
|
@@ -148,7 +162,8 @@ describe("getInflatedDataSourceUrl", () => {
|
|
|
148
162
|
expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);
|
|
149
163
|
|
|
150
164
|
expect(reactHooksLogger.warning).toHaveBeenCalledWith({
|
|
151
|
-
message:
|
|
165
|
+
message:
|
|
166
|
+
"Not enough data to fully inflate url: {{entry.extensions.groupName2}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName1}}?test={{groupName2}}",
|
|
152
167
|
data: { source, contexts, mapping },
|
|
153
168
|
});
|
|
154
169
|
});
|
|
@@ -45,19 +45,23 @@ const filterEmptyData = (data) => {
|
|
|
45
45
|
return true;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
const
|
|
48
|
+
const getData = (rawData) =>
|
|
49
|
+
rawData.component_type === "gallery-qb"
|
|
50
|
+
? rawData.ui_components[0].data
|
|
51
|
+
: rawData.data;
|
|
52
|
+
|
|
53
|
+
const extractData = compose(uniq, map(getData));
|
|
49
54
|
|
|
50
55
|
export const allFeedsIsReady = (
|
|
51
56
|
feeds: ZappPipesCollection,
|
|
52
57
|
urls: (ZappDataSourceSource | null)[]
|
|
53
58
|
): boolean =>
|
|
54
|
-
urls
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.every(Boolean);
|
|
59
|
+
urls.every((url) =>
|
|
60
|
+
isNil(url) ? true : url && feeds[url] ? checkFeedIsReady(feeds[url]) : false
|
|
61
|
+
);
|
|
58
62
|
|
|
59
63
|
export const useBatchLoading = (
|
|
60
|
-
|
|
64
|
+
componentsToRender: { data?: ZappDataSource; component_type: string }[],
|
|
61
65
|
options: Options
|
|
62
66
|
) => {
|
|
63
67
|
const dispatch = useDispatch();
|
|
@@ -65,10 +69,18 @@ export const useBatchLoading = (
|
|
|
65
69
|
const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
|
|
66
70
|
const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false);
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
// if first component is gallery-qb, take only one component for initial load
|
|
73
|
+
const takeSize =
|
|
74
|
+
componentsToRender?.[0]?.component_type === "gallery-qb"
|
|
75
|
+
? 1
|
|
76
|
+
: min(
|
|
77
|
+
options.initialBatchSize ?? DEFAULT_BATCH_SIZE,
|
|
78
|
+
componentsToRender.length
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const takeBatchSize = React.useCallback(take(takeSize), [takeSize]);
|
|
71
82
|
|
|
83
|
+
// Prepare inflated url for datasource
|
|
72
84
|
const getUrl = React.useCallback(
|
|
73
85
|
(data) => {
|
|
74
86
|
const mappedFeed = data.mapping
|
|
@@ -88,13 +100,16 @@ export const useBatchLoading = (
|
|
|
88
100
|
[entryContext, screenContext, searchContext]
|
|
89
101
|
);
|
|
90
102
|
|
|
91
|
-
|
|
103
|
+
// components for initial load
|
|
104
|
+
const batchComponents = takeBatchSize(componentsToRender);
|
|
92
105
|
|
|
106
|
+
// contains datasources from batch components loading
|
|
93
107
|
const feedData: ZappDataSource[] = React.useMemo(
|
|
94
|
-
() => extractData(
|
|
95
|
-
[
|
|
108
|
+
() => extractData(batchComponents).filter(filterEmptyData),
|
|
109
|
+
[batchComponents]
|
|
96
110
|
);
|
|
97
111
|
|
|
112
|
+
// contains inflated urls from batch components loading
|
|
98
113
|
const feedUrls: ZappDataSourceSource[] = React.useMemo(
|
|
99
114
|
() =>
|
|
100
115
|
feedData.map((data) => {
|
|
@@ -102,34 +117,43 @@ export const useBatchLoading = (
|
|
|
102
117
|
|
|
103
118
|
return mappedFeedUrl;
|
|
104
119
|
}),
|
|
105
|
-
[]
|
|
120
|
+
[]
|
|
106
121
|
);
|
|
107
122
|
|
|
108
123
|
const feeds = useZappPipesFeeds(feedUrls);
|
|
109
124
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
const feed = feeds[data.source];
|
|
125
|
+
// dispatch loadPipesData for each feed that is not loaded
|
|
126
|
+
const runBatchLoading = React.useCallback(() => {
|
|
127
|
+
batchComponents.forEach((rawData: any) => {
|
|
128
|
+
// 1. get data (dataOf)
|
|
115
129
|
|
|
116
|
-
|
|
130
|
+
const data = getData(rawData);
|
|
117
131
|
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
// 2. filter out empty data filterEmptyData
|
|
133
|
+
if (!filterEmptyData(data)) return false;
|
|
120
134
|
|
|
121
|
-
|
|
122
|
-
const
|
|
135
|
+
// 3. filter out data that is already loaded (feeds[data.source]) & !feedHasData(feed) && !feedIsLoading(feed);
|
|
136
|
+
const feed = feeds[data?.source];
|
|
123
137
|
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
138
|
+
if (!feed || (!feedHasData(feed) && !feedIsLoading(feed))) {
|
|
139
|
+
const mappedFeedUrl = getUrl(data);
|
|
140
|
+
|
|
141
|
+
if (mappedFeedUrl) {
|
|
142
|
+
// 4. load data
|
|
143
|
+
return dispatch(
|
|
144
|
+
loadPipesData(mappedFeedUrl, { riverId: options.riverId })
|
|
145
|
+
);
|
|
146
|
+
}
|
|
128
147
|
}
|
|
129
148
|
});
|
|
130
|
-
}, []);
|
|
149
|
+
}, [feedUrls]);
|
|
150
|
+
|
|
151
|
+
React.useEffect(() => {
|
|
152
|
+
runBatchLoading();
|
|
153
|
+
}, []);
|
|
131
154
|
|
|
132
155
|
React.useEffect(() => {
|
|
156
|
+
// check if all feeds are ready and set hasEverBeenReady to true
|
|
133
157
|
if (allFeedsIsReady(feeds, feedUrls) && !hasEverBeenReady) {
|
|
134
158
|
setHasEverBeenReady(true);
|
|
135
159
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// @ts-ignore
|
|
2
1
|
import { RequestBuilder } from "@applicaster/zapp-pipes-v2-client";
|
|
3
|
-
import
|
|
4
|
-
import { useInflatedUrl } from "
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { useInflatedUrl } from "@applicaster/zapp-react-native-utils/reactHooks/feed/useInflatedUrl";
|
|
5
4
|
import { reactHooksLogger } from "../logger";
|
|
6
5
|
|
|
7
6
|
export const logger = reactHooksLogger.addSubsystem("build-pipes-url");
|
|
@@ -15,18 +14,16 @@ type PipesBuildRequestObject = {
|
|
|
15
14
|
export function useBuildPipesUrl({
|
|
16
15
|
url,
|
|
17
16
|
mapping = {},
|
|
17
|
+
requestBuilder = new RequestBuilder(),
|
|
18
18
|
}): PipesBuildRequestObject {
|
|
19
|
-
const [requestParams, setRequestParams] = useState<object | null>(null);
|
|
20
|
-
const [error, setError] = useState(null);
|
|
19
|
+
const [requestParams, setRequestParams] = React.useState<object | null>(null);
|
|
21
20
|
|
|
21
|
+
const [error, setError] = React.useState(null);
|
|
22
22
|
const inflatedUrl = useInflatedUrl({ feedUrl: url, mapping });
|
|
23
23
|
|
|
24
|
-
const requestBuilder = new RequestBuilder();
|
|
25
|
-
|
|
26
24
|
const buildRequest = async (_url) => {
|
|
27
25
|
try {
|
|
28
26
|
const result = await requestBuilder.build(_url);
|
|
29
|
-
|
|
30
27
|
setRequestParams(result);
|
|
31
28
|
} catch (e) {
|
|
32
29
|
setError(e);
|
|
@@ -34,7 +31,7 @@ export function useBuildPipesUrl({
|
|
|
34
31
|
}
|
|
35
32
|
};
|
|
36
33
|
|
|
37
|
-
useEffect(() => {
|
|
34
|
+
React.useEffect(() => {
|
|
38
35
|
if (inflatedUrl && !requestParams) {
|
|
39
36
|
buildRequest(inflatedUrl);
|
|
40
37
|
}
|
|
@@ -42,7 +39,7 @@ export function useBuildPipesUrl({
|
|
|
42
39
|
|
|
43
40
|
return {
|
|
44
41
|
url: inflatedUrl || "",
|
|
45
|
-
requestParams
|
|
42
|
+
requestParams, // TODO should it be: || {} ?
|
|
46
43
|
error: error || undefined,
|
|
47
44
|
};
|
|
48
45
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
2
|
+
import { path, max } from "ramda";
|
|
3
3
|
import { isTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
4
4
|
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
5
|
+
import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
5
6
|
|
|
6
7
|
import { reactHooksLogger } from "../logger";
|
|
7
8
|
import { ReloadDataFunction } from "./useFeedLoader";
|
|
@@ -18,9 +19,8 @@ const minimumRefreshingInterval = 5;
|
|
|
18
19
|
const defaultRefreshInterval = 60;
|
|
19
20
|
|
|
20
21
|
export const useFeedRefresh = ({ reloadData, component }: Props): void => {
|
|
21
|
-
const
|
|
22
|
-
["rules", "enable_data_refreshing"],
|
|
23
|
-
component
|
|
22
|
+
const isDataRefreshingEnabled = isTrue(
|
|
23
|
+
path(["rules", "enable_data_refreshing"], component)
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
const refreshing_interval = toNumberWithDefault(
|
|
@@ -29,10 +29,12 @@ export const useFeedRefresh = ({ reloadData, component }: Props): void => {
|
|
|
29
29
|
);
|
|
30
30
|
|
|
31
31
|
const refreshingIntervalInMilliseconds =
|
|
32
|
-
|
|
32
|
+
max(refreshing_interval, minimumRefreshingInterval) * 1000;
|
|
33
|
+
|
|
34
|
+
const isScreenActive = useIsScreenActive();
|
|
33
35
|
|
|
34
36
|
React.useEffect(() => {
|
|
35
|
-
if (
|
|
37
|
+
if (isDataRefreshingEnabled && refreshing_interval && isScreenActive) {
|
|
36
38
|
if (refreshing_interval < minimumRefreshingInterval) {
|
|
37
39
|
feedRefreshLogger.warning({
|
|
38
40
|
message: `You set your feed refresh interval to ${refreshing_interval} when minimum value is ${minimumRefreshingInterval}seconds.Your feed will refresh after ${minimumRefreshingInterval}seconds.`,
|
|
@@ -59,5 +61,5 @@ export const useFeedRefresh = ({ reloadData, component }: Props): void => {
|
|
|
59
61
|
clearInterval(refreshInterval);
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
|
-
}, [reloadData]);
|
|
64
|
+
}, [reloadData, isScreenActive]);
|
|
63
65
|
};
|