@applicaster/zapp-react-native-utils 12.0.0-alpha.5251355865 → 12.0.0-alpha.5292367215
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/helpers/hooks.ts +1 -1
- package/analyticsUtils/index.tsx +12 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/HooksManager/index.js +3 -1
- 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 -57
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +75 -35
- 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 +185 -0
- package/appUtils/playerManager/conts.ts +6 -0
- package/appUtils/playerManager/index.ts +20 -32
- package/appUtils/playerManager/nativePlayerManager.ts +12 -0
- package/appUtils/playerManager/player.ts +44 -3
- package/appUtils/playerManager/playerFactory.ts +12 -10
- package/appUtils/playerManager/playerNative.ts +15 -15
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +7 -1
- package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
- package/arrayUtils/index.ts +5 -0
- package/booleanUtils/index.ts +4 -4
- package/cellUtils/__tests__/cellUtils.test.ts +9 -20
- package/cellUtils/index.ts +48 -19
- package/dateUtils/index.ts +11 -0
- package/focusManager/index.ts +1 -1
- 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 -19
- package/logger/index.ts +1 -1
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +2 -1
- package/manifestUtils/index.js +5 -1
- package/manifestUtils/secondaryImage/index.js +18 -15
- package/navigationUtils/index.ts +48 -0
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/__tests__/toNumber.test.ts +2 -2
- package/numberUtils/index.ts +9 -18
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +3 -1
- package/playerUtils/imaScriptDom.ts +59 -0
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
- package/reactHooks/cell-click/__tests__/index.test.js +10 -1
- package/reactHooks/cell-click/index.ts +12 -12
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- 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/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +44 -29
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/types/index.ts +2 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
- package/reactHooks/navigation/__mocks__/index.ts +15 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +11 -8
- package/reactHooks/navigation/index.ts +64 -21
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
- 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/screen/useScreenData.ts +1 -1
- package/reactHooks/screen/useTargetScreenData.ts +7 -0
- package/reactHooks/state/useRivers.ts +3 -1
- package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
- package/reactUtils/index.ts +104 -0
- package/riverComponetsMeasurementProvider/index.tsx +2 -2
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +24 -22
- 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
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { cleanup, renderHook } from "@testing-library/react-hooks";
|
|
3
3
|
import { CellTapContext } from "@applicaster/zapp-react-native-ui-components/Contexts/CellTapContext";
|
|
4
|
+
import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
|
|
5
|
+
|
|
4
6
|
import { waitFor } from "@testing-library/react-native";
|
|
5
7
|
|
|
6
8
|
const navigator = {
|
|
7
9
|
getPathname: () => "pathnameMock",
|
|
8
10
|
push: jest.fn(),
|
|
11
|
+
data: {},
|
|
9
12
|
};
|
|
10
13
|
|
|
11
14
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
@@ -42,9 +45,15 @@ const mockProps = {
|
|
|
42
45
|
|
|
43
46
|
const onCellTap = jest.fn();
|
|
44
47
|
|
|
48
|
+
const actionExecutor = {
|
|
49
|
+
handleEntryActions: jest.fn(() => Promise.resolve()),
|
|
50
|
+
};
|
|
51
|
+
|
|
45
52
|
const wrapper = ({ children }) => (
|
|
46
53
|
<CellTapContext.Provider value={onCellTap}>
|
|
47
|
-
{
|
|
54
|
+
<ActionExecutorContext.Provider value={actionExecutor}>
|
|
55
|
+
{children}
|
|
56
|
+
</ActionExecutorContext.Provider>
|
|
48
57
|
</CellTapContext.Provider>
|
|
49
58
|
);
|
|
50
59
|
|
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
12
12
|
import { ZappPipesEntryContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
13
13
|
import { toBooleanWithDefaultTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
import {
|
|
14
|
+
import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
|
|
15
|
+
|
|
16
|
+
import { isFunction, noop } from "../../functionUtils";
|
|
17
17
|
import { useSendAnalyticsOnPress } from "../analytics";
|
|
18
18
|
import { logOnPress, warnEmptyContentType } from "./helpers";
|
|
19
19
|
|
|
@@ -30,12 +30,12 @@ type Props = {
|
|
|
30
30
|
zappPipesData?: ZappPipesData;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export const useCellClick = (
|
|
34
|
-
const { component, zappPipesData } = props;
|
|
33
|
+
export const useCellClick = ({ component, zappPipesData, item }: Props) => {
|
|
35
34
|
const { push, currentRoute } = useNavigation();
|
|
36
35
|
const { pathname } = useRoute();
|
|
37
|
-
|
|
38
|
-
const
|
|
36
|
+
|
|
37
|
+
const onCellTap: Option<Function> = React.useContext(CellTapContext);
|
|
38
|
+
const actionExecutor = React.useContext(ActionExecutorContext);
|
|
39
39
|
|
|
40
40
|
const cellSelectable = toBooleanWithDefaultTrue(
|
|
41
41
|
component?.rules?.component_cells_selectable
|
|
@@ -47,7 +47,7 @@ export const useCellClick = (props: Props) => {
|
|
|
47
47
|
const logTimestamp = useProfilerLogging();
|
|
48
48
|
|
|
49
49
|
const sendAnalyticsOnPress = useSendAnalyticsOnPress(
|
|
50
|
-
|
|
50
|
+
item,
|
|
51
51
|
component,
|
|
52
52
|
zappPipesData
|
|
53
53
|
);
|
|
@@ -56,7 +56,7 @@ export const useCellClick = (props: Props) => {
|
|
|
56
56
|
async (_item?: ZappEntry | any, _index?: number) => {
|
|
57
57
|
// Making sure we are not getting GestureResponderEvent in here.
|
|
58
58
|
const isZappEntry = _item?.id && _item?.type;
|
|
59
|
-
const selectedItem = isZappEntry ? _item :
|
|
59
|
+
const selectedItem = isZappEntry ? _item : item;
|
|
60
60
|
setEntryContext?.(selectedItem);
|
|
61
61
|
|
|
62
62
|
if (!selectedItem?.type?.value) {
|
|
@@ -80,10 +80,10 @@ export const useCellClick = (props: Props) => {
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
} else {
|
|
83
|
-
if (
|
|
84
|
-
onCellTap(selectedItem, _index);
|
|
83
|
+
if (isFunction(onCellTap)) {
|
|
84
|
+
onCellTap?.(selectedItem, _index);
|
|
85
85
|
} else {
|
|
86
|
-
if (currentRoute === pathname || pathname
|
|
86
|
+
if (currentRoute === pathname || pathname.includes("video-modal")) {
|
|
87
87
|
const targetScreen = component?.data?.target;
|
|
88
88
|
|
|
89
89
|
push(
|
|
@@ -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
|
});
|
|
@@ -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
|
});
|
|
@@ -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
|
};
|
|
@@ -9,9 +9,15 @@ import {
|
|
|
9
9
|
|
|
10
10
|
import { inflateString } from "../../stringUtils";
|
|
11
11
|
import { useRoute } from "../navigation";
|
|
12
|
-
import {
|
|
12
|
+
import { getMissingValues } from "../../inflatedUrlUtils";
|
|
13
13
|
import { reactHooksLogger as logger } from "../logger";
|
|
14
14
|
import { isTV } from "../../reactUtils";
|
|
15
|
+
import {
|
|
16
|
+
findEndpointForURL,
|
|
17
|
+
HTTP_METHODS,
|
|
18
|
+
} from "@applicaster/zapp-pipes-v2-client";
|
|
19
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
20
|
+
import { ENDPOINT_TAGS } from "../../types";
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* will match any occurrence in a string of one or more word characters
|
|
@@ -105,24 +111,47 @@ export const getInflatedDataSourceUrl: GetInflatedDataSourceUrl = ({
|
|
|
105
111
|
R.keys(mapping)
|
|
106
112
|
);
|
|
107
113
|
|
|
108
|
-
|
|
109
|
-
const message = "Not enough data to fully inflate url";
|
|
114
|
+
const missingValues = getMissingValues(contexts, string);
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
if (missingValues && missingValues.length > 0) {
|
|
117
|
+
const pipesEndpoints = appStore.get("pipesEndpoints");
|
|
118
|
+
|
|
119
|
+
const endpointURL = findEndpointForURL(
|
|
120
|
+
source,
|
|
121
|
+
pipesEndpoints,
|
|
122
|
+
HTTP_METHODS.GET
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const tags = pipesEndpoints?.[endpointURL]?.tags;
|
|
126
|
+
const allowMissingKeys = tags?.includes(ENDPOINT_TAGS.allow_missing_keys);
|
|
127
|
+
|
|
128
|
+
if (!allowMissingKeys) {
|
|
129
|
+
const message = `Not enough data to fully inflate url: ${missingValues.join(
|
|
130
|
+
","
|
|
131
|
+
)} source: ${source}`;
|
|
132
|
+
|
|
133
|
+
// todo: add js only payload instead
|
|
134
|
+
if (__DEV__) {
|
|
135
|
+
logger.warning({
|
|
136
|
+
message,
|
|
137
|
+
data: { source, contexts, mapping },
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
// todo: log values we've failed to resolve
|
|
141
|
+
logger.warning({
|
|
142
|
+
message,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// return null in order to prevent start fetching data with invalid url
|
|
147
|
+
return null;
|
|
117
148
|
} else {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
149
|
+
logger.debug({
|
|
150
|
+
message: `Ignoring missing values in url because ${
|
|
151
|
+
ENDPOINT_TAGS.allow_missing_keys
|
|
152
|
+
} endpoint tag, keys: ${missingValues.join(",")}`,
|
|
121
153
|
});
|
|
122
154
|
}
|
|
123
|
-
|
|
124
|
-
// return null in order to prevent start fetching data with invalid url
|
|
125
|
-
return null;
|
|
126
155
|
}
|
|
127
156
|
|
|
128
157
|
return inflateString({ string, data: contexts });
|
|
@@ -5,27 +5,34 @@ type Return = (index: number) => void;
|
|
|
5
5
|
|
|
6
6
|
type Predicate = (
|
|
7
7
|
entry: ZappUIComponentProps["zappPipesData"]["data"]["entry"],
|
|
8
|
-
index: number
|
|
8
|
+
index: number,
|
|
9
|
+
numberOfColumns: number
|
|
9
10
|
) => boolean;
|
|
10
11
|
|
|
11
12
|
type Props = {
|
|
12
13
|
ifNeeded: Predicate;
|
|
13
14
|
loadNextData: () => void;
|
|
14
15
|
entry: ZappUIComponentProps["zappPipesData"]["data"]["entry"];
|
|
16
|
+
numberOfColumns?: number;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
const handleLoadNextPage =
|
|
18
20
|
(
|
|
19
|
-
ifNeeded: (
|
|
21
|
+
ifNeeded: (
|
|
22
|
+
entry: ZappEntry[],
|
|
23
|
+
index: number,
|
|
24
|
+
numberOfColumns?: number
|
|
25
|
+
) => boolean,
|
|
20
26
|
loadNextData: () => void,
|
|
21
|
-
entry: ZappEntry[]
|
|
27
|
+
entry: ZappEntry[],
|
|
28
|
+
numberOfColumns
|
|
22
29
|
) =>
|
|
23
30
|
(index: number) => {
|
|
24
31
|
/**
|
|
25
32
|
* Lazy-Loading, invoke nextLoadData if we reach the end of the List
|
|
26
33
|
*/
|
|
27
34
|
|
|
28
|
-
if (ifNeeded(entry, index)) {
|
|
35
|
+
if (ifNeeded(entry, index, numberOfColumns)) {
|
|
29
36
|
loadNextData();
|
|
30
37
|
}
|
|
31
38
|
};
|
|
@@ -34,10 +41,10 @@ export const useLoadNextPageIfNeeded = ({
|
|
|
34
41
|
ifNeeded,
|
|
35
42
|
loadNextData = noop,
|
|
36
43
|
entry,
|
|
44
|
+
numberOfColumns = 0,
|
|
37
45
|
}: Props): Return => {
|
|
38
|
-
return React.useCallback(
|
|
39
|
-
ifNeeded,
|
|
40
|
-
loadNextData,
|
|
41
|
-
|
|
42
|
-
]);
|
|
46
|
+
return React.useCallback(
|
|
47
|
+
handleLoadNextPage(ifNeeded, loadNextData, entry, numberOfColumns),
|
|
48
|
+
[ifNeeded, loadNextData, entry, numberOfColumns]
|
|
49
|
+
);
|
|
43
50
|
};
|