@applicaster/zapp-react-native-utils 12.0.0-alpha.4220424021 → 12.0.0-alpha.4223432180
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/appUtils/accessibilityManager/index.ts +1 -12
- package/appUtils/platform/platformUtils.ts +35 -2
- package/arrayUtils/index.ts +11 -0
- package/focusManager/FocusManager.ts +20 -5
- package/focusManager/__tests__/FocusManager.test.ts +0 -1
- package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
- package/manifestUtils/keys.js +2 -1
- package/navigationUtils/index.ts +1 -1
- 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/reactHooks/cell-click/__tests__/index.test.js +3 -3
- package/reactHooks/cell-click/index.ts +17 -3
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
- package/reactHooks/feed/useBatchLoading.ts +52 -28
- package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
- package/reactHooks/navigation/index.ts +1 -1
- package/reactHooks/player/useAutoSeek.tsx +9 -7
- package/reactHooks/useConfiguration.ts +2 -2
- package/testUtils/index.tsx +42 -9
|
@@ -85,18 +85,7 @@ export class AccessibilityManager {
|
|
|
85
85
|
textToRead = localizedMessage;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
logger.debug({
|
|
90
|
-
message: `Reading text: ${textToRead}`,
|
|
91
|
-
data: {
|
|
92
|
-
textToRead,
|
|
93
|
-
text,
|
|
94
|
-
keyOfLocalizedText,
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
this.ttsManager.readText(textToRead);
|
|
99
|
-
}
|
|
88
|
+
this.ttsManager && this.ttsManager.readText(textToRead);
|
|
100
89
|
|
|
101
90
|
this.announcements$.next({
|
|
102
91
|
message: textToRead,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { BehaviorSubject } from "rxjs";
|
|
2
2
|
|
|
3
3
|
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
|
|
4
|
+
import {
|
|
5
|
+
isLgPlatform,
|
|
6
|
+
isSamsungPlatform,
|
|
7
|
+
} from "@applicaster/zapp-react-dom-app/App/Loader/utils/platform";
|
|
4
8
|
|
|
5
9
|
import { PLATFORM_KEYS, PLATFORMS } from "./const";
|
|
6
10
|
import { createLogger, utilsLogger } from "../../logger";
|
|
@@ -25,8 +29,7 @@ const { log_debug } = createLogger({
|
|
|
25
29
|
parent: utilsLogger,
|
|
26
30
|
});
|
|
27
31
|
|
|
28
|
-
export const getUserAgent = () =>
|
|
29
|
-
window?.navigator?.userAgent || global?.navigator?.userAgent;
|
|
32
|
+
export const getUserAgent = () => window?.navigator?.userAgent;
|
|
30
33
|
|
|
31
34
|
/** VIZIO UTILS */
|
|
32
35
|
|
|
@@ -39,6 +42,8 @@ export const getUserAgent = () =>
|
|
|
39
42
|
export const isVizioPlatform = () => {
|
|
40
43
|
const userAgent = getUserAgent();
|
|
41
44
|
|
|
45
|
+
if (!userAgent) return false;
|
|
46
|
+
|
|
42
47
|
const isVizioAgent =
|
|
43
48
|
userAgent.includes(PLATFORMS.vizio) ||
|
|
44
49
|
userAgent.includes(PLATFORMS.smartcast) ||
|
|
@@ -178,6 +183,34 @@ export class TTSManager {
|
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
readText(text: string) {
|
|
186
|
+
if (isSamsungPlatform() && window.speechSynthesis) {
|
|
187
|
+
const utterance = new SpeechSynthesisUtterance(text);
|
|
188
|
+
window.speechSynthesis.speak(utterance);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (isLgPlatform() && window.webOS?.service) {
|
|
192
|
+
try {
|
|
193
|
+
window.webOS.service.request("luna://com.webos.service.tts", {
|
|
194
|
+
method: "speak",
|
|
195
|
+
onFailure(error: any) {
|
|
196
|
+
log_debug("There was a failure setting up webOS TTS service", {
|
|
197
|
+
error,
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
onSuccess(response: any) {
|
|
201
|
+
log_debug("webOS TTS service is configured successfully", {
|
|
202
|
+
response,
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
parameters: {
|
|
206
|
+
text,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
log_debug("webOS TTS service error", { error });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
181
214
|
if (!window.VIZIO?.Chromevox) return;
|
|
182
215
|
|
|
183
216
|
window.VIZIO.Chromevox.play(text);
|
package/arrayUtils/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable unused-imports/no-unused-vars */
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
+
import { invariant } from "@applicaster/zapp-react-native-utils/errorUtils";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* shifts an array by the given offset. a negative offset will take items
|
|
@@ -100,3 +101,13 @@ export const makeListOf = (value: unknown, size: number): number[] => {
|
|
|
100
101
|
export function isFilledArray(value: unknown): boolean {
|
|
101
102
|
return R.is(Array, value) && R.length(value) > 0;
|
|
102
103
|
}
|
|
104
|
+
|
|
105
|
+
// get random item from the list
|
|
106
|
+
export const sample = (xs: unknown[]): unknown => {
|
|
107
|
+
invariant(R.is(Array, xs), `input value is not a array: ${xs}`);
|
|
108
|
+
invariant(isFilledArray(xs), `input array is empty: ${xs}`);
|
|
109
|
+
|
|
110
|
+
const index = Math.floor(Math.random() * xs.length);
|
|
111
|
+
|
|
112
|
+
return xs[index];
|
|
113
|
+
};
|
|
@@ -18,6 +18,7 @@ class FocusManager {
|
|
|
18
18
|
private static instance: FocusManager;
|
|
19
19
|
|
|
20
20
|
private _focusedId: string | null = null;
|
|
21
|
+
private _prevFocusedId: string | null = null;
|
|
21
22
|
public previousNavigationDirection: FocusManager.Android.NavDir = null;
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -47,10 +48,22 @@ class FocusManager {
|
|
|
47
48
|
return focusedRef?.current;
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
get prevFocused() {
|
|
52
|
+
const focusedRef = this.prevFocusedId
|
|
53
|
+
? FocusManager.findFocusable(this.prevFocusedId)
|
|
54
|
+
: { current: null };
|
|
55
|
+
|
|
56
|
+
return focusedRef?.current;
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
get focusedId() {
|
|
51
60
|
return FocusManager.instance._focusedId;
|
|
52
61
|
}
|
|
53
62
|
|
|
63
|
+
get prevFocusedId() {
|
|
64
|
+
return FocusManager.instance._prevFocusedId;
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
public static getInstance(): FocusManager {
|
|
55
68
|
if (!FocusManager.instance) {
|
|
56
69
|
FocusManager.instance = new FocusManager();
|
|
@@ -151,9 +164,8 @@ class FocusManager {
|
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
private setFocusLocal(nextFocus: FocusManager.TouchableReactRef) {
|
|
167
|
+
FocusManager.instance._prevFocusedId = FocusManager.instance._focusedId;
|
|
154
168
|
FocusManager.instance._focusedId = nextFocus?.current?.props?.id ?? null;
|
|
155
|
-
|
|
156
|
-
this.eventHandler?.invokeHandler?.(FOCUS_EVENTS.FOCUS, nextFocus);
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
private setPreviousNavigationDirection(
|
|
@@ -217,8 +229,11 @@ class FocusManager {
|
|
|
217
229
|
|
|
218
230
|
FocusManager.instance.setFocus(nextNextFocus, options);
|
|
219
231
|
} else {
|
|
220
|
-
FocusManager.instance.blurPrevious(options);
|
|
221
232
|
FocusManager.instance.setFocusLocal(nextFocus);
|
|
233
|
+
|
|
234
|
+
FocusManager.instance.blurPrevious(options);
|
|
235
|
+
this.eventHandler?.invokeHandler?.(FOCUS_EVENTS.FOCUS, nextFocus);
|
|
236
|
+
|
|
222
237
|
FocusManager.instance.setPreviousNavigationDirection(options ?? null);
|
|
223
238
|
|
|
224
239
|
nextFocus?.current?.onFocus?.(nextFocus.current, options ?? {});
|
|
@@ -227,8 +242,8 @@ class FocusManager {
|
|
|
227
242
|
|
|
228
243
|
blurPrevious(options?: FocusManager.Android.CallbackOptions) {
|
|
229
244
|
if (options) {
|
|
230
|
-
FocusManager.instance.
|
|
231
|
-
FocusManager.instance.
|
|
245
|
+
FocusManager.instance.prevFocused?.onBlur?.(
|
|
246
|
+
FocusManager.instance.prevFocused,
|
|
232
247
|
options
|
|
233
248
|
);
|
|
234
249
|
}
|
|
@@ -156,7 +156,6 @@ describe("FocusManager", () => {
|
|
|
156
156
|
child1.current.onBlur.mockClear();
|
|
157
157
|
focusManager.setFocus(child1.current.props.id);
|
|
158
158
|
focusManager.blurPrevious({ direction: "down" });
|
|
159
|
-
expect(child1.current.onBlur).toBeCalled();
|
|
160
159
|
});
|
|
161
160
|
|
|
162
161
|
it("focusManager - focus shouldn't change if nextFocus has blockFocus", () => {
|
|
@@ -18,6 +18,7 @@ function getDevice(platform) {
|
|
|
18
18
|
case "lg_tv":
|
|
19
19
|
case "samsung_tv":
|
|
20
20
|
case "web":
|
|
21
|
+
case "vizio":
|
|
21
22
|
return "tv";
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -1795,6 +1796,30 @@ function getPlayerConfiguration({ platform }) {
|
|
|
1795
1796
|
key: "menu_header_samsung_letter_spacing",
|
|
1796
1797
|
initial_value: 0,
|
|
1797
1798
|
},
|
|
1799
|
+
{
|
|
1800
|
+
type: "vizio_font_selector",
|
|
1801
|
+
label: "Vizio Font Family",
|
|
1802
|
+
key: "menu_header_vizio_font_family",
|
|
1803
|
+
initial_value: "SmsungOneUI-Bold",
|
|
1804
|
+
},
|
|
1805
|
+
{
|
|
1806
|
+
type: "number_input",
|
|
1807
|
+
label: "Vizio Font Size",
|
|
1808
|
+
key: "menu_header_vizio_font_size",
|
|
1809
|
+
initial_value: 30,
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
type: "number_input",
|
|
1813
|
+
label: "Vizio Line Height",
|
|
1814
|
+
key: "menu_header_vizio_line_height",
|
|
1815
|
+
initial_value: 60,
|
|
1816
|
+
},
|
|
1817
|
+
{
|
|
1818
|
+
type: "number_input",
|
|
1819
|
+
label: "Vizio Letter Spacing",
|
|
1820
|
+
key: "menu_header_vizio_letter_spacing",
|
|
1821
|
+
initial_value: 0,
|
|
1822
|
+
},
|
|
1798
1823
|
]
|
|
1799
1824
|
),
|
|
1800
1825
|
// Captions Menu - Item Text Label
|
|
@@ -1874,6 +1899,30 @@ function getPlayerConfiguration({ platform }) {
|
|
|
1874
1899
|
key: "menu_item_samsung_letter_spacing",
|
|
1875
1900
|
initial_value: 0,
|
|
1876
1901
|
},
|
|
1902
|
+
{
|
|
1903
|
+
type: "vizio_font_selector",
|
|
1904
|
+
label: "Vizio Font Family",
|
|
1905
|
+
key: "menu_item_vizio_font_family",
|
|
1906
|
+
initial_value: "SmsungOneUI-Bold",
|
|
1907
|
+
},
|
|
1908
|
+
{
|
|
1909
|
+
type: "number_input",
|
|
1910
|
+
label: "Vizio Font Size",
|
|
1911
|
+
key: "menu_item_vizio_font_size",
|
|
1912
|
+
initial_value: 25,
|
|
1913
|
+
},
|
|
1914
|
+
{
|
|
1915
|
+
type: "number_input",
|
|
1916
|
+
label: "Vizio Line Height",
|
|
1917
|
+
key: "menu_item_vizio_line_height",
|
|
1918
|
+
initial_value: 40,
|
|
1919
|
+
},
|
|
1920
|
+
{
|
|
1921
|
+
type: "number_input",
|
|
1922
|
+
label: "Vizio Letter Spacing",
|
|
1923
|
+
key: "menu_item_vizio_letter_spacing",
|
|
1924
|
+
initial_value: 0,
|
|
1925
|
+
},
|
|
1877
1926
|
]
|
|
1878
1927
|
),
|
|
1879
1928
|
// Captions Menu - Item Border
|
|
@@ -2017,6 +2066,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2017
2066
|
key: "samsung_tv_video_player_title_font_size",
|
|
2018
2067
|
initial_value: 64,
|
|
2019
2068
|
},
|
|
2069
|
+
{
|
|
2070
|
+
type: "vizio_font_selector",
|
|
2071
|
+
label: "Vizio Font Family",
|
|
2072
|
+
key: "vizio_video_player_title_font_family",
|
|
2073
|
+
initial_value: "Ubuntu-Bold",
|
|
2074
|
+
},
|
|
2075
|
+
{
|
|
2076
|
+
type: "number_input",
|
|
2077
|
+
label: "Vizio Font Size",
|
|
2078
|
+
key: "vizio_video_player_title_font_size",
|
|
2079
|
+
initial_value: 64,
|
|
2080
|
+
},
|
|
2020
2081
|
{
|
|
2021
2082
|
type: "tvos_font_selector",
|
|
2022
2083
|
label: "tvOS Font Family",
|
|
@@ -2090,6 +2151,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2090
2151
|
key: "samsung_tv_video_player_summary_font_size",
|
|
2091
2152
|
initial_value: 30,
|
|
2092
2153
|
},
|
|
2154
|
+
{
|
|
2155
|
+
type: "vizio_font_selector",
|
|
2156
|
+
label: "Vizio Font Family",
|
|
2157
|
+
key: "vizio_video_player_summary_font_family",
|
|
2158
|
+
initial_value: "Ubuntu-Bold",
|
|
2159
|
+
},
|
|
2160
|
+
{
|
|
2161
|
+
type: "number_input",
|
|
2162
|
+
label: "Vizio Font Size",
|
|
2163
|
+
key: "vizio_video_player_summary_font_size",
|
|
2164
|
+
initial_value: 30,
|
|
2165
|
+
},
|
|
2093
2166
|
{
|
|
2094
2167
|
type: "tvos_font_selector",
|
|
2095
2168
|
label: "tvOS Font Family",
|
|
@@ -2157,6 +2230,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2157
2230
|
key: "samsung_tv_audio_player_title_font_size",
|
|
2158
2231
|
initial_value: 38,
|
|
2159
2232
|
},
|
|
2233
|
+
{
|
|
2234
|
+
type: "vizio_font_selector",
|
|
2235
|
+
label: "Vizio Font Family",
|
|
2236
|
+
key: "vizio_tv_audio_player_title_font_family",
|
|
2237
|
+
initial_value: "Ubuntu-Bold",
|
|
2238
|
+
},
|
|
2239
|
+
{
|
|
2240
|
+
type: "number_input",
|
|
2241
|
+
label: "Vizio Font Size",
|
|
2242
|
+
key: "vizio_audio_player_title_font_size",
|
|
2243
|
+
initial_value: 38,
|
|
2244
|
+
},
|
|
2160
2245
|
{
|
|
2161
2246
|
type: "tvos_font_selector",
|
|
2162
2247
|
label: "tvOS Font Family",
|
|
@@ -2212,6 +2297,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2212
2297
|
key: "samsung_tv_audio_player_summary_font_size",
|
|
2213
2298
|
initial_value: 20,
|
|
2214
2299
|
},
|
|
2300
|
+
{
|
|
2301
|
+
type: "vizio_font_selector",
|
|
2302
|
+
label: "Vizio Font Family",
|
|
2303
|
+
key: "vizio_audio_player_summary_font_family",
|
|
2304
|
+
initial_value: "Ubuntu-Bold",
|
|
2305
|
+
},
|
|
2306
|
+
{
|
|
2307
|
+
type: "number_input",
|
|
2308
|
+
label: "Vizio Font Size",
|
|
2309
|
+
key: "vizio_audio_player_summary_font_size",
|
|
2310
|
+
initial_value: 20,
|
|
2311
|
+
},
|
|
2215
2312
|
{
|
|
2216
2313
|
type: "tvos_font_selector",
|
|
2217
2314
|
label: "tvOS Font Family",
|
|
@@ -2267,6 +2364,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2267
2364
|
key: "samsung_tv_player_time_font_size",
|
|
2268
2365
|
initial_value: 20,
|
|
2269
2366
|
},
|
|
2367
|
+
{
|
|
2368
|
+
type: "vizio_font_selector",
|
|
2369
|
+
label: "Vizio Font Family",
|
|
2370
|
+
key: "vizio_player_time_font_family",
|
|
2371
|
+
initial_value: "Ubuntu-Bold",
|
|
2372
|
+
},
|
|
2373
|
+
{
|
|
2374
|
+
type: "number_input",
|
|
2375
|
+
label: "Vizio Font Size",
|
|
2376
|
+
key: "vizio_player_time_font_size",
|
|
2377
|
+
initial_value: 20,
|
|
2378
|
+
},
|
|
2270
2379
|
{
|
|
2271
2380
|
type: "android_font_selector",
|
|
2272
2381
|
label: "Android TV Font Family",
|
|
@@ -2322,6 +2431,18 @@ function getPlayerConfiguration({ platform }) {
|
|
|
2322
2431
|
key: "samsung_tv_audio_player_run_time_font_size",
|
|
2323
2432
|
initial_value: 20,
|
|
2324
2433
|
},
|
|
2434
|
+
{
|
|
2435
|
+
type: "vizio_font_selector",
|
|
2436
|
+
label: "Vizio Font Family",
|
|
2437
|
+
key: "vizio_audio_player_run_time_font_family",
|
|
2438
|
+
initial_value: "Ubuntu-Bold",
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
type: "number_input",
|
|
2442
|
+
label: "Vizio Font Size",
|
|
2443
|
+
key: "vizio_audio_player_run_time_font_size",
|
|
2444
|
+
initial_value: 20,
|
|
2445
|
+
},
|
|
2325
2446
|
{
|
|
2326
2447
|
type: "tvos_font_selector",
|
|
2327
2448
|
label: "tvOS Font Family",
|
package/manifestUtils/keys.js
CHANGED
|
@@ -17,10 +17,11 @@ const ZAPPIFEST_FIELDS = {
|
|
|
17
17
|
lg: "lg_tv_font_selector",
|
|
18
18
|
samsung: "samsung_font_selector",
|
|
19
19
|
roku: "roku_font_selector",
|
|
20
|
+
vizio: "vizio_font_selector",
|
|
20
21
|
},
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
const TV_PLATFORMS = ["tvOS", "android", "LG", "samsung", "roku"];
|
|
24
|
+
const TV_PLATFORMS = ["tvOS", "android", "LG", "samsung", "roku", "vizio"];
|
|
24
25
|
|
|
25
26
|
const FontPropsForTVPlatform = (platform) => ({
|
|
26
27
|
[`${R.toLower(platform)}FontFamily`]: {
|
package/navigationUtils/index.ts
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { toPositiveNumberWithDefault } from "..";
|
|
2
|
+
|
|
3
|
+
describe("toNumber", () => {
|
|
4
|
+
const DEFAULT = 5;
|
|
5
|
+
|
|
6
|
+
it("return number if input is positive number", () => {
|
|
7
|
+
const inputs = [0, 1, 2, 100, 200.12, Infinity];
|
|
8
|
+
expect.assertions(inputs.length);
|
|
9
|
+
|
|
10
|
+
inputs.forEach((input) => {
|
|
11
|
+
const output = toPositiveNumberWithDefault(DEFAULT, input);
|
|
12
|
+
expect(output).toBe(input);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("return number if input is positive number as string", () => {
|
|
17
|
+
const inputs = ["0", "1", "2", "100", "200.12"];
|
|
18
|
+
expect.assertions(inputs.length);
|
|
19
|
+
|
|
20
|
+
inputs.forEach((input) => {
|
|
21
|
+
const output = toPositiveNumberWithDefault(DEFAULT, input);
|
|
22
|
+
expect(output).toBe(Number(input));
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("return default number if input is negative number", () => {
|
|
27
|
+
const inputs = [-1, -2, -100, -Infinity];
|
|
28
|
+
expect.assertions(inputs.length);
|
|
29
|
+
|
|
30
|
+
inputs.forEach((input) => {
|
|
31
|
+
const output = toPositiveNumberWithDefault(DEFAULT, input);
|
|
32
|
+
expect(output).toBe(DEFAULT);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("return default number if input is negative number as string", () => {
|
|
37
|
+
const inputs = ["-1", "-2", "-100"];
|
|
38
|
+
expect.assertions(inputs.length);
|
|
39
|
+
|
|
40
|
+
inputs.forEach((input) => {
|
|
41
|
+
const output = toPositiveNumberWithDefault(DEFAULT, input);
|
|
42
|
+
expect(output).toBe(DEFAULT);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("return default if input is not a number or infinite", () => {
|
|
47
|
+
const inputs = [
|
|
48
|
+
"vfdvf",
|
|
49
|
+
null,
|
|
50
|
+
undefined,
|
|
51
|
+
NaN,
|
|
52
|
+
"",
|
|
53
|
+
{},
|
|
54
|
+
{ test: 1 },
|
|
55
|
+
[],
|
|
56
|
+
[1],
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
expect.assertions(inputs.length);
|
|
60
|
+
|
|
61
|
+
inputs.forEach((input) => {
|
|
62
|
+
const output = toPositiveNumberWithDefault(DEFAULT, input);
|
|
63
|
+
expect(output).toBe(DEFAULT);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { toPositiveNumberWithDefaultZero } from "..";
|
|
2
|
+
|
|
3
|
+
const DEFAULT = 0;
|
|
4
|
+
|
|
5
|
+
describe("toNumber", () => {
|
|
6
|
+
it("return number if input is positive number", () => {
|
|
7
|
+
const inputs = [0, 1, 2, 100, 200.12];
|
|
8
|
+
expect.assertions(inputs.length);
|
|
9
|
+
|
|
10
|
+
inputs.forEach((input) => {
|
|
11
|
+
const output = toPositiveNumberWithDefaultZero(input);
|
|
12
|
+
expect(output).toBe(input);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("return number if input is positive number as string", () => {
|
|
17
|
+
const inputs = ["0", "1", "2", "100", "200.12"];
|
|
18
|
+
expect.assertions(inputs.length);
|
|
19
|
+
|
|
20
|
+
inputs.forEach((input) => {
|
|
21
|
+
const output = toPositiveNumberWithDefaultZero(input);
|
|
22
|
+
expect(output).toBe(Number(input));
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("return default number if input is negative number", () => {
|
|
27
|
+
const inputs = [-1, -2, -100];
|
|
28
|
+
expect.assertions(inputs.length);
|
|
29
|
+
|
|
30
|
+
inputs.forEach((input) => {
|
|
31
|
+
const output = toPositiveNumberWithDefaultZero(input);
|
|
32
|
+
expect(output).toBe(DEFAULT);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("return default number if input is negative number as string", () => {
|
|
37
|
+
const inputs = ["-1", "-2", "-100"];
|
|
38
|
+
expect.assertions(inputs.length);
|
|
39
|
+
|
|
40
|
+
inputs.forEach((input) => {
|
|
41
|
+
const output = toPositiveNumberWithDefaultZero(input);
|
|
42
|
+
expect(output).toBe(DEFAULT);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("return default if input is not a number or infinite", () => {
|
|
47
|
+
const inputs = [
|
|
48
|
+
"vfdvf",
|
|
49
|
+
null,
|
|
50
|
+
undefined,
|
|
51
|
+
NaN,
|
|
52
|
+
"",
|
|
53
|
+
{},
|
|
54
|
+
{ test: 1 },
|
|
55
|
+
[],
|
|
56
|
+
[1],
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
expect.assertions(inputs.length);
|
|
60
|
+
|
|
61
|
+
inputs.forEach((input) => {
|
|
62
|
+
const output = toPositiveNumberWithDefaultZero(input);
|
|
63
|
+
expect(output).toBe(DEFAULT);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
package/numberUtils/index.ts
CHANGED
|
@@ -66,3 +66,23 @@ export const isZero = (value: unknown): boolean =>
|
|
|
66
66
|
export const isMinusZero = (value: number) => {
|
|
67
67
|
return 1 / value === -Infinity;
|
|
68
68
|
};
|
|
69
|
+
|
|
70
|
+
export const toPositiveNumberWithDefault = (
|
|
71
|
+
defaultValue: number,
|
|
72
|
+
value: unknown
|
|
73
|
+
): number => {
|
|
74
|
+
const possibleNumber = toNumber(value);
|
|
75
|
+
|
|
76
|
+
if (R.isNil(possibleNumber)) {
|
|
77
|
+
return defaultValue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (possibleNumber < 0) {
|
|
81
|
+
return defaultValue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return possibleNumber;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const toPositiveNumberWithDefaultZero = (value: unknown): number =>
|
|
88
|
+
toPositiveNumberWithDefault(0, value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.4223432180",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "12.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "12.0.0-alpha.4223432180",
|
|
32
32
|
"buffer": "^5.2.1",
|
|
33
33
|
"camelize": "^1.0.0",
|
|
34
34
|
"create-react-context": "^0.2.3",
|
|
@@ -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",
|
|
@@ -60,13 +60,13 @@ const wrapper = ({ children }) => (
|
|
|
60
60
|
describe("useCellClick", () => {
|
|
61
61
|
it("returns a function", () => {
|
|
62
62
|
const { result } = renderHook(() => useCellClick(mockProps));
|
|
63
|
-
expect(typeof result.current).toBe("function");
|
|
63
|
+
expect(typeof result.current.current).toBe("function");
|
|
64
64
|
cleanup();
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it("runs default logic", () => {
|
|
68
68
|
const { result } = renderHook(() => useCellClick(mockProps));
|
|
69
|
-
const useCellClickHook = result.current;
|
|
69
|
+
const useCellClickHook = result.current.current;
|
|
70
70
|
|
|
71
71
|
useCellClickHook();
|
|
72
72
|
|
|
@@ -76,7 +76,7 @@ describe("useCellClick", () => {
|
|
|
76
76
|
|
|
77
77
|
it("runs onCellTap", async () => {
|
|
78
78
|
const { result } = renderHook(() => useCellClick(mockProps), { wrapper });
|
|
79
|
-
const useCellClickHook = result.current;
|
|
79
|
+
const useCellClickHook = result.current.current;
|
|
80
80
|
|
|
81
81
|
useCellClickHook();
|
|
82
82
|
|
|
@@ -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): React.MutableRefObject<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 onPressRef;
|
|
114
128
|
};
|
|
@@ -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", () => {
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
2
|
+
import { Dimensions, StatusBar } from "react-native";
|
|
3
|
+
|
|
4
|
+
const mockUsePickFromState = jest.fn();
|
|
5
|
+
const mockUseIsScreenActive = jest.fn();
|
|
6
|
+
const mockGetInitialDimensions = jest.fn();
|
|
7
|
+
const mockGetDeviceInfo = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
|
|
10
|
+
...(jest.requireActual("@applicaster/zapp-react-native-redux/hooks") as {}),
|
|
11
|
+
usePickFromState: mockUsePickFromState,
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
jest.mock("../../../navigation", () => ({
|
|
15
|
+
useIsScreenActive: mockUseIsScreenActive,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock("../helpers", () => ({
|
|
19
|
+
getInitialDimensions: mockGetInitialDimensions,
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock("../../getDeviceInfo", () => ({
|
|
23
|
+
getDeviceInfo: mockGetDeviceInfo,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const { useDimensions } = require("../useDimensions");
|
|
27
|
+
|
|
28
|
+
describe("useDimensions", () => {
|
|
29
|
+
const mockDimensions = { width: 100, height: 200, scale: 1, fontScale: 1 };
|
|
30
|
+
const mockAppData = { someData: "test" };
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
Dimensions.get = jest.fn().mockReturnValue(mockDimensions);
|
|
35
|
+
|
|
36
|
+
Dimensions.addEventListener = jest.fn().mockReturnValue({
|
|
37
|
+
remove: jest.fn(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
mockUsePickFromState.mockReturnValue({ appData: mockAppData });
|
|
41
|
+
mockUseIsScreenActive.mockReturnValue(true);
|
|
42
|
+
mockGetInitialDimensions.mockReturnValue(mockDimensions);
|
|
43
|
+
mockGetDeviceInfo.mockReturnValue({ deviceInfo: "testDeviceInfo" });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("returns correct initial dimensions", () => {
|
|
47
|
+
const { result } = renderHook(() =>
|
|
48
|
+
useDimensions("window", { fullDimensions: false })
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(result.current).toEqual({
|
|
52
|
+
...mockDimensions,
|
|
53
|
+
statusBarHeight: StatusBar.currentHeight,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(mockGetInitialDimensions).toHaveBeenCalledWith("window");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("calls handler on mount", () => {
|
|
60
|
+
renderHook(() => useDimensions("window", { fullDimensions: false }));
|
|
61
|
+
|
|
62
|
+
expect(Dimensions.addEventListener).toHaveBeenCalledWith(
|
|
63
|
+
"change",
|
|
64
|
+
expect.any(Function)
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("calls handler on isActive change", () => {
|
|
69
|
+
const { rerender } = renderHook(() =>
|
|
70
|
+
useDimensions("window", { fullDimensions: false })
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
mockUseIsScreenActive.mockReturnValue(false);
|
|
74
|
+
rerender();
|
|
75
|
+
|
|
76
|
+
expect(Dimensions.addEventListener).toHaveBeenCalledWith(
|
|
77
|
+
"change",
|
|
78
|
+
expect.any(Function)
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("handles fullDimensions option", () => {
|
|
83
|
+
const { result } = renderHook(() =>
|
|
84
|
+
useDimensions("window", { fullDimensions: true })
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(result.current).toEqual({
|
|
88
|
+
...mockDimensions,
|
|
89
|
+
scale: 1,
|
|
90
|
+
fontScale: 1,
|
|
91
|
+
statusBarHeight: StatusBar.currentHeight,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("handles excludeStatusBar option", () => {
|
|
96
|
+
const { result } = renderHook(() =>
|
|
97
|
+
useDimensions("window", { excludeStatusBar: true })
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(result.current.height).toBe(
|
|
101
|
+
mockDimensions.height - StatusBar.currentHeight ?? 0
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("handles rounded option", () => {
|
|
106
|
+
const { result } = renderHook(() =>
|
|
107
|
+
useDimensions("window", { rounded: true })
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(result.current.width).toBe(Math.ceil(mockDimensions.width));
|
|
111
|
+
expect(result.current.height).toBe(Math.ceil(mockDimensions.height));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("handles deviceInfo option", () => {
|
|
115
|
+
const { result } = renderHook(() =>
|
|
116
|
+
useDimensions("window", { deviceInfo: true })
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(result.current.deviceInfo).toMatchObject({
|
|
120
|
+
deviceInfo: "testDeviceInfo",
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("logs deprecation warning when fullDimensions is boolean", () => {
|
|
125
|
+
const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
126
|
+
renderHook(() => useDimensions("window", true));
|
|
127
|
+
|
|
128
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
129
|
+
"Deprecation Warning\nSecond argument is now the options object. {fullDimensions: boolean, ...}"
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
consoleWarnSpy.mockRestore();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
QUICK_BRICK_NAVBAR,
|
|
18
18
|
QUICK_BRICK_CONTENT,
|
|
19
19
|
} from "@applicaster/quick-brick-core/const";
|
|
20
|
-
import { useZappHooksModalState } from "@applicaster/zapp-react-dom-ui-components/Components/ZappHooksModal/
|
|
20
|
+
import { useZappHooksModalState } from "@applicaster/zapp-react-dom-ui-components/Components/ZappHooksModal/hooks/useZappHooksModalState";
|
|
21
21
|
import { ZappHookModalContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
22
22
|
import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
|
|
23
23
|
import { HookModalContextT } from "@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
|
-
import * as R from "ramda";
|
|
3
2
|
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
|
|
4
3
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
5
4
|
import {
|
|
@@ -17,10 +16,6 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
|
|
|
17
16
|
const intervalTimer = React.useRef(null);
|
|
18
17
|
const prevRunning = usePrevious(isRunning);
|
|
19
18
|
|
|
20
|
-
const setIntervalTimer = (cb) => {
|
|
21
|
-
intervalTimer.current = setInterval(cb, ON_HOLD_INTERVAL);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
19
|
const clearIntervalTimer = () => {
|
|
25
20
|
try {
|
|
26
21
|
clearInterval(intervalTimer.current);
|
|
@@ -29,6 +24,11 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
|
|
|
29
24
|
}
|
|
30
25
|
};
|
|
31
26
|
|
|
27
|
+
const setIntervalTimer = (cb) => {
|
|
28
|
+
clearIntervalTimer();
|
|
29
|
+
intervalTimer.current = setInterval(cb, ON_HOLD_INTERVAL);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
32
|
const resetState = () => {
|
|
33
33
|
setIsRunning(false);
|
|
34
34
|
setDirection(null);
|
|
@@ -42,7 +42,7 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
|
|
|
42
42
|
if (!player) return;
|
|
43
43
|
|
|
44
44
|
if (direction) {
|
|
45
|
-
player[direction](
|
|
45
|
+
player[direction](Math.min(MAX_SEEK_TIME, SKIP_TIME_BASE * multiplier));
|
|
46
46
|
resetHUDTimer();
|
|
47
47
|
}
|
|
48
48
|
}, [multiplier, direction]);
|
|
@@ -50,7 +50,9 @@ export const useAutoSeek = (resetHUDTimer = noop) => {
|
|
|
50
50
|
useEffect(() => {
|
|
51
51
|
if (isRunning && isRunning !== prevRunning) {
|
|
52
52
|
setIntervalTimer(() => {
|
|
53
|
-
setMultiplier(
|
|
53
|
+
setMultiplier((prev) => {
|
|
54
|
+
return prev + 1;
|
|
55
|
+
});
|
|
54
56
|
});
|
|
55
57
|
} else if (!isRunning && isRunning !== prevRunning) {
|
|
56
58
|
resetState();
|
|
@@ -67,7 +67,7 @@ export const useConfiguration = ({
|
|
|
67
67
|
if (component) {
|
|
68
68
|
const remappedStylesConfiguration = remapUpdatedKeys(
|
|
69
69
|
pluginManifest,
|
|
70
|
-
component
|
|
70
|
+
component
|
|
71
71
|
);
|
|
72
72
|
|
|
73
73
|
R.forEachObjIndexed((value, key) => {
|
|
@@ -83,7 +83,7 @@ export const useConfiguration = ({
|
|
|
83
83
|
|
|
84
84
|
const styles = populateConfigurationValues(
|
|
85
85
|
pluginManifest?.styles?.fields,
|
|
86
|
-
remappedStylesConfiguration,
|
|
86
|
+
remappedStylesConfiguration.styles,
|
|
87
87
|
skipDefaults
|
|
88
88
|
);
|
|
89
89
|
|
package/testUtils/index.tsx
CHANGED
|
@@ -7,9 +7,11 @@ import { Provider } from "react-redux";
|
|
|
7
7
|
import thunk from "redux-thunk";
|
|
8
8
|
import configureStore from "redux-mock-store";
|
|
9
9
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
10
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
10
11
|
|
|
11
12
|
import { render } from "@testing-library/react-native";
|
|
12
13
|
import { AnalyticsProvider } from "../analyticsUtils";
|
|
14
|
+
import { ThemeContext } from "../theme";
|
|
13
15
|
|
|
14
16
|
export { getByTestId } from "./getByTestId";
|
|
15
17
|
|
|
@@ -59,7 +61,9 @@ const initialState = {
|
|
|
59
61
|
appSettings: {},
|
|
60
62
|
plugins: [],
|
|
61
63
|
appState: { appReady: true, appLaunched: true },
|
|
64
|
+
pluginConfigurations: {},
|
|
62
65
|
appData: { layoutVersion: "v1" },
|
|
66
|
+
zappPipes: {},
|
|
63
67
|
contentTypes: null,
|
|
64
68
|
};
|
|
65
69
|
|
|
@@ -68,20 +72,49 @@ export const WrappedWithProviders = ({
|
|
|
68
72
|
store: storeObj = {},
|
|
69
73
|
}: any) => {
|
|
70
74
|
const _store = configureStore([thunk])(
|
|
71
|
-
R.
|
|
75
|
+
R.mergeDeepRight(initialState, { ...storeObj })
|
|
72
76
|
);
|
|
73
77
|
|
|
78
|
+
appStore.set(_store);
|
|
79
|
+
|
|
74
80
|
return (
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
<ThemeContext.Provider
|
|
82
|
+
value={{
|
|
83
|
+
themes: {
|
|
84
|
+
light: {
|
|
85
|
+
component_margin_top: 0,
|
|
86
|
+
component_margin_bottom: 0,
|
|
87
|
+
component_margin_left: 0,
|
|
88
|
+
component_margin_right: 0,
|
|
89
|
+
component_padding_top: 0,
|
|
90
|
+
component_padding_bottom: 0,
|
|
91
|
+
component_padding_left: 0,
|
|
92
|
+
component_padding_right: 0,
|
|
93
|
+
component_gutter_vertical: 0,
|
|
94
|
+
component_gutter_horizontal: 0,
|
|
95
|
+
component_corner_radius: 0,
|
|
96
|
+
app_background_color: "rgba(0,0,0,0)",
|
|
97
|
+
screen_margin_top: 0,
|
|
98
|
+
screen_margin_bottom: 0,
|
|
99
|
+
component_anchor_point_y: 0,
|
|
100
|
+
assets: "",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
selectedThemeId: "light",
|
|
104
|
+
setSelectedThemeId: () => {},
|
|
79
105
|
}}
|
|
80
106
|
>
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
<SafeAreaProvider
|
|
108
|
+
initialMetrics={{
|
|
109
|
+
insets: { top: 0, left: 0, right: 0, bottom: 0 },
|
|
110
|
+
frame: { x: 0, y: 0, width: 0, height: 0 },
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<Provider store={_store}>
|
|
114
|
+
<AnalyticsProvider>{children}</AnalyticsProvider>
|
|
115
|
+
</Provider>
|
|
116
|
+
</SafeAreaProvider>
|
|
117
|
+
</ThemeContext.Provider>
|
|
85
118
|
);
|
|
86
119
|
};
|
|
87
120
|
|