@applicaster/zapp-react-native-utils 13.0.0-alpha.5428512514 → 13.0.0-alpha.5771418585
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/README.md +0 -10
- package/actionsExecutor/ActionExecutor.ts +16 -9
- package/actionsExecutor/ActionExecutorContext.tsx +47 -7
- package/appUtils/HooksManager/Hook.ts +2 -2
- package/appUtils/HooksManager/types.ts +5 -2
- package/appUtils/accessibilityManager/index.ts +42 -1
- package/appUtils/contextKeysManager/contextResolver.ts +6 -2
- package/appUtils/contextKeysManager/index.ts +2 -2
- package/appUtils/contextKeysManager/utils/index.ts +2 -1
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
- package/appUtils/focusManager/index.ts +11 -3
- package/appUtils/focusManagerAux/utils/index.ts +4 -3
- package/appUtils/platform/const.ts +14 -0
- package/appUtils/platform/platformUtils.ts +47 -4
- package/appUtils/playerManager/player.ts +10 -9
- package/appUtils/playerManager/playerNative.ts +5 -5
- package/equalUtils/index.ts +10 -3
- package/focusManager/FocusManager.ts +11 -1
- package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
- package/idleUtils/index.ts +1 -1
- package/idleUtils/withTimeout.ts +15 -2
- package/logger/const.ts +9 -67
- package/manifestUtils/__tests__/createConfig.test.js +0 -2
- package/navigationUtils/index.ts +3 -3
- package/package.json +2 -2
- package/reactHooks/cell-click/__tests__/index.test.js +5 -0
- package/reactHooks/cell-click/index.ts +7 -1
- package/reactHooks/feed/useFeedLoader.tsx +1 -1
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
- package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
- package/reactHooks/navigation/index.ts +7 -19
- package/reactHooks/navigation/useGetTabBarHeight.ts +1 -3
- package/reactHooks/navigation/useProfilerLogging.ts +15 -0
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +40 -13
- package/reactHooks/player/TVSeekControlller/TapSeekController.ts +10 -3
- package/reactHooks/state/useRefWithInitialValue.ts +14 -0
- package/reactHooks/state/useStateFromSubscribe.ts +23 -0
- package/reactHooks/usePluginConfiguration.ts +1 -1
- package/reactHooks/useSubscriberFor.ts +4 -3
- package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
- package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
- package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +6 -72
- package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
- package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
- package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
- package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
- package/reactUtils/createContext/__tests__/provider.test.js +4 -6
- package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
- package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
- package/screenState/index.ts +24 -10
- package/testUtils/index.tsx +0 -10
- package/theme/index.ts +1 -1
- package/time/Timer.ts +5 -2
- package/types.ts +1 -0
- package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
|
@@ -250,7 +250,17 @@ class FocusManager {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
onDisableFocusChange = (id) => {
|
|
253
|
-
|
|
253
|
+
const isFocused = FocusManager.instance.isFocused(id);
|
|
254
|
+
|
|
255
|
+
const isDescendantFocused = (() => {
|
|
256
|
+
try {
|
|
257
|
+
return FocusManager.instance.isAnyDescendantFocused(id);
|
|
258
|
+
} catch (_e) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
})();
|
|
262
|
+
|
|
263
|
+
if (isFocused || isDescendantFocused) {
|
|
254
264
|
// Move focus to next one
|
|
255
265
|
const nextFocus = FocusManager.instance.focused?.props
|
|
256
266
|
?.nextFocusDown as string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { TestScheduler } from "rxjs/testing";
|
|
2
|
+
|
|
3
|
+
import { withTimeout$ } from "..";
|
|
4
|
+
|
|
5
|
+
describe("withTimeout$", () => {
|
|
6
|
+
let testScheduler: TestScheduler;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
testScheduler = new TestScheduler((actual, expected) => {
|
|
10
|
+
expect(actual).toEqual(expected);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should emit undefined after the specified timeout", () => {
|
|
15
|
+
testScheduler.run(({ expectObservable }) => {
|
|
16
|
+
const timeout = 1000;
|
|
17
|
+
const expectedMarble = "1s (a|)";
|
|
18
|
+
const expectedValues = { a: undefined };
|
|
19
|
+
|
|
20
|
+
const result$ = withTimeout$(timeout);
|
|
21
|
+
|
|
22
|
+
expectObservable(result$).toBe(expectedMarble, expectedValues);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should complete after emitting undefined", () => {
|
|
27
|
+
testScheduler.run(({ expectObservable }) => {
|
|
28
|
+
const timeout = 500;
|
|
29
|
+
const expectedMarble = "500ms (a|)";
|
|
30
|
+
const expectedValues = { a: undefined };
|
|
31
|
+
|
|
32
|
+
const result$ = withTimeout$(timeout);
|
|
33
|
+
|
|
34
|
+
expectObservable(result$).toBe(expectedMarble, expectedValues);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
package/idleUtils/index.ts
CHANGED
package/idleUtils/withTimeout.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { of, Observable } from "rxjs";
|
|
2
|
+
import { delay } from "rxjs/operators";
|
|
3
|
+
|
|
4
|
+
export const withTimeout = (timeout: number): Promise<void> => {
|
|
5
|
+
return new Promise<void>((resolve) => {
|
|
3
6
|
setTimeout(() => {
|
|
4
7
|
resolve(undefined);
|
|
5
8
|
}, timeout);
|
|
6
9
|
});
|
|
7
10
|
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates an observable that emits a single value after a specified timeout.
|
|
14
|
+
*
|
|
15
|
+
* @param timeout - The delay duration in milliseconds before emitting the value.
|
|
16
|
+
* @returns An observable that emits `undefined` after the specified timeout.
|
|
17
|
+
*/
|
|
18
|
+
export const withTimeout$ = (timeout: number): Observable<void> => {
|
|
19
|
+
return of(undefined).pipe(delay(timeout));
|
|
20
|
+
};
|
package/logger/const.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/* eslint-disable unused-imports/no-unused-vars */
|
|
2
|
-
import * as R from "ramda";
|
|
3
|
-
|
|
4
1
|
enum XRayLogLevel {
|
|
5
2
|
verbose = 0,
|
|
6
3
|
debug = 1,
|
|
@@ -11,51 +8,9 @@ enum XRayLogLevel {
|
|
|
11
8
|
off = 100,
|
|
12
9
|
}
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
off = "off",
|
|
16
|
-
verbose = "verbose",
|
|
17
|
-
debug = "debug",
|
|
18
|
-
info = "info",
|
|
19
|
-
warning = "warning",
|
|
20
|
-
error = "error",
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const toastLevelToLogLevel = (toastLogLevel: ToastLogLevel): XRayLogLevel => {
|
|
24
|
-
if (R.isNil(toastLogLevel)) {
|
|
25
|
-
return XRayLogLevel.off;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
switch (toastLogLevel) {
|
|
29
|
-
case ToastLogLevel.verbose:
|
|
30
|
-
return XRayLogLevel.verbose;
|
|
31
|
-
case ToastLogLevel.debug:
|
|
32
|
-
return XRayLogLevel.debug;
|
|
33
|
-
case ToastLogLevel.info:
|
|
34
|
-
return XRayLogLevel.info;
|
|
35
|
-
case ToastLogLevel.error:
|
|
36
|
-
return XRayLogLevel.error;
|
|
37
|
-
default:
|
|
38
|
-
return XRayLogLevel.off;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const PLUGIN_IDENTIFIER = "xray_logging_plugin";
|
|
43
|
-
|
|
44
|
-
const wrapInObject = (obj, name) => {};
|
|
45
|
-
|
|
46
|
-
const applyConditions = (wrappedObj) => {};
|
|
47
|
-
|
|
48
|
-
const sanitizeEventPayload = () => {};
|
|
11
|
+
const wrapInObject = (_ctx: unknown, _key: string) => {};
|
|
49
12
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const isWeb = () => {};
|
|
53
|
-
|
|
54
|
-
const isDOMApp = () => {};
|
|
55
|
-
|
|
56
|
-
const isBrowser = () => {};
|
|
57
|
-
|
|
58
|
-
const eventAllowed = () => {};
|
|
13
|
+
const applyConditions = (_fn: unknown) => {};
|
|
59
14
|
|
|
60
15
|
/**
|
|
61
16
|
* This is the mocked version of XRayLogger
|
|
@@ -90,34 +45,21 @@ export default class XRayLogger {
|
|
|
90
45
|
);
|
|
91
46
|
};
|
|
92
47
|
|
|
93
|
-
log = (
|
|
94
|
-
const logEvent = typeof event === "string" ? { message: event } : event;
|
|
95
|
-
};
|
|
48
|
+
log = (_event) => {};
|
|
96
49
|
|
|
97
|
-
debug = (
|
|
98
|
-
const logEvent = typeof event === "string" ? { message: event } : event;
|
|
99
|
-
};
|
|
50
|
+
debug = (_event) => {};
|
|
100
51
|
|
|
101
|
-
info = (
|
|
102
|
-
const logEvent = typeof event === "string" ? { message: event } : event;
|
|
103
|
-
};
|
|
52
|
+
info = (_event) => {};
|
|
104
53
|
|
|
105
|
-
warning = (
|
|
106
|
-
const logEvent = typeof event === "string" ? { message: event } : event;
|
|
107
|
-
};
|
|
54
|
+
warning = (_event) => {};
|
|
108
55
|
|
|
109
|
-
warn = (
|
|
56
|
+
warn = (_event) => {
|
|
110
57
|
this.warning(event);
|
|
111
58
|
};
|
|
112
59
|
|
|
113
|
-
error = (
|
|
114
|
-
const logEvent: XRayEventData =
|
|
115
|
-
event instanceof Error
|
|
116
|
-
? { exception: event as Error, message: event.message }
|
|
117
|
-
: (event as XRayEventData);
|
|
118
|
-
};
|
|
60
|
+
error = (_event) => {};
|
|
119
61
|
|
|
120
|
-
sendEvent = (
|
|
62
|
+
sendEvent = (_event, _eventData) => {};
|
|
121
63
|
|
|
122
64
|
// To be used by QuickBrick Hook events
|
|
123
65
|
sendAudienceEvent = (event, eventData = null) => {
|
package/navigationUtils/index.ts
CHANGED
|
@@ -644,8 +644,8 @@ export const getScreenId = (
|
|
|
644
644
|
fallbackRiver: ZappRiver
|
|
645
645
|
) => {
|
|
646
646
|
return screenData?.targetScreen
|
|
647
|
-
? screenData?.targetScreen?.id ?? fallbackRiver.id
|
|
647
|
+
? (screenData?.targetScreen?.id ?? fallbackRiver.id)
|
|
648
648
|
: "screen_id" in screenData
|
|
649
|
-
|
|
650
|
-
|
|
649
|
+
? screenData.screen_id
|
|
650
|
+
: fallbackRiver.id;
|
|
651
651
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.5771418585",
|
|
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": "13.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "13.0.0-alpha.5771418585",
|
|
32
32
|
"buffer": "^5.2.1",
|
|
33
33
|
"camelize": "^1.0.0",
|
|
34
34
|
"create-react-context": "^0.2.3",
|
|
@@ -25,6 +25,11 @@ jest.mock("@applicaster/zapp-react-native-utils/analyticsUtils/", () => ({
|
|
|
25
25
|
}),
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
|
|
29
|
+
useTargetScreenData: jest.fn(() => ({})),
|
|
30
|
+
useCurrentScreenData: jest.fn(() => ({})),
|
|
31
|
+
}));
|
|
32
|
+
|
|
28
33
|
jest.mock("@applicaster/zapp-react-native-ui-components/Contexts", () => ({
|
|
29
34
|
...jest.requireActual(
|
|
30
35
|
"@applicaster/zapp-react-native-ui-components/Contexts"
|
|
@@ -16,6 +16,7 @@ import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/acti
|
|
|
16
16
|
import { isFunction, noop } from "../../functionUtils";
|
|
17
17
|
import { useSendAnalyticsOnPress } from "../analytics";
|
|
18
18
|
import { logOnPress, warnEmptyContentType } from "./helpers";
|
|
19
|
+
import { useCurrentScreenData } from "../screen";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* If onCellTap is defined execute the function and
|
|
@@ -44,6 +45,7 @@ export const useCellClick = ({
|
|
|
44
45
|
|
|
45
46
|
const onCellTap: Option<Function> = React.useContext(CellTapContext);
|
|
46
47
|
const actionExecutor = React.useContext(ActionExecutorContext);
|
|
48
|
+
const screenData = useCurrentScreenData();
|
|
47
49
|
|
|
48
50
|
const cellSelectable = toBooleanWithDefaultTrue(
|
|
49
51
|
component?.rules?.component_cells_selectable
|
|
@@ -78,7 +80,10 @@ export const useCellClick = ({
|
|
|
78
80
|
logOnPress(selectedItem, pathname, component, onCellTap);
|
|
79
81
|
|
|
80
82
|
if (selectedItem) {
|
|
81
|
-
await actionExecutor?.handleEntryActions(selectedItem
|
|
83
|
+
await actionExecutor?.handleEntryActions(selectedItem, {
|
|
84
|
+
component,
|
|
85
|
+
screenData,
|
|
86
|
+
});
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
if (selectedItem?.type?.value === "action") {
|
|
@@ -111,6 +116,7 @@ export const useCellClick = ({
|
|
|
111
116
|
pathname,
|
|
112
117
|
push,
|
|
113
118
|
sendAnalyticsOnPress,
|
|
119
|
+
screenData,
|
|
114
120
|
]
|
|
115
121
|
);
|
|
116
122
|
|
|
@@ -52,6 +52,14 @@ export const useDimensions: UseDimensions = (
|
|
|
52
52
|
? { fullDimensions }
|
|
53
53
|
: { ...fullDimensions };
|
|
54
54
|
|
|
55
|
+
if (options.updateForInactiveScreens === undefined) {
|
|
56
|
+
options.updateForInactiveScreens = true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.fullDimensions === undefined) {
|
|
60
|
+
options.fullDimensions = false;
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
const [dimensions, setDimensions] = useState(getInitialDimensions(context));
|
|
56
64
|
const deviceInfo = useRef(getDeviceInfo(dimensions, appData));
|
|
57
65
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { BackHandler
|
|
1
|
+
import { useContext, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { BackHandler } from "react-native";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
useContentTypes,
|
|
@@ -31,6 +31,8 @@ export { useGetBottomTabBarHeight } from "./useGetBottomTabBarHeight";
|
|
|
31
31
|
|
|
32
32
|
export { useIsScreenActive } from "./useIsScreenActive";
|
|
33
33
|
|
|
34
|
+
export { useProfilerLogging } from "./useProfilerLogging";
|
|
35
|
+
|
|
34
36
|
export {
|
|
35
37
|
useUniqueRouteSuffix,
|
|
36
38
|
useNavbarId,
|
|
@@ -121,10 +123,8 @@ export function isNavBarVisible(
|
|
|
121
123
|
return true;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
const { ProfilerBridge } = NativeModules;
|
|
125
|
-
|
|
126
126
|
export const useBackHandler = (cb: () => boolean) => {
|
|
127
|
-
|
|
127
|
+
useEffect(() => {
|
|
128
128
|
BackHandler.addEventListener("hardwareBackPress", cb);
|
|
129
129
|
|
|
130
130
|
return () => {
|
|
@@ -158,7 +158,7 @@ export const useZappHooksForEntry = (
|
|
|
158
158
|
setIsHooksExecutionInProgress,
|
|
159
159
|
setIsPresentingFullScreen,
|
|
160
160
|
setIsRunningInBackground,
|
|
161
|
-
}: HookModalContextT =
|
|
161
|
+
}: HookModalContextT = useContext(ZappHookModalContext.ReactContext);
|
|
162
162
|
|
|
163
163
|
const {
|
|
164
164
|
appData: { layoutVersion },
|
|
@@ -256,22 +256,10 @@ export const useZappHooksForEntry = (
|
|
|
256
256
|
}, [entry.id]);
|
|
257
257
|
};
|
|
258
258
|
|
|
259
|
-
export const useProfilerLogging = () => {
|
|
260
|
-
const logTimestamp = (data: string) =>
|
|
261
|
-
InteractionManager.runAfterInteractions(() => {
|
|
262
|
-
if (ProfilerBridge && data) {
|
|
263
|
-
ProfilerBridge.updateTimer("navigation", data, Date.now());
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
return logTimestamp;
|
|
268
|
-
};
|
|
269
|
-
|
|
270
259
|
export const useRunIfLongPress = (
|
|
271
260
|
options = { timeout: LONG_KEY_PRESS_TIMEOUT }
|
|
272
261
|
) => {
|
|
273
|
-
const pressTimeout =
|
|
274
|
-
React.useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
|
|
262
|
+
const pressTimeout = useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
|
|
275
263
|
|
|
276
264
|
const resetTimeout = () => {
|
|
277
265
|
clearTimeout(pressTimeout.current as ReturnType<typeof setTimeout>);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
2
|
-
|
|
3
1
|
import { platformSelect } from "../../reactUtils";
|
|
4
2
|
|
|
5
3
|
const TAB_BAR_HEIGHT_IOS = 49;
|
|
@@ -7,6 +5,6 @@ const TAB_BAR_HEIGHT_ANDROID = 56;
|
|
|
7
5
|
|
|
8
6
|
export const useGetTabBarHeight = () =>
|
|
9
7
|
platformSelect({
|
|
10
|
-
ios: TAB_BAR_HEIGHT_IOS
|
|
8
|
+
ios: TAB_BAR_HEIGHT_IOS,
|
|
11
9
|
android: TAB_BAR_HEIGHT_ANDROID,
|
|
12
10
|
}) as number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { InteractionManager, NativeModules } from "react-native";
|
|
3
|
+
|
|
4
|
+
const { ProfilerBridge } = NativeModules;
|
|
5
|
+
|
|
6
|
+
export const useProfilerLogging = () =>
|
|
7
|
+
useCallback(
|
|
8
|
+
(data: string) =>
|
|
9
|
+
InteractionManager.runAfterInteractions(() => {
|
|
10
|
+
if (ProfilerBridge && data) {
|
|
11
|
+
ProfilerBridge.updateTimer("navigation", data, Date.now());
|
|
12
|
+
}
|
|
13
|
+
}),
|
|
14
|
+
[]
|
|
15
|
+
);
|
|
@@ -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: 1,
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const CONFIG_LONG_PRESS = {
|
|
@@ -93,6 +93,8 @@ 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
|
+
|
|
96
98
|
this.clearLongPressThresholdTimeout();
|
|
97
99
|
this.clearSeekCompleteTimeout();
|
|
98
100
|
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
@@ -105,27 +107,36 @@ export class TVSeekController
|
|
|
105
107
|
};
|
|
106
108
|
|
|
107
109
|
onForwardPress = (): boolean => {
|
|
110
|
+
log_debug("onForwardPress:");
|
|
111
|
+
|
|
108
112
|
this.handleStartSeek(SEEK_TYPE.FORWARD);
|
|
109
113
|
|
|
110
114
|
return true;
|
|
111
115
|
};
|
|
112
116
|
|
|
113
117
|
onRewindPress = (): boolean => {
|
|
118
|
+
log_debug("onRewindPress:");
|
|
119
|
+
|
|
114
120
|
this.handleStartSeek(SEEK_TYPE.REWIND);
|
|
115
121
|
|
|
116
122
|
return true;
|
|
117
123
|
};
|
|
118
124
|
|
|
119
125
|
onSeekEnd = () => {
|
|
126
|
+
log_debug("onSeekEnd: Seek ended");
|
|
120
127
|
this.clearLongPressThresholdTimeout();
|
|
121
128
|
this.currentSeekController.onSeekEnd();
|
|
122
129
|
};
|
|
123
130
|
|
|
124
131
|
onRewindPressOut = () => {
|
|
132
|
+
log_debug("onRewindPressOut:");
|
|
133
|
+
|
|
125
134
|
this.onSeekEnd();
|
|
126
135
|
};
|
|
127
136
|
|
|
128
137
|
onForwardPressOut = () => {
|
|
138
|
+
log_debug("onForwardPressOut:");
|
|
139
|
+
|
|
129
140
|
this.onSeekEnd();
|
|
130
141
|
};
|
|
131
142
|
|
|
@@ -138,7 +149,9 @@ export class TVSeekController
|
|
|
138
149
|
};
|
|
139
150
|
|
|
140
151
|
handleDelayedSeek = (offset: number = 0) => {
|
|
141
|
-
this.
|
|
152
|
+
if (this.delayedSeekPosition === null) {
|
|
153
|
+
void this.handlePlayerPause();
|
|
154
|
+
}
|
|
142
155
|
|
|
143
156
|
const currentPos =
|
|
144
157
|
this.delayedSeekPosition === null
|
|
@@ -174,36 +187,46 @@ export class TVSeekController
|
|
|
174
187
|
}
|
|
175
188
|
}
|
|
176
189
|
|
|
190
|
+
log_debug(`handleDelayedSeek: currentPos: ${currentPos} targetPos:${targetPos}, initiallyPaused: ${this.isPlayerInitiallyPaused}`);
|
|
191
|
+
|
|
177
192
|
this.delayedSeekPosition = targetPos;
|
|
178
193
|
this.delayedSeekProgressSubject.next(targetPos);
|
|
179
194
|
};
|
|
180
195
|
|
|
181
|
-
handleSeekTo = (newPosition: number) => {
|
|
182
|
-
this.playerController.seekTo(newPosition);
|
|
196
|
+
handleSeekTo = async (newPosition: number) => {
|
|
197
|
+
await this.playerController.seekTo(newPosition);
|
|
198
|
+
|
|
199
|
+
log_debug(
|
|
200
|
+
`handleSeekTo: isPlayerInitiallyPaused: ${this.isPlayerInitiallyPaused} CallPlayerPlay: ${this.isPlayerInitiallyPaused ? "NO" : "YES"}`
|
|
201
|
+
);
|
|
183
202
|
|
|
184
203
|
if (!this.isPlayerInitiallyPaused) {
|
|
185
|
-
this.playerController.play();
|
|
204
|
+
await this.playerController.play();
|
|
186
205
|
}
|
|
187
206
|
};
|
|
188
207
|
|
|
189
|
-
handleSeek = (offset: number) => {
|
|
190
|
-
this.playerController[this.currentSeekType](offset);
|
|
208
|
+
handleSeek = async (offset: number) => {
|
|
209
|
+
await this.playerController[this.currentSeekType](offset);
|
|
210
|
+
|
|
211
|
+
log_debug(
|
|
212
|
+
`handleSeek: isPlayerInitiallyPaused: ${this.isPlayerInitiallyPaused} CallPlayerPlay: ${this.isPlayerInitiallyPaused ? "NO" : "YES"}`
|
|
213
|
+
);
|
|
191
214
|
|
|
192
215
|
if (!this.isPlayerInitiallyPaused) {
|
|
193
|
-
this.playerController.play();
|
|
216
|
+
await this.playerController.play();
|
|
194
217
|
}
|
|
195
218
|
};
|
|
196
219
|
|
|
197
220
|
handleSeekComplete = () => {
|
|
198
221
|
this.clearSeekCompleteTimeout();
|
|
199
222
|
|
|
200
|
-
this.seekEndTimeout = setTimeout(() => {
|
|
223
|
+
this.seekEndTimeout = setTimeout(async () => {
|
|
201
224
|
if (this.delayedSeekPosition !== null) {
|
|
202
|
-
this.handleSeekTo(this.delayedSeekPosition);
|
|
225
|
+
await this.handleSeekTo(this.delayedSeekPosition);
|
|
203
226
|
}
|
|
204
227
|
|
|
205
228
|
log_debug(
|
|
206
|
-
`handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}. Reset all states and seek position.`
|
|
229
|
+
`handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}, playerPaused: ${this.playerController.isPaused()}. Reset all states and seek position.`
|
|
207
230
|
);
|
|
208
231
|
|
|
209
232
|
this.resetAll();
|
|
@@ -214,12 +237,16 @@ export class TVSeekController
|
|
|
214
237
|
return this.delayedSeekPosition !== null;
|
|
215
238
|
};
|
|
216
239
|
|
|
217
|
-
handlePlayerPause = () => {
|
|
240
|
+
handlePlayerPause = async () => {
|
|
241
|
+
log_debug(
|
|
242
|
+
`handleSeek: handlePlayerPause isPlayerPaused: ${this.playerController.isPaused()}`
|
|
243
|
+
);
|
|
244
|
+
|
|
218
245
|
if (this.playerController.isPaused()) {
|
|
219
246
|
return;
|
|
220
247
|
}
|
|
221
248
|
|
|
222
|
-
this.playerController.pause();
|
|
249
|
+
await this.playerController.pause();
|
|
223
250
|
};
|
|
224
251
|
|
|
225
252
|
clearSeekCompleteTimeout = () => {
|
|
@@ -12,6 +12,8 @@ interface TapPressConfig {
|
|
|
12
12
|
|
|
13
13
|
export class TapSeekController implements ISeekController {
|
|
14
14
|
readonly title = "TapSeek";
|
|
15
|
+
private lastFastSeekExit = 0;
|
|
16
|
+
|
|
15
17
|
constructor(
|
|
16
18
|
private seekControllerDelegate: ISeekControllerDelegate,
|
|
17
19
|
private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
|
|
@@ -23,13 +25,18 @@ export class TapSeekController implements ISeekController {
|
|
|
23
25
|
|
|
24
26
|
const skipTime = this.seekIncrementalStrategy.getSkipTime();
|
|
25
27
|
const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
|
|
28
|
+
const cooldown = Date.now() - this.lastFastSeekExit > 1000;
|
|
26
29
|
|
|
27
30
|
if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
|
|
28
31
|
log_debug(
|
|
29
32
|
`${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
|
|
30
33
|
);
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
if (cooldown) {
|
|
36
|
+
this.seekControllerDelegate.handleDelayedSeek(skipTime);
|
|
37
|
+
} else {
|
|
38
|
+
this.seekControllerDelegate.handleSeek(skipTime);
|
|
39
|
+
}
|
|
33
40
|
} else {
|
|
34
41
|
log_debug(
|
|
35
42
|
`${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
|
|
@@ -46,8 +53,8 @@ export class TapSeekController implements ISeekController {
|
|
|
46
53
|
};
|
|
47
54
|
|
|
48
55
|
reset = () => {
|
|
49
|
-
log_debug(
|
|
50
|
-
|
|
56
|
+
log_debug(`${this.title}: Reset states`);
|
|
57
|
+
this.lastFastSeekExit = Date.now();
|
|
51
58
|
this.seekIncrementalStrategy.reset();
|
|
52
59
|
};
|
|
53
60
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* returns a ref with an initial value that is lazily evaluated once
|
|
5
|
+
* */
|
|
6
|
+
export const useRefWithInitialValue = <T>(initialValueGetter: () => T) => {
|
|
7
|
+
const ref = React.useRef<T>(null);
|
|
8
|
+
|
|
9
|
+
if (ref.current === null) {
|
|
10
|
+
ref.current = initialValueGetter();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return ref;
|
|
14
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import type { Subject } from "rxjs";
|
|
3
|
+
|
|
4
|
+
export const useStateFromSubscribe = <T, U>(
|
|
5
|
+
subject: Subject<T>,
|
|
6
|
+
onUpdate: (
|
|
7
|
+
observerValue: T,
|
|
8
|
+
next: React.Dispatch<React.SetStateAction<U>>
|
|
9
|
+
) => void,
|
|
10
|
+
initialValue: U
|
|
11
|
+
) => {
|
|
12
|
+
const [state, setState] = useState<U>(initialValue);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const subscription = subject.subscribe((observer) =>
|
|
16
|
+
onUpdate(observer, setState)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return () => subscription.unsubscribe();
|
|
20
|
+
}, [subject]);
|
|
21
|
+
|
|
22
|
+
return state;
|
|
23
|
+
};
|
|
@@ -4,9 +4,10 @@ import { subscriber } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
4
4
|
const subscriberObjects = Object.create({});
|
|
5
5
|
|
|
6
6
|
interface UseSubscriberFor {
|
|
7
|
-
(
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
(
|
|
8
|
+
eventName: string,
|
|
9
|
+
handler?: (...args: unknown[]) => void
|
|
10
|
+
): (...args: unknown[]) => ReturnType<typeof subscriber>;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|