@applicaster/zapp-react-native-utils 13.0.0-alpha.5771418585 → 13.0.0-alpha.5928753399
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/currentScreenProperities.ts +6 -4
- package/appUtils/HooksManager/index.ts +1 -1
- package/appUtils/focusManager/index.ts +1 -10
- package/appUtils/keyCodes/index.ts +11 -7
- package/appUtils/orientationHelper.ts +11 -13
- package/appUtils/playerManager/index.ts +1 -1
- package/appUtils/playerManager/player.ts +31 -20
- package/appUtils/playerManager/playerNative.ts +23 -8
- package/equalUtils/__tests__/isEqual/combination.test.ts +263 -0
- package/equalUtils/index.ts +41 -29
- package/focusManager/index.ts +41 -4
- package/functionUtils/index.ts +21 -8
- package/manifestUtils/defaultManifestConfigurations/player.js +8 -0
- package/manifestUtils/fonts/index.js +5 -6
- package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
- package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +875 -0
- package/manifestUtils/sharedConfiguration/screenPicker/utils.js +130 -0
- package/numberUtils/index.ts +7 -5
- package/package.json +4 -10
- package/playerUtils/configurationGenerator.ts +8 -0
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +5 -7
- package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +6 -17
- package/reactHooks/navigation/index.ts +6 -4
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +13 -40
- package/reactHooks/player/TVSeekControlller/TapSeekController.ts +3 -10
- package/reactUtils/__tests__/reactUtils.test.js +1 -19
- package/reactUtils/createContext/__tests__/{index.test.js → index.test.ts} +2 -2
- package/reactUtils/createContext/helpers.ts +6 -1
- package/reactUtils/createContext/index.ts +11 -25
- package/reactUtils/index.ts +0 -11
- package/services/js2native.ts +673 -0
- package/time/BackgroundTimer.ts +55 -0
- package/time/Timer.ts +6 -6
- package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
- /package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const defaultPlatforms = {
|
|
2
|
+
tvos: "tvOS",
|
|
3
|
+
android_tv: "Android TV",
|
|
4
|
+
lg_tv: "LG TV",
|
|
5
|
+
samsung_tv: "Samsung TV",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const global_defaults = {
|
|
9
|
+
fontSize: 20,
|
|
10
|
+
lineHeight: 40,
|
|
11
|
+
letterSpacing: 0.4,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const generateFontConfiguration = (platforms = defaultPlatforms) => {
|
|
15
|
+
const platformEntries = Object.entries(platforms);
|
|
16
|
+
const defaultsEntries = Object.entries(global_defaults);
|
|
17
|
+
const splitCamelCase = (str) => str.replace(/([a-z](?=[A-Z]))/g, "$1_");
|
|
18
|
+
|
|
19
|
+
return platformEntries.reduce((acc, [platformKey, platformValue]) => {
|
|
20
|
+
acc.push(
|
|
21
|
+
...defaultsEntries.reduce((acc2, [valueKey, defaultValue]) => {
|
|
22
|
+
acc2.push({
|
|
23
|
+
type: "number_input",
|
|
24
|
+
label: `${platformValue} ${splitCamelCase(valueKey).replace(
|
|
25
|
+
"_",
|
|
26
|
+
" "
|
|
27
|
+
)}`,
|
|
28
|
+
key: `${platformKey}_${splitCamelCase(valueKey)}`.toLowerCase(),
|
|
29
|
+
initial_value: defaultValue,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return acc2;
|
|
33
|
+
}, [])
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return acc;
|
|
37
|
+
}, []);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const conditional_rule = (key, condition_value) => ({
|
|
41
|
+
key,
|
|
42
|
+
condition_value,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const createConditionalConfig = (rules, matchAll = false) => ({
|
|
46
|
+
rules: matchAll ? "all_conditions" : "conditional",
|
|
47
|
+
conditional_fields: rules.map((field) => conditional_rule(...field)),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const conditional_horizontal_type = createConditionalConfig([
|
|
51
|
+
["styles/screen_picker_type", "horizontal"],
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const conditional_vertical_type = createConditionalConfig([
|
|
55
|
+
["styles/screen_picker_type", "vertical"],
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
const conditional_horizontal_type_item_fixed = createConditionalConfig(
|
|
59
|
+
[
|
|
60
|
+
["styles/screen_picker_type", "horizontal"],
|
|
61
|
+
["styles/screen_picker_item_size", "fixed"],
|
|
62
|
+
],
|
|
63
|
+
true
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const conditional_horizontal_type_item_dynamic = createConditionalConfig(
|
|
67
|
+
[
|
|
68
|
+
["styles/screen_picker_type", "horizontal"],
|
|
69
|
+
["screen_picker_horizontal_display_type", "dynamic"],
|
|
70
|
+
],
|
|
71
|
+
true
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const conditional_horizontal_display_type_fixed = createConditionalConfig(
|
|
75
|
+
[
|
|
76
|
+
["styles/screen_picker_type", "horizontal"],
|
|
77
|
+
["styles/screen_picker_horizontal_display_type", "fixed"],
|
|
78
|
+
],
|
|
79
|
+
true
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const conditional_horizontal_asset_on = createConditionalConfig(
|
|
83
|
+
[
|
|
84
|
+
["styles/screen_picker_type", "horizontal"],
|
|
85
|
+
["styles/screen_picker_asset_switch", true],
|
|
86
|
+
],
|
|
87
|
+
true
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const createVariationFields = ({
|
|
91
|
+
prefix = "screen_picker",
|
|
92
|
+
key,
|
|
93
|
+
type = "number_input",
|
|
94
|
+
label = "",
|
|
95
|
+
variants: [a, b],
|
|
96
|
+
}) => {
|
|
97
|
+
return [
|
|
98
|
+
{
|
|
99
|
+
type,
|
|
100
|
+
key: `${prefix}${prefix.length > 0 ? "_" : ""}${
|
|
101
|
+
a?.key ?? "vertical"
|
|
102
|
+
}_${key}`,
|
|
103
|
+
label: a?.label ?? label,
|
|
104
|
+
initial_value: a.initial_value,
|
|
105
|
+
...conditional_vertical_type,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type,
|
|
109
|
+
key: `${prefix}${prefix.length > 0 ? "_" : ""}${
|
|
110
|
+
b.key ?? "horizontal"
|
|
111
|
+
}_${key}`,
|
|
112
|
+
label: b?.label ?? label,
|
|
113
|
+
initial_value: b.initial_value,
|
|
114
|
+
...conditional_horizontal_type,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
generateFontConfiguration,
|
|
121
|
+
createVariationFields,
|
|
122
|
+
createConditionalConfig,
|
|
123
|
+
conditional_rule,
|
|
124
|
+
conditional_horizontal_type,
|
|
125
|
+
conditional_vertical_type,
|
|
126
|
+
conditional_horizontal_type_item_fixed,
|
|
127
|
+
conditional_horizontal_type_item_dynamic,
|
|
128
|
+
conditional_horizontal_display_type_fixed,
|
|
129
|
+
conditional_horizontal_asset_on,
|
|
130
|
+
};
|
package/numberUtils/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { logger } from "@applicaster/zapp-react-native-utils/logger";
|
|
|
3
3
|
|
|
4
4
|
const numberUtilsLogger = logger?.addSubsystem("NumberUtils");
|
|
5
5
|
|
|
6
|
-
export const toNumber = (value:
|
|
6
|
+
export const toNumber = (value: unknown): number | undefined => {
|
|
7
7
|
if (R.isNil(value) || R.isEmpty(value)) {
|
|
8
8
|
return undefined;
|
|
9
9
|
}
|
|
@@ -17,10 +17,12 @@ export const toNumber = (value: any): number | undefined => {
|
|
|
17
17
|
return undefined;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
export const toNumberWithDefault = (
|
|
21
|
-
|
|
20
|
+
export const toNumberWithDefault = (
|
|
21
|
+
defaultValue: number,
|
|
22
|
+
value: unknown
|
|
23
|
+
): number => toNumber(value) ?? defaultValue;
|
|
22
24
|
|
|
23
|
-
export const toNumberWithDefaultZero = (value:
|
|
25
|
+
export const toNumberWithDefaultZero = (value: unknown): number =>
|
|
24
26
|
toNumberWithDefault(0, value);
|
|
25
27
|
|
|
26
28
|
export const requireNumber = (obj, key: string) => {
|
|
@@ -39,7 +41,7 @@ export const requireNumber = (obj, key: string) => {
|
|
|
39
41
|
|
|
40
42
|
export const toFiniteNumberWithDefault = (
|
|
41
43
|
defaultValue: number,
|
|
42
|
-
value:
|
|
44
|
+
value: unknown
|
|
43
45
|
): number => {
|
|
44
46
|
const possibleNumber = toNumberWithDefault(defaultValue, value);
|
|
45
47
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.5928753399",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "jest -w=2",
|
|
9
8
|
"build": "echo skipping hybrid ts build in main packages for now"
|
|
10
9
|
},
|
|
11
10
|
"publishConfig": {
|
|
@@ -28,11 +27,9 @@
|
|
|
28
27
|
},
|
|
29
28
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
29
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "13.0.0-alpha.
|
|
30
|
+
"@applicaster/applicaster-types": "13.0.0-alpha.5928753399",
|
|
32
31
|
"buffer": "^5.2.1",
|
|
33
32
|
"camelize": "^1.0.0",
|
|
34
|
-
"create-react-context": "^0.2.3",
|
|
35
|
-
"create-react-ref": "^0.1.0",
|
|
36
33
|
"dayjs": "^1.11.10",
|
|
37
34
|
"memoizee": "0.4.15",
|
|
38
35
|
"prop-types": "^15.0.0",
|
|
@@ -42,13 +39,10 @@
|
|
|
42
39
|
"@applicaster/zapp-pipes-v2-client": "*",
|
|
43
40
|
"@react-native-community/netinfo": "*",
|
|
44
41
|
"immer": "*",
|
|
45
|
-
"react": "
|
|
46
|
-
"react-native": "
|
|
42
|
+
"react": "*",
|
|
43
|
+
"react-native": "*",
|
|
47
44
|
"uglify-js": "*",
|
|
48
45
|
"validate-color": "*",
|
|
49
46
|
"zustand": "*"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@types/react-native": "0.69.6"
|
|
53
47
|
}
|
|
54
48
|
}
|
|
@@ -55,6 +55,14 @@ function getPlayerConfiguration({ platform }) {
|
|
|
55
55
|
label_tooltip:
|
|
56
56
|
"Set the query params which control the size of the image e.g width, height. This will help to optimize the loading time, when magic background is enabled.",
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
key: "audio_player_background_image_default_color",
|
|
60
|
+
label: "Audio Player Background Image Default Color",
|
|
61
|
+
initial_value: "",
|
|
62
|
+
type: "text_input",
|
|
63
|
+
label_tooltip:
|
|
64
|
+
"Set the default color for the audio player background image if not image or set of gradient colors are not provided.",
|
|
65
|
+
},
|
|
58
66
|
],
|
|
59
67
|
};
|
|
60
68
|
|
|
@@ -35,25 +35,23 @@ const Wrapper = ({ children }: { children: React.ReactChild }) => (
|
|
|
35
35
|
const { useTrackedView } = require("../useTrackedView");
|
|
36
36
|
|
|
37
37
|
describe("useTrackCurrentAutoScrollingElement", () => {
|
|
38
|
-
it("should update position for selected component",
|
|
38
|
+
it("should update position for selected component - onViewportEnter", () => {
|
|
39
39
|
const { result } = renderHook(() => useTrackedView("123"), {
|
|
40
40
|
wrapper: Wrapper,
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
// Fast-forward until all timers have been executed
|
|
44
|
-
jest.advanceTimersByTime(3000);
|
|
45
|
-
|
|
46
43
|
expect(result.current.inViewPort).toBe(false);
|
|
47
44
|
|
|
48
45
|
const mockRect = {
|
|
49
46
|
rect: { left: 1, right: 1, top: 1, bottom: 1 },
|
|
50
47
|
};
|
|
51
48
|
|
|
52
|
-
act(() => {
|
|
53
|
-
result.current.onPositionUpdated(mockRect);
|
|
49
|
+
act(async () => {
|
|
50
|
+
await result.current.onPositionUpdated(mockRect);
|
|
54
51
|
});
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
// Fast-forward until all timers have been executed
|
|
54
|
+
jest.runAllTimers();
|
|
57
55
|
|
|
58
56
|
expect(result.current.inViewPort).toBe(true);
|
|
59
57
|
|
|
@@ -2,6 +2,10 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
import { useScreenTrackedViewPositionsContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext";
|
|
3
3
|
import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";
|
|
4
4
|
import { findTrackedView } from "./utils/findTrackedView";
|
|
5
|
+
import { isNil } from "ramda";
|
|
6
|
+
import { withTimeout } from "@applicaster/zapp-react-native-utils/idleUtils";
|
|
7
|
+
|
|
8
|
+
const TIMEOUT_FOR_DELAY_ON_VIEWPORT_ENTER_EVENT = 500; // ms
|
|
5
9
|
|
|
6
10
|
const getNormalizedRect = (rect, componentId, screenWidth, screenHeight) => {
|
|
7
11
|
if (!rect) {
|
|
@@ -37,9 +41,16 @@ export const useTrackedView = (componentId: string) => {
|
|
|
37
41
|
);
|
|
38
42
|
|
|
39
43
|
const onPositionUpdated = useCallback(
|
|
40
|
-
({ rect }) => {
|
|
44
|
+
async ({ rect }) => {
|
|
41
45
|
const currentPosition = normalizedRect(componentId, rect);
|
|
42
46
|
|
|
47
|
+
// There is an issue on the native side where they are unable to guarantee
|
|
48
|
+
// that the order of onViewportLeave and onViewportEnter events is always correct.
|
|
49
|
+
// This workaround/hack delays the onViewportEnter event to ensure it is received last.
|
|
50
|
+
if (!isNil(currentPosition)) {
|
|
51
|
+
await withTimeout(TIMEOUT_FOR_DELAY_ON_VIEWPORT_ENTER_EVENT);
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
updateComponentsPositions(componentId, currentPosition as any);
|
|
44
55
|
|
|
45
56
|
setInViewPort(!!currentPosition);
|
|
@@ -310,14 +310,14 @@ describe("navigation", () => {
|
|
|
310
310
|
expect(navBarVisible).toBe(false);
|
|
311
311
|
});
|
|
312
312
|
|
|
313
|
-
it("returns false if it's secondary nav bar and
|
|
313
|
+
it("returns false if it's secondary nav bar and show_secondary_level_menu is false", () => {
|
|
314
314
|
const navBarVisible = isNavBarVisible(
|
|
315
315
|
"",
|
|
316
316
|
{
|
|
317
317
|
navigations: [
|
|
318
318
|
{
|
|
319
319
|
styles: {
|
|
320
|
-
|
|
320
|
+
show_secondary_level_menu: false,
|
|
321
321
|
},
|
|
322
322
|
},
|
|
323
323
|
],
|
|
@@ -329,14 +329,14 @@ describe("navigation", () => {
|
|
|
329
329
|
expect(navBarVisible).toBe(false);
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
-
it("returns true if it's secondary nav bar and
|
|
332
|
+
it("returns true if it's secondary nav bar and show_secondary_level_menu is true", () => {
|
|
333
333
|
const navBarVisible = isNavBarVisible(
|
|
334
334
|
"",
|
|
335
335
|
{
|
|
336
336
|
navigations: [
|
|
337
337
|
{
|
|
338
338
|
styles: {
|
|
339
|
-
|
|
339
|
+
show_secondary_level_menu: true,
|
|
340
340
|
},
|
|
341
341
|
},
|
|
342
342
|
],
|
|
@@ -348,14 +348,14 @@ describe("navigation", () => {
|
|
|
348
348
|
expect(navBarVisible).toBe(true);
|
|
349
349
|
});
|
|
350
350
|
|
|
351
|
-
it("returns true if it's first level menu and
|
|
351
|
+
it("returns true if it's first level menu and show_secondary_level_menu is false", () => {
|
|
352
352
|
const navBarVisible = isNavBarVisible(
|
|
353
353
|
"",
|
|
354
354
|
{
|
|
355
355
|
navigations: [
|
|
356
356
|
{
|
|
357
357
|
styles: {
|
|
358
|
-
|
|
358
|
+
show_secondary_level_menu: false,
|
|
359
359
|
},
|
|
360
360
|
},
|
|
361
361
|
],
|
|
@@ -366,16 +366,5 @@ describe("navigation", () => {
|
|
|
366
366
|
|
|
367
367
|
expect(navBarVisible).toBe(true);
|
|
368
368
|
});
|
|
369
|
-
|
|
370
|
-
it("returns true otherwise", () => {
|
|
371
|
-
const navBarVisible = isNavBarVisible(
|
|
372
|
-
"",
|
|
373
|
-
{ navigations: [] } as any,
|
|
374
|
-
true,
|
|
375
|
-
true
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
expect(navBarVisible).toBe(true);
|
|
379
|
-
});
|
|
380
369
|
});
|
|
381
370
|
});
|
|
@@ -108,11 +108,13 @@ export function isNavBarVisible(
|
|
|
108
108
|
return false;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const
|
|
112
|
-
(
|
|
113
|
-
|
|
111
|
+
const showSecondaryLevel =
|
|
112
|
+
!isTV() ||
|
|
113
|
+
screenData?.navigations?.some(
|
|
114
|
+
(navigation) => navigation.styles?.show_secondary_level_menu
|
|
115
|
+
);
|
|
114
116
|
|
|
115
|
-
if (canGoBack &&
|
|
117
|
+
if (canGoBack && !showSecondaryLevel) {
|
|
116
118
|
return false;
|
|
117
119
|
}
|
|
118
120
|
|
|
@@ -23,7 +23,7 @@ export enum SEEK_STRATEGY {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const CONFIG_TAP_SEEK = {
|
|
26
|
-
CLICKS_BEFORE_PAUSE:
|
|
26
|
+
CLICKS_BEFORE_PAUSE: 0,
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const CONFIG_LONG_PRESS = {
|
|
@@ -93,8 +93,6 @@ export class TVSeekController
|
|
|
93
93
|
this.isPlayerInitiallyPaused = this.playerController.isPaused();
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
log_debug(`handleStartSeek: seekType: ${seekType}, initiallyPaused: ${this.isPlayerInitiallyPaused}, isPlayerPaused: ${this.playerController.isPaused()}`);
|
|
97
|
-
|
|
98
96
|
this.clearLongPressThresholdTimeout();
|
|
99
97
|
this.clearSeekCompleteTimeout();
|
|
100
98
|
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
@@ -107,36 +105,27 @@ export class TVSeekController
|
|
|
107
105
|
};
|
|
108
106
|
|
|
109
107
|
onForwardPress = (): boolean => {
|
|
110
|
-
log_debug("onForwardPress:");
|
|
111
|
-
|
|
112
108
|
this.handleStartSeek(SEEK_TYPE.FORWARD);
|
|
113
109
|
|
|
114
110
|
return true;
|
|
115
111
|
};
|
|
116
112
|
|
|
117
113
|
onRewindPress = (): boolean => {
|
|
118
|
-
log_debug("onRewindPress:");
|
|
119
|
-
|
|
120
114
|
this.handleStartSeek(SEEK_TYPE.REWIND);
|
|
121
115
|
|
|
122
116
|
return true;
|
|
123
117
|
};
|
|
124
118
|
|
|
125
119
|
onSeekEnd = () => {
|
|
126
|
-
log_debug("onSeekEnd: Seek ended");
|
|
127
120
|
this.clearLongPressThresholdTimeout();
|
|
128
121
|
this.currentSeekController.onSeekEnd();
|
|
129
122
|
};
|
|
130
123
|
|
|
131
124
|
onRewindPressOut = () => {
|
|
132
|
-
log_debug("onRewindPressOut:");
|
|
133
|
-
|
|
134
125
|
this.onSeekEnd();
|
|
135
126
|
};
|
|
136
127
|
|
|
137
128
|
onForwardPressOut = () => {
|
|
138
|
-
log_debug("onForwardPressOut:");
|
|
139
|
-
|
|
140
129
|
this.onSeekEnd();
|
|
141
130
|
};
|
|
142
131
|
|
|
@@ -149,9 +138,7 @@ export class TVSeekController
|
|
|
149
138
|
};
|
|
150
139
|
|
|
151
140
|
handleDelayedSeek = (offset: number = 0) => {
|
|
152
|
-
|
|
153
|
-
void this.handlePlayerPause();
|
|
154
|
-
}
|
|
141
|
+
this.handlePlayerPause();
|
|
155
142
|
|
|
156
143
|
const currentPos =
|
|
157
144
|
this.delayedSeekPosition === null
|
|
@@ -187,46 +174,36 @@ export class TVSeekController
|
|
|
187
174
|
}
|
|
188
175
|
}
|
|
189
176
|
|
|
190
|
-
log_debug(`handleDelayedSeek: currentPos: ${currentPos} targetPos:${targetPos}, initiallyPaused: ${this.isPlayerInitiallyPaused}`);
|
|
191
|
-
|
|
192
177
|
this.delayedSeekPosition = targetPos;
|
|
193
178
|
this.delayedSeekProgressSubject.next(targetPos);
|
|
194
179
|
};
|
|
195
180
|
|
|
196
|
-
handleSeekTo =
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
log_debug(
|
|
200
|
-
`handleSeekTo: isPlayerInitiallyPaused: ${this.isPlayerInitiallyPaused} CallPlayerPlay: ${this.isPlayerInitiallyPaused ? "NO" : "YES"}`
|
|
201
|
-
);
|
|
181
|
+
handleSeekTo = (newPosition: number) => {
|
|
182
|
+
this.playerController.seekTo(newPosition);
|
|
202
183
|
|
|
203
184
|
if (!this.isPlayerInitiallyPaused) {
|
|
204
|
-
|
|
185
|
+
this.playerController.play();
|
|
205
186
|
}
|
|
206
187
|
};
|
|
207
188
|
|
|
208
|
-
handleSeek =
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
log_debug(
|
|
212
|
-
`handleSeek: isPlayerInitiallyPaused: ${this.isPlayerInitiallyPaused} CallPlayerPlay: ${this.isPlayerInitiallyPaused ? "NO" : "YES"}`
|
|
213
|
-
);
|
|
189
|
+
handleSeek = (offset: number) => {
|
|
190
|
+
this.playerController[this.currentSeekType](offset);
|
|
214
191
|
|
|
215
192
|
if (!this.isPlayerInitiallyPaused) {
|
|
216
|
-
|
|
193
|
+
this.playerController.play();
|
|
217
194
|
}
|
|
218
195
|
};
|
|
219
196
|
|
|
220
197
|
handleSeekComplete = () => {
|
|
221
198
|
this.clearSeekCompleteTimeout();
|
|
222
199
|
|
|
223
|
-
this.seekEndTimeout = setTimeout(
|
|
200
|
+
this.seekEndTimeout = setTimeout(() => {
|
|
224
201
|
if (this.delayedSeekPosition !== null) {
|
|
225
|
-
|
|
202
|
+
this.handleSeekTo(this.delayedSeekPosition);
|
|
226
203
|
}
|
|
227
204
|
|
|
228
205
|
log_debug(
|
|
229
|
-
`handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}
|
|
206
|
+
`handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}. Reset all states and seek position.`
|
|
230
207
|
);
|
|
231
208
|
|
|
232
209
|
this.resetAll();
|
|
@@ -237,16 +214,12 @@ export class TVSeekController
|
|
|
237
214
|
return this.delayedSeekPosition !== null;
|
|
238
215
|
};
|
|
239
216
|
|
|
240
|
-
handlePlayerPause =
|
|
241
|
-
log_debug(
|
|
242
|
-
`handleSeek: handlePlayerPause isPlayerPaused: ${this.playerController.isPaused()}`
|
|
243
|
-
);
|
|
244
|
-
|
|
217
|
+
handlePlayerPause = () => {
|
|
245
218
|
if (this.playerController.isPaused()) {
|
|
246
219
|
return;
|
|
247
220
|
}
|
|
248
221
|
|
|
249
|
-
|
|
222
|
+
this.playerController.pause();
|
|
250
223
|
};
|
|
251
224
|
|
|
252
225
|
clearSeekCompleteTimeout = () => {
|
|
@@ -12,8 +12,6 @@ interface TapPressConfig {
|
|
|
12
12
|
|
|
13
13
|
export class TapSeekController implements ISeekController {
|
|
14
14
|
readonly title = "TapSeek";
|
|
15
|
-
private lastFastSeekExit = 0;
|
|
16
|
-
|
|
17
15
|
constructor(
|
|
18
16
|
private seekControllerDelegate: ISeekControllerDelegate,
|
|
19
17
|
private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
|
|
@@ -25,18 +23,13 @@ export class TapSeekController implements ISeekController {
|
|
|
25
23
|
|
|
26
24
|
const skipTime = this.seekIncrementalStrategy.getSkipTime();
|
|
27
25
|
const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
|
|
28
|
-
const cooldown = Date.now() - this.lastFastSeekExit > 1000;
|
|
29
26
|
|
|
30
27
|
if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
|
|
31
28
|
log_debug(
|
|
32
29
|
`${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
|
|
33
30
|
);
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
this.seekControllerDelegate.handleDelayedSeek(skipTime);
|
|
37
|
-
} else {
|
|
38
|
-
this.seekControllerDelegate.handleSeek(skipTime);
|
|
39
|
-
}
|
|
32
|
+
this.seekControllerDelegate.handleDelayedSeek(skipTime);
|
|
40
33
|
} else {
|
|
41
34
|
log_debug(
|
|
42
35
|
`${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
|
|
@@ -53,8 +46,8 @@ export class TapSeekController implements ISeekController {
|
|
|
53
46
|
};
|
|
54
47
|
|
|
55
48
|
reset = () => {
|
|
56
|
-
log_debug(
|
|
57
|
-
|
|
49
|
+
log_debug("{this.title}: Reset states");
|
|
50
|
+
|
|
58
51
|
this.seekIncrementalStrategy.reset();
|
|
59
52
|
};
|
|
60
53
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
componentDidMount,
|
|
8
8
|
} from "./fixtures";
|
|
9
9
|
|
|
10
|
-
import { attachLifeCycleMethods,
|
|
10
|
+
import { attachLifeCycleMethods, mergeChildrenProps } from "..";
|
|
11
11
|
|
|
12
12
|
describe("attachLifeCycleMethods", () => {
|
|
13
13
|
let addLifeCycleMethodsSpy;
|
|
@@ -41,24 +41,6 @@ describe("attachLifeCycleMethods", () => {
|
|
|
41
41
|
});
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
describe("createReactRef", () => {
|
|
45
|
-
it("calls the React.createRef function if it exists", () => {
|
|
46
|
-
jest.clearAllMocks();
|
|
47
|
-
|
|
48
|
-
const ref = createReactRef();
|
|
49
|
-
expect(ref).toHaveProperty("current");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("calls the polyfill if React.createRef doesn't exist", () => {
|
|
53
|
-
jest.clearAllMocks();
|
|
54
|
-
const originalCreateRef = React.createRef;
|
|
55
|
-
React.createRef = undefined;
|
|
56
|
-
const ref = createReactRef();
|
|
57
|
-
expect(ref).toHaveProperty("current");
|
|
58
|
-
React.createRef = originalCreateRef;
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
44
|
describe("mergeChildrenProps", () => {
|
|
63
45
|
it("returns the cloned children with the merged props", () => {
|
|
64
46
|
const Egg = () => React.createElement("Egg");
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createContext } from "../";
|
|
2
|
+
|
|
1
3
|
jest.mock("react", () => ({
|
|
2
4
|
...jest.requireActual("react"),
|
|
3
5
|
createContext: jest.fn(() => ({
|
|
@@ -6,8 +8,6 @@ jest.mock("react", () => ({
|
|
|
6
8
|
})),
|
|
7
9
|
}));
|
|
8
10
|
|
|
9
|
-
const { createContext } = require("../index");
|
|
10
|
-
|
|
11
11
|
const mockedProvider = jest.fn();
|
|
12
12
|
const mockedWithConsumer = jest.fn();
|
|
13
13
|
const mockedWithProvider = jest.fn();
|
|
@@ -33,7 +33,12 @@ export const getSetterName: (property: string) => string = R.compose(
|
|
|
33
33
|
export function createContextSetters(
|
|
34
34
|
properties: string[],
|
|
35
35
|
component: any,
|
|
36
|
-
options:
|
|
36
|
+
options: {
|
|
37
|
+
stateValidator?: (
|
|
38
|
+
obj: { property: string; value: unknown },
|
|
39
|
+
oldValue: unknown
|
|
40
|
+
) => boolean;
|
|
41
|
+
} = {}
|
|
37
42
|
) {
|
|
38
43
|
const { stateValidator = R.T } = options;
|
|
39
44
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import createReactContext from "create-react-context";
|
|
3
2
|
import * as R from "ramda";
|
|
4
3
|
|
|
5
4
|
import { getSetterName, NOOP } from "./helpers";
|
|
@@ -7,33 +6,20 @@ import { createProvider } from "./provider";
|
|
|
7
6
|
import { createWithConsumerDecorator } from "./withConsumer";
|
|
8
7
|
import { createWithProviderDecorator } from "./withProvider";
|
|
9
8
|
|
|
10
|
-
const reactContextCreator = React.createContext;
|
|
11
|
-
|
|
12
|
-
// Depending on the version of React used, the `createContext` function may not be available
|
|
13
|
-
// if this is the case, we fall back to a polyfill library
|
|
14
|
-
const contextCreatorFn =
|
|
15
|
-
reactContextCreator && typeof reactContextCreator === "function"
|
|
16
|
-
? reactContextCreator
|
|
17
|
-
: createReactContext;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @typedef Context
|
|
21
|
-
* @property {Object} Context.Provider react class component to use as context provider
|
|
22
|
-
* @property {Function} Context.Consumer react functional component to use as context consumer
|
|
23
|
-
* @property {Function} Context.withProvider HOC to apply the context provider
|
|
24
|
-
* @property {function} Context.withConsumer HOC to apply the context consumer. passes the context as props
|
|
25
|
-
* @property {React.Context} Context.ReactContext React context object
|
|
26
|
-
* to the wrapped component
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
9
|
/**
|
|
30
10
|
* Creates a React Context and returns consumer & providers components & decorators to interact
|
|
31
11
|
* with this context.
|
|
32
|
-
* @param
|
|
33
|
-
* @param
|
|
34
|
-
* @returns
|
|
12
|
+
* @param initialContext map of context properties and their initial values
|
|
13
|
+
* @param stateValidator optional predicate to tell whether the state update is valid or not
|
|
14
|
+
* @returns Object containing the context components & decorators
|
|
35
15
|
*/
|
|
36
|
-
export function createContext(
|
|
16
|
+
export function createContext(
|
|
17
|
+
initialContext: Record<string, any>,
|
|
18
|
+
stateValidator: (
|
|
19
|
+
obj: { property: string; value: unknown },
|
|
20
|
+
oldValue: unknown
|
|
21
|
+
) => boolean = R.T
|
|
22
|
+
) {
|
|
37
23
|
// first we extract the properties name, and create setters name for those
|
|
38
24
|
const contextPropertiesNames = R.keys(initialContext);
|
|
39
25
|
const contextSettersNames = R.map(getSetterName, contextPropertiesNames);
|
|
@@ -41,7 +27,7 @@ export function createContext(initialContext, stateValidator = R.T) {
|
|
|
41
27
|
// we create a React Context with the properties & their setters, assigning a noop function
|
|
42
28
|
// temporarily (will be overriden by proper setters in the provider component)
|
|
43
29
|
const ReactContext = R.compose(
|
|
44
|
-
|
|
30
|
+
React.createContext,
|
|
45
31
|
R.merge(initialContext),
|
|
46
32
|
R.zipObj(contextSettersNames),
|
|
47
33
|
R.map(R.always(NOOP))
|