@applicaster/zapp-react-native-utils 12.0.0-rc.99 → 12.0.2-alpha.1882692414
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/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/accessibilityManager/hooks.ts +16 -7
- package/appUtils/accessibilityManager/index.ts +33 -28
- package/appUtils/platform/platformUtils.ts +35 -2
- package/appUtils/playerManager/index.ts +9 -27
- package/appUtils/playerManager/playerFactory.ts +5 -4
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
- package/appUtils/playerManager/playerNative.ts +1 -1
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -1
- package/appUtils/playerManager/usePlayerState.tsx +19 -34
- package/arrayUtils/index.ts +11 -0
- package/cellUtils/index.ts +38 -5
- package/focusManager/FocusManager.ts +20 -5
- package/focusManager/__tests__/FocusManager.test.ts +0 -1
- package/functionUtils/index.ts +38 -0
- package/index.d.ts +15 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
- package/manifestUtils/keys.js +2 -1
- package/navigationUtils/index.ts +1 -1
- 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 +20 -0
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +120 -0
- package/playerUtils/imaScriptDom.ts +59 -0
- package/reactHooks/cell-click/index.ts +17 -3
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -2
- package/reactHooks/feed/useBatchLoading.ts +52 -28
- package/reactHooks/feed/useFeedLoader.tsx +11 -7
- package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
- package/reactHooks/navigation/index.ts +1 -1
- package/reactHooks/player/useAutoSeek.tsx +9 -7
- package/reactHooks/resolvers/useCellResolver.ts +5 -1
- package/reactHooks/useConfiguration.ts +2 -2
- package/reactHooks/utils/index.ts +13 -0
- package/reactUtils/index.ts +1 -2
- package/services/localStorageServiceSync.web.ts +48 -0
- package/services/sessionStorageServiceSync.web.ts +32 -0
- package/services/storageServiceSync.web.ts +24 -0
- package/services/storageWebUtils.web.ts +10 -0
- package/testUtils/index.tsx +42 -9
|
@@ -1798,6 +1798,30 @@ function getPlayerConfiguration({ platform }) {
|
|
|
1798
1798
|
key: "menu_header_samsung_letter_spacing",
|
|
1799
1799
|
initial_value: 0,
|
|
1800
1800
|
},
|
|
1801
|
+
{
|
|
1802
|
+
type: "vizio_font_selector",
|
|
1803
|
+
label: "Vizio Font Family",
|
|
1804
|
+
key: "menu_header_vizio_font_family",
|
|
1805
|
+
initial_value: "Ubuntu-Bold",
|
|
1806
|
+
},
|
|
1807
|
+
{
|
|
1808
|
+
type: "number_input",
|
|
1809
|
+
label: "Vizio Font Size",
|
|
1810
|
+
key: "menu_header_vizio_font_size",
|
|
1811
|
+
initial_value: 30,
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
type: "number_input",
|
|
1815
|
+
label: "Vizio Line Height",
|
|
1816
|
+
key: "menu_header_vizio_line_height",
|
|
1817
|
+
initial_value: 60,
|
|
1818
|
+
},
|
|
1819
|
+
{
|
|
1820
|
+
type: "number_input",
|
|
1821
|
+
label: "Vizio Letter Spacing",
|
|
1822
|
+
key: "menu_header_vizio_letter_spacing",
|
|
1823
|
+
initial_value: 0,
|
|
1824
|
+
},
|
|
1801
1825
|
]
|
|
1802
1826
|
),
|
|
1803
1827
|
// Captions Menu - Item Text Label
|
|
@@ -1877,6 +1901,30 @@ function getPlayerConfiguration({ platform }) {
|
|
|
1877
1901
|
key: "menu_item_samsung_letter_spacing",
|
|
1878
1902
|
initial_value: 0,
|
|
1879
1903
|
},
|
|
1904
|
+
{
|
|
1905
|
+
type: "vizio_font_selector",
|
|
1906
|
+
label: "Vizio Font Family",
|
|
1907
|
+
key: "menu_item_vizio_font_family",
|
|
1908
|
+
initial_value: "Ubuntu-Medium",
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
type: "number_input",
|
|
1912
|
+
label: "Vizio Font Size",
|
|
1913
|
+
key: "menu_item_vizio_font_size",
|
|
1914
|
+
initial_value: 25,
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
type: "number_input",
|
|
1918
|
+
label: "Vizio Line Height",
|
|
1919
|
+
key: "menu_item_vizio_line_height",
|
|
1920
|
+
initial_value: 40,
|
|
1921
|
+
},
|
|
1922
|
+
{
|
|
1923
|
+
type: "number_input",
|
|
1924
|
+
label: "Vizio Letter Spacing",
|
|
1925
|
+
key: "menu_item_vizio_letter_spacing",
|
|
1926
|
+
initial_value: 0,
|
|
1927
|
+
},
|
|
1880
1928
|
]
|
|
1881
1929
|
),
|
|
1882
1930
|
// Captions Menu - Item Border
|
|
@@ -2020,6 +2068,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2020
2068
|
key: "samsung_tv_video_player_title_font_size",
|
|
2021
2069
|
initial_value: 58,
|
|
2022
2070
|
},
|
|
2071
|
+
{
|
|
2072
|
+
type: "vizio_font_selector",
|
|
2073
|
+
label: "Vizio Font Family",
|
|
2074
|
+
key: "vizio_video_player_title_font_family",
|
|
2075
|
+
initial_value: "Ubuntu-Bold",
|
|
2076
|
+
},
|
|
2077
|
+
{
|
|
2078
|
+
type: "number_input",
|
|
2079
|
+
label: "Vizio Font Size",
|
|
2080
|
+
key: "vizio_video_player_title_font_size",
|
|
2081
|
+
initial_value: 58,
|
|
2082
|
+
},
|
|
2023
2083
|
{
|
|
2024
2084
|
type: "tvos_font_selector",
|
|
2025
2085
|
label: "tvOS Font Family",
|
|
@@ -2093,6 +2153,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2093
2153
|
key: "samsung_tv_video_player_summary_font_size",
|
|
2094
2154
|
initial_value: 30,
|
|
2095
2155
|
},
|
|
2156
|
+
{
|
|
2157
|
+
type: "vizio_font_selector",
|
|
2158
|
+
label: "Vizio Font Family",
|
|
2159
|
+
key: "vizio_video_player_summary_font_family",
|
|
2160
|
+
initial_value: "Ubuntu-Medium",
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
type: "number_input",
|
|
2164
|
+
label: "Vizio Font Size",
|
|
2165
|
+
key: "vizio_video_player_summary_font_size",
|
|
2166
|
+
initial_value: 30,
|
|
2167
|
+
},
|
|
2096
2168
|
{
|
|
2097
2169
|
type: "tvos_font_selector",
|
|
2098
2170
|
label: "tvOS Font Family",
|
|
@@ -2160,6 +2232,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2160
2232
|
key: "samsung_tv_audio_player_title_font_size",
|
|
2161
2233
|
initial_value: 40,
|
|
2162
2234
|
},
|
|
2235
|
+
{
|
|
2236
|
+
type: "vizio_font_selector",
|
|
2237
|
+
label: "Vizio Font Family",
|
|
2238
|
+
key: "vizio_audio_player_title_font_family",
|
|
2239
|
+
initial_value: "Ubuntu-Bold",
|
|
2240
|
+
},
|
|
2241
|
+
{
|
|
2242
|
+
type: "number_input",
|
|
2243
|
+
label: "Vizio Font Size",
|
|
2244
|
+
key: "vizio_audio_player_title_font_size",
|
|
2245
|
+
initial_value: 40,
|
|
2246
|
+
},
|
|
2163
2247
|
{
|
|
2164
2248
|
type: "tvos_font_selector",
|
|
2165
2249
|
label: "tvOS Font Family",
|
|
@@ -2215,6 +2299,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2215
2299
|
key: "samsung_tv_audio_player_summary_font_size",
|
|
2216
2300
|
initial_value: 22,
|
|
2217
2301
|
},
|
|
2302
|
+
{
|
|
2303
|
+
type: "vizio_font_selector",
|
|
2304
|
+
label: "Vizio Font Family",
|
|
2305
|
+
key: "vizio_audio_player_summary_font_family",
|
|
2306
|
+
initial_value: "Ubuntu-Medium",
|
|
2307
|
+
},
|
|
2308
|
+
{
|
|
2309
|
+
type: "number_input",
|
|
2310
|
+
label: "Vizio Font Size",
|
|
2311
|
+
key: "vizio_audio_player_summary_font_size",
|
|
2312
|
+
initial_value: 22,
|
|
2313
|
+
},
|
|
2218
2314
|
{
|
|
2219
2315
|
type: "tvos_font_selector",
|
|
2220
2316
|
label: "tvOS Font Family",
|
|
@@ -2270,6 +2366,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2270
2366
|
key: "samsung_tv_player_time_font_size",
|
|
2271
2367
|
initial_value: 20,
|
|
2272
2368
|
},
|
|
2369
|
+
{
|
|
2370
|
+
type: "vizio_font_selector",
|
|
2371
|
+
label: "Vizio Font Family",
|
|
2372
|
+
key: "vizio_player_time_font_family",
|
|
2373
|
+
initial_value: "Ubuntu-Medium",
|
|
2374
|
+
},
|
|
2375
|
+
{
|
|
2376
|
+
type: "number_input",
|
|
2377
|
+
label: "Vizio Font Size",
|
|
2378
|
+
key: "vizio_player_time_font_size",
|
|
2379
|
+
initial_value: 20,
|
|
2380
|
+
},
|
|
2273
2381
|
{
|
|
2274
2382
|
type: "android_font_selector",
|
|
2275
2383
|
label: "Android TV Font Family",
|
|
@@ -2325,6 +2433,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2325
2433
|
key: "samsung_tv_audio_player_run_time_font_size",
|
|
2326
2434
|
initial_value: 20,
|
|
2327
2435
|
},
|
|
2436
|
+
{
|
|
2437
|
+
type: "vizio_font_selector",
|
|
2438
|
+
label: "Vizio Font Family",
|
|
2439
|
+
key: "vizio_audio_player_run_time_font_family",
|
|
2440
|
+
initial_value: "Ubuntu-Medium",
|
|
2441
|
+
},
|
|
2442
|
+
{
|
|
2443
|
+
type: "number_input",
|
|
2444
|
+
label: "Vizio Font Size",
|
|
2445
|
+
key: "vizio_audio_player_run_time_font_size",
|
|
2446
|
+
initial_value: 20,
|
|
2447
|
+
},
|
|
2328
2448
|
{
|
|
2329
2449
|
type: "tvos_font_selector",
|
|
2330
2450
|
label: "tvOS Font Family",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createLogger } from "../logger";
|
|
2
|
+
|
|
3
|
+
export const logger = createLogger({
|
|
4
|
+
category: "ImaDomUtils",
|
|
5
|
+
subsystem: "Player/ImaDomUtils",
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const { log_debug, log_error, log_verbose } = logger;
|
|
9
|
+
|
|
10
|
+
const removeElement = (node) => {
|
|
11
|
+
if (node) {
|
|
12
|
+
const parentNode = node.parentNode;
|
|
13
|
+
|
|
14
|
+
if (parentNode) {
|
|
15
|
+
parentNode.removeChild(node);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// TODO: This should probably be loaded to root of project before player init
|
|
21
|
+
export const loadIMAScript = async () => {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
// If ima script is already loaded, resolve
|
|
24
|
+
const imaScript = document.getElementById("ima-script");
|
|
25
|
+
|
|
26
|
+
if (imaScript) {
|
|
27
|
+
log_verbose("loadIMAScript: IMA script already loaded");
|
|
28
|
+
|
|
29
|
+
return resolve(true);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const script = document.createElement("script");
|
|
33
|
+
|
|
34
|
+
if (__DEV__) {
|
|
35
|
+
script.src = "https://imasdk.googleapis.com/js/sdkloader/ima3_debug.js";
|
|
36
|
+
} else {
|
|
37
|
+
script.src = "https://imasdk.googleapis.com/js/sdkloader/ima3.js";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
script.async = true;
|
|
41
|
+
script.id = "ima-script";
|
|
42
|
+
|
|
43
|
+
script.onload = () => {
|
|
44
|
+
removeElement(script);
|
|
45
|
+
log_debug("loadIMAScript: Loaded ima script", script.src);
|
|
46
|
+
resolve(true);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
script.onerror = (_e) => {
|
|
50
|
+
removeElement(script);
|
|
51
|
+
log_error("loadIMAScript: Failed to load ima script", script.src);
|
|
52
|
+
resolve(false);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (document.head) {
|
|
56
|
+
document.head.appendChild(script);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
@@ -30,7 +30,15 @@ type Props = {
|
|
|
30
30
|
zappPipesData?: ZappPipesData;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
type onPressReturnFn =
|
|
34
|
+
| ((_item?: ZappEntry | any, _index?: number) => Promise<void>)
|
|
35
|
+
| (() => void);
|
|
36
|
+
|
|
37
|
+
export const useCellClick = ({
|
|
38
|
+
component,
|
|
39
|
+
zappPipesData,
|
|
40
|
+
item,
|
|
41
|
+
}: Props): onPressReturnFn => {
|
|
34
42
|
const { push, currentRoute } = useNavigation();
|
|
35
43
|
const { pathname } = useRoute();
|
|
36
44
|
|
|
@@ -106,9 +114,15 @@ export const useCellClick = ({ component, zappPipesData, item }: Props) => {
|
|
|
106
114
|
]
|
|
107
115
|
);
|
|
108
116
|
|
|
117
|
+
const onPressRef = React.useRef<onPressReturnFn>();
|
|
118
|
+
|
|
109
119
|
if (!cellSelectable) {
|
|
110
|
-
|
|
120
|
+
// update ref with dummy function
|
|
121
|
+
onPressRef.current = noop;
|
|
122
|
+
} else {
|
|
123
|
+
// update ref with the latest onPress function
|
|
124
|
+
onPressRef.current = onPress;
|
|
111
125
|
}
|
|
112
126
|
|
|
113
|
-
return
|
|
127
|
+
return React.useCallback(onPressRef.current, []);
|
|
114
128
|
};
|
|
@@ -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", () => {
|
|
@@ -135,7 +135,11 @@ describe("useFeedLoader", () => {
|
|
|
135
135
|
);
|
|
136
136
|
|
|
137
137
|
expect(result.current.data).toBeNull();
|
|
138
|
-
|
|
138
|
+
|
|
139
|
+
expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
|
|
140
|
+
clearCache: true,
|
|
141
|
+
riverId: undefined,
|
|
142
|
+
});
|
|
139
143
|
|
|
140
144
|
const store2 = mockStore({
|
|
141
145
|
plugins: [],
|
|
@@ -172,7 +176,11 @@ describe("useFeedLoader", () => {
|
|
|
172
176
|
);
|
|
173
177
|
|
|
174
178
|
expect(result.current.data).toBeNull();
|
|
175
|
-
|
|
179
|
+
|
|
180
|
+
expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
|
|
181
|
+
clearCache: true,
|
|
182
|
+
riverId: undefined,
|
|
183
|
+
});
|
|
176
184
|
|
|
177
185
|
const store2 = mockStore({
|
|
178
186
|
plugins: [],
|
|
@@ -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
|
}
|