@applicaster/zapp-react-native-utils 13.0.0-alpha.1632352572 → 13.0.0-alpha.1825599445
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/actionsExecutor/ActionExecutor.ts +16 -9
- package/actionsExecutor/ActionExecutorContext.tsx +74 -9
- package/alertUtils/alert.ts +148 -0
- package/alertUtils/index.ts +1 -148
- package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
- package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
- package/analyticsUtils/helpers/hooks.ts +1 -1
- package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
- package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +10 -8
- package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +693 -0
- package/appUtils/HooksManager/__tests__/hook.test.js +196 -184
- package/appUtils/HooksManager/__tests__/hooksManager.test.js +96 -99
- package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
- package/appUtils/HooksManager/{index.js → index.ts} +121 -112
- package/appUtils/HooksManager/types.ts +119 -0
- package/appUtils/RiverFocusManager/index.js +17 -12
- package/appUtils/accessibilityManager/index.ts +42 -1
- package/appUtils/contextKeysManager/contextResolver.ts +77 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +6 -6
- package/appUtils/focusManager/index.ts +91 -65
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +25 -3
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -3
- package/appUtils/focusManager/utils/enums.ts +4 -0
- package/appUtils/focusManagerAux/utils/index.ts +4 -3
- package/appUtils/{index.js → index.ts} +1 -1
- package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
- package/appUtils/keyCodes/__tests__/keyCodes.test.js +4 -4
- package/appUtils/keyCodes/{index.js → index.ts} +2 -0
- package/appUtils/keyCodes/keys/index.ts +3 -0
- package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
- package/appUtils/keyCodes/keys/keys.android.ts +18 -0
- package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
- package/appUtils/keyCodes/keys/keys.ts +15 -0
- package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +3 -3
- package/appUtils/keyCodes/keysByPlatform.ts +8 -0
- package/appUtils/orientationHelper.ts +1 -1
- package/appUtils/platform/const.ts +14 -0
- package/appUtils/platform/platformUtils.ts +41 -18
- package/appUtils/playerManager/player.ts +2 -2
- package/appUtils/playerManager/playerNative.ts +1 -1
- package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
- package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
- package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
- package/functionUtils/index.ts +15 -11
- 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 +16 -16
- package/modalState/index.ts +1 -5
- package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +6 -6
- package/navigationUtils/__tests__/itemTypes.test.js +2 -2
- package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
- package/navigationUtils/index.ts +17 -8
- package/package.json +3 -2
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -1
- package/reactHooks/cell-click/__tests__/index.test.js +10 -2
- package/reactHooks/cell-click/index.ts +7 -1
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
- package/reactHooks/feed/useFeedLoader.tsx +1 -1
- package/reactHooks/feed/usePipesCacheReset.ts +5 -5
- package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
- package/reactHooks/layout/__tests__/index.test.tsx +10 -10
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
- package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +0 -4
- package/reactHooks/navigation/index.ts +29 -218
- package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
- package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
- package/reactHooks/navigation/useGetTabBarHeight.ts +12 -0
- package/reactHooks/navigation/useIsScreenActive.ts +17 -0
- package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
- package/reactHooks/navigation/useNavigationType.ts +15 -0
- package/reactHooks/navigation/usePathname.ts +4 -0
- package/reactHooks/navigation/useRoute.ts +100 -0
- package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
- package/reactHooks/player/TVSeekControlller/ISeekController.ts +27 -2
- package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +23 -61
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +138 -48
- package/reactHooks/player/TVSeekControlller/TapSeekController.ts +29 -51
- package/reactHooks/player/__tests__/useAutoSeek._test.tsx +115 -0
- package/reactHooks/player/__tests__/useTapSeek._test.ts +94 -0
- package/reactHooks/player/const.ts +1 -5
- 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__/reactUtils.test.js +10 -28
- package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +19 -19
- package/reactUtils/createContext/__tests__/__snapshots__/index.test.js.snap +2 -2
- package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -3
- package/reactUtils/createContext/__tests__/helpers.test.js +8 -8
- package/reactUtils/index.ts +0 -14
- package/screenState/index.ts +24 -10
- package/theme/__mocks__/index.ts +4 -0
- package/theme/index.ts +1 -1
- package/time/Timer.ts +5 -2
- package/types.ts +1 -0
- package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
- package/appUtils/keyCodes/keys.ios.js +0 -46
- /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
- /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
- /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
- /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
- /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
- /package/domUtils/{logger.js → logger.ts} +0 -0
|
@@ -1,102 +1,64 @@
|
|
|
1
|
-
import { ISeekController } from "./ISeekController";
|
|
2
|
-
import { SEEK_TYPE } from "../const";
|
|
3
1
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "
|
|
2
|
+
ISeekController,
|
|
3
|
+
ISeekControllerDelegate,
|
|
4
|
+
SeekIncrementalBaseStrategy,
|
|
5
|
+
} from "./ISeekController";
|
|
6
|
+
import { SEEK_TYPE } from "../const";
|
|
7
|
+
import { log_debug, log_error, log_verbose } from "./TVSeekController";
|
|
9
8
|
import { interval, Subscription } from "rxjs";
|
|
10
9
|
import { tap } from "rxjs/operators";
|
|
11
10
|
|
|
12
11
|
interface LongPressConfig {
|
|
13
|
-
|
|
14
|
-
TIMER_INTERVAL_MS: number; // ms
|
|
15
|
-
SKIP_INCREMENT_STEP: number; // seconds
|
|
16
|
-
MAX_SKIP_PER_TICK: number; // seconds
|
|
17
|
-
INITIAL_SKIP_AMOUNT: number; // e.g., 10s to start
|
|
12
|
+
TIMER_INTERVAL_MS: number;
|
|
18
13
|
}
|
|
19
14
|
|
|
20
15
|
export class LongPressSeekController implements ISeekController {
|
|
21
|
-
private pressStartTime = 0;
|
|
22
|
-
private isLongPressActive = false;
|
|
23
|
-
private currentSkipAmount = 0;
|
|
24
16
|
private subscription: Subscription | null = null;
|
|
25
|
-
|
|
17
|
+
readonly title = "LongPressSeek";
|
|
26
18
|
constructor(
|
|
27
|
-
private
|
|
19
|
+
private seekControllerDelegate: ISeekControllerDelegate,
|
|
20
|
+
private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
|
|
28
21
|
private config: LongPressConfig
|
|
29
22
|
) {}
|
|
30
23
|
|
|
31
24
|
onStartSeek(seekType: SEEK_TYPE) {
|
|
32
25
|
this.cleanUpSubscription();
|
|
33
26
|
|
|
34
|
-
log_debug(
|
|
35
|
-
`LongPressSeekController: start long press seek type:${seekType}.`
|
|
36
|
-
);
|
|
27
|
+
log_debug(`${this.title}: start long press seek type: ${seekType}.`);
|
|
37
28
|
|
|
38
|
-
this.
|
|
39
|
-
this.parent.pausePlayer();
|
|
40
|
-
|
|
41
|
-
this.pressStartTime = Date.now();
|
|
42
|
-
this.isLongPressActive = true;
|
|
43
|
-
this.currentSkipAmount = this.config.INITIAL_SKIP_AMOUNT;
|
|
29
|
+
this.seekControllerDelegate.handlePlayerPause();
|
|
44
30
|
|
|
45
31
|
this.subscription = interval(this.config.TIMER_INTERVAL_MS)
|
|
46
32
|
.pipe(
|
|
47
33
|
tap(() => {
|
|
48
|
-
this.
|
|
49
|
-
this.
|
|
50
|
-
this.currentSkipAmount
|
|
34
|
+
this.seekControllerDelegate.handleDelayedSeek(
|
|
35
|
+
this.seekIncrementalStrategy.getSkipTime()
|
|
51
36
|
);
|
|
52
37
|
|
|
53
38
|
log_debug(
|
|
54
|
-
|
|
39
|
+
`${
|
|
40
|
+
this.title
|
|
41
|
+
}: handle delayed seek type: ${seekType} - multiplier: ${this.seekIncrementalStrategy.getSkipMultiplier()}, currentSkipAmount: ${this.seekIncrementalStrategy.getSkipTime()}.`
|
|
55
42
|
);
|
|
56
43
|
|
|
57
|
-
this.
|
|
58
|
-
this.currentSkipAmount + this.config.SKIP_INCREMENT_STEP,
|
|
59
|
-
this.config.MAX_SKIP_PER_TICK
|
|
60
|
-
);
|
|
44
|
+
this.seekIncrementalStrategy.increment();
|
|
61
45
|
})
|
|
62
46
|
)
|
|
63
47
|
.subscribe({
|
|
64
48
|
complete: () =>
|
|
65
|
-
log_verbose(
|
|
66
|
-
"LongPressSeekController: Long press subscription complete"
|
|
67
|
-
),
|
|
49
|
+
log_verbose(`${this.title}: Long press subscription complete`),
|
|
68
50
|
error: (err) =>
|
|
69
|
-
log_error(
|
|
70
|
-
"LongPressSeekController: Long press subscription error",
|
|
71
|
-
err
|
|
72
|
-
),
|
|
51
|
+
log_error(`${this.title}: Long press subscription error`, err),
|
|
73
52
|
});
|
|
74
53
|
}
|
|
75
54
|
|
|
76
55
|
onSeekEnd() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
this.isLongPressActive &&
|
|
81
|
-
pressDuration > this.config.LONG_PRESS_THRESHOLD
|
|
82
|
-
) {
|
|
83
|
-
const offset = this.parent.delayedSeekOffset;
|
|
84
|
-
|
|
85
|
-
if (offset) {
|
|
86
|
-
log_debug(
|
|
87
|
-
`LongPressSeekController: onPressEnd for LongPressSeekController, seek offset: ${offset}`
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
this.parent.seekPlayer(offset);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
this.parent.resetAll();
|
|
56
|
+
this.cleanUpSubscription();
|
|
57
|
+
this.seekControllerDelegate.handleSeekComplete();
|
|
95
58
|
}
|
|
96
59
|
|
|
97
60
|
reset() {
|
|
98
|
-
this.
|
|
99
|
-
this.currentSkipAmount = 0;
|
|
61
|
+
this.seekIncrementalStrategy.reset();
|
|
100
62
|
this.cleanUpSubscription();
|
|
101
63
|
}
|
|
102
64
|
|
|
@@ -5,7 +5,11 @@ import { SEEK_TYPE } from "../const";
|
|
|
5
5
|
import { createLogger } from "../../../logger";
|
|
6
6
|
import { Player } from "../../../appUtils/playerManager/player";
|
|
7
7
|
import { SeekKeyHandlerListener } from "../../../appUtils/keyInputHandler/KeyInputHandler";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ISeekController,
|
|
10
|
+
ISeekControllerDelegate,
|
|
11
|
+
SeekIncrementalBaseStrategy,
|
|
12
|
+
} from "./ISeekController";
|
|
9
13
|
|
|
10
14
|
export const { log_warning, log_debug, log_info, log_error, log_verbose } =
|
|
11
15
|
createLogger({
|
|
@@ -18,34 +22,48 @@ export enum SEEK_STRATEGY {
|
|
|
18
22
|
AUTO_SEEK = "AUTO_SEEK",
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
const CONFIG_TAP_SEEK = {
|
|
26
|
+
CLICKS_BEFORE_PAUSE: 0,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const CONFIG_LONG_PRESS = {
|
|
30
|
+
TIMER_INTERVAL_MS: 200,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const LONG_PRESS_THRESHOLD = 500;
|
|
34
|
+
const SEEK_END_TIMEOUT_DURATION = 700;
|
|
35
|
+
// prettier-ignore
|
|
36
|
+
|
|
37
|
+
export class TVSeekController
|
|
38
|
+
implements SeekKeyHandlerListener, ISeekControllerDelegate {
|
|
39
|
+
public delayedSeekPosition: number | null = null;
|
|
24
40
|
private readonly playerController: Player;
|
|
25
41
|
private delayedSeekProgressSubject = new BehaviorSubject<number | null>(null);
|
|
42
|
+
isPlayerInitiallyPaused: null | boolean = null;
|
|
43
|
+
currentSeekType: SEEK_TYPE | null = null;
|
|
26
44
|
|
|
27
45
|
private currentSeekController: ISeekController;
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
private longPressSeekController: LongPressSeekController;
|
|
46
|
+
private readonly tapSeekController: TapSeekController;
|
|
47
|
+
private readonly longPressSeekController: LongPressSeekController;
|
|
31
48
|
private longPressThresholdTimeout: any | null = null;
|
|
32
|
-
|
|
33
|
-
private config = {
|
|
34
|
-
LONG_PRESS_THRESHOLD: 500,
|
|
35
|
-
TIMER_INTERVAL_MS: 300,
|
|
36
|
-
SKIP_INCREMENT_STEP: 5,
|
|
37
|
-
MAX_SKIP_PER_TICK: 300,
|
|
38
|
-
INITIAL_SKIP_AMOUNT: 10,
|
|
39
|
-
};
|
|
49
|
+
private seekEndTimeout: any | null = null;
|
|
40
50
|
|
|
41
51
|
constructor(playerController: Player) {
|
|
42
52
|
this.playerController = playerController;
|
|
43
|
-
|
|
53
|
+
|
|
54
|
+
this.tapSeekController = new TapSeekController(
|
|
55
|
+
this,
|
|
56
|
+
new SeekIncrementalBaseStrategy(),
|
|
57
|
+
CONFIG_TAP_SEEK
|
|
58
|
+
);
|
|
44
59
|
|
|
45
60
|
this.longPressSeekController = new LongPressSeekController(
|
|
46
61
|
this,
|
|
47
|
-
|
|
62
|
+
new SeekIncrementalBaseStrategy(),
|
|
63
|
+
CONFIG_LONG_PRESS
|
|
48
64
|
);
|
|
65
|
+
|
|
66
|
+
this.currentSeekController = this.tapSeekController;
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
getDelayedSeekProgressObservable = () => {
|
|
@@ -55,27 +73,35 @@ export class TVSeekController implements SeekKeyHandlerListener {
|
|
|
55
73
|
setStrategy = (seekStrategy: SEEK_STRATEGY) => {
|
|
56
74
|
switch (seekStrategy) {
|
|
57
75
|
case SEEK_STRATEGY.TAP_SEEK:
|
|
58
|
-
clearTimeout(this.longPressThresholdTimeout);
|
|
59
|
-
this.longPressThresholdTimeout = null;
|
|
60
76
|
this.currentSeekController = this.tapSeekController;
|
|
77
|
+
log_debug("TVSeekController: setStrategy - TAP_SEEK");
|
|
61
78
|
|
|
62
79
|
break;
|
|
63
80
|
case SEEK_STRATEGY.AUTO_SEEK:
|
|
64
|
-
this.currentSeekController.reset();
|
|
65
81
|
this.currentSeekController = this.longPressSeekController;
|
|
82
|
+
log_debug("TVSeekController: setStrategy - AUTO_SEEK");
|
|
83
|
+
|
|
66
84
|
break;
|
|
67
85
|
default:
|
|
68
86
|
}
|
|
69
87
|
};
|
|
70
88
|
|
|
71
89
|
handleStartSeek = (seekType: SEEK_TYPE) => {
|
|
90
|
+
this.currentSeekType = seekType;
|
|
91
|
+
|
|
92
|
+
if (this.isPlayerInitiallyPaused === null) {
|
|
93
|
+
this.isPlayerInitiallyPaused = this.playerController.isPaused();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.clearLongPressThresholdTimeout();
|
|
97
|
+
this.clearSeekCompleteTimeout();
|
|
72
98
|
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
73
99
|
this.currentSeekController.onStartSeek(seekType);
|
|
74
100
|
|
|
75
101
|
this.longPressThresholdTimeout = setTimeout(() => {
|
|
76
102
|
this.setStrategy(SEEK_STRATEGY.AUTO_SEEK);
|
|
77
103
|
this.currentSeekController.onStartSeek(seekType);
|
|
78
|
-
},
|
|
104
|
+
}, LONG_PRESS_THRESHOLD);
|
|
79
105
|
};
|
|
80
106
|
|
|
81
107
|
onForwardPress = (): boolean => {
|
|
@@ -91,8 +117,8 @@ export class TVSeekController implements SeekKeyHandlerListener {
|
|
|
91
117
|
};
|
|
92
118
|
|
|
93
119
|
onSeekEnd = () => {
|
|
120
|
+
this.clearLongPressThresholdTimeout();
|
|
94
121
|
this.currentSeekController.onSeekEnd();
|
|
95
|
-
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
96
122
|
};
|
|
97
123
|
|
|
98
124
|
onRewindPressOut = () => {
|
|
@@ -103,62 +129,126 @@ export class TVSeekController implements SeekKeyHandlerListener {
|
|
|
103
129
|
this.onSeekEnd();
|
|
104
130
|
};
|
|
105
131
|
|
|
106
|
-
|
|
107
|
-
if (this.
|
|
132
|
+
onVideoProgress = (position: number) => {
|
|
133
|
+
if (this.delayedSeekPosition !== null) {
|
|
108
134
|
return;
|
|
109
135
|
}
|
|
110
136
|
|
|
111
|
-
// TODO: If 1 second after seek, send
|
|
112
|
-
|
|
113
137
|
this.delayedSeekProgressSubject.next(position);
|
|
114
138
|
};
|
|
115
139
|
|
|
116
|
-
handleDelayedSeek(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
140
|
+
handleDelayedSeek = (offset: number = 0) => {
|
|
141
|
+
this.handlePlayerPause();
|
|
142
|
+
|
|
143
|
+
const currentPos =
|
|
144
|
+
this.delayedSeekPosition === null
|
|
145
|
+
? this.playerController.getPosition()
|
|
146
|
+
: this.delayedSeekPosition;
|
|
120
147
|
|
|
121
|
-
const newOffset = (this.delayedSeekOffset || 0) + skipTime;
|
|
122
|
-
const currentPos = this.playerController.getPosition();
|
|
123
148
|
let targetPos = currentPos;
|
|
124
149
|
|
|
125
|
-
if (
|
|
150
|
+
if (this.currentSeekType === SEEK_TYPE.FORWARD) {
|
|
126
151
|
targetPos = Math.min(
|
|
127
|
-
currentPos +
|
|
152
|
+
currentPos + offset,
|
|
128
153
|
this.playerController.getSeekableDuration()
|
|
129
154
|
);
|
|
155
|
+
} else if (this.currentSeekType === SEEK_TYPE.REWIND) {
|
|
156
|
+
targetPos = Math.max(0, currentPos - offset);
|
|
130
157
|
} else {
|
|
131
|
-
|
|
158
|
+
log_warning(
|
|
159
|
+
`TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
|
|
160
|
+
);
|
|
132
161
|
}
|
|
133
162
|
|
|
134
|
-
this.delayedSeekOffset = newOffset;
|
|
135
|
-
|
|
136
163
|
log_debug(
|
|
137
|
-
`TVSeekController: handleDelayedSeek - targetPos: ${targetPos},
|
|
164
|
+
`TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, skipTime: ${offset}`
|
|
138
165
|
);
|
|
139
166
|
|
|
167
|
+
const prevValue = this.delayedSeekProgressSubject.getValue();
|
|
168
|
+
|
|
169
|
+
if (prevValue !== null) {
|
|
170
|
+
const delta = Math.abs(prevValue - targetPos);
|
|
171
|
+
|
|
172
|
+
if (delta < 1) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.delayedSeekPosition = targetPos;
|
|
140
178
|
this.delayedSeekProgressSubject.next(targetPos);
|
|
141
|
-
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
handleSeekTo = (newPosition: number) => {
|
|
182
|
+
this.playerController.seekTo(newPosition);
|
|
183
|
+
|
|
184
|
+
if (!this.isPlayerInitiallyPaused) {
|
|
185
|
+
this.playerController.play();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
142
188
|
|
|
143
|
-
|
|
189
|
+
handleSeek = (offset: number) => {
|
|
144
190
|
this.playerController[this.currentSeekType](offset);
|
|
145
|
-
this.playerPlay();
|
|
146
|
-
}
|
|
147
191
|
|
|
148
|
-
|
|
149
|
-
if (this.playerController.isPaused()) {
|
|
192
|
+
if (!this.isPlayerInitiallyPaused) {
|
|
150
193
|
this.playerController.play();
|
|
151
194
|
}
|
|
152
|
-
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
handleSeekComplete = () => {
|
|
198
|
+
this.clearSeekCompleteTimeout();
|
|
199
|
+
|
|
200
|
+
this.seekEndTimeout = setTimeout(() => {
|
|
201
|
+
if (this.delayedSeekPosition !== null) {
|
|
202
|
+
this.handleSeekTo(this.delayedSeekPosition);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
log_debug(
|
|
206
|
+
`handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}. Reset all states and seek position.`
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
this.resetAll();
|
|
210
|
+
}, SEEK_END_TIMEOUT_DURATION);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
isSeeking = (): boolean => {
|
|
214
|
+
return this.delayedSeekPosition !== null;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
handlePlayerPause = () => {
|
|
218
|
+
if (this.playerController.isPaused()) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
153
221
|
|
|
154
|
-
pausePlayer() {
|
|
155
222
|
this.playerController.pause();
|
|
156
|
-
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
clearSeekCompleteTimeout = () => {
|
|
226
|
+
if (this.seekEndTimeout === null) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
clearTimeout(this.seekEndTimeout);
|
|
231
|
+
this.seekEndTimeout = null;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
clearLongPressThresholdTimeout = () => {
|
|
235
|
+
if (this.longPressThresholdTimeout === null) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
clearTimeout(this.longPressThresholdTimeout);
|
|
240
|
+
this.longPressThresholdTimeout = null;
|
|
241
|
+
};
|
|
157
242
|
|
|
158
243
|
resetAll() {
|
|
159
244
|
log_debug("TVSeekController: Reset all states");
|
|
160
245
|
this.currentSeekType = null;
|
|
161
|
-
|
|
246
|
+
|
|
247
|
+
this.delayedSeekPosition = null;
|
|
248
|
+
this.isPlayerInitiallyPaused = null;
|
|
249
|
+
|
|
250
|
+
this.clearSeekCompleteTimeout();
|
|
251
|
+
this.clearLongPressThresholdTimeout();
|
|
162
252
|
|
|
163
253
|
this.tapSeekController.reset();
|
|
164
254
|
this.longPressSeekController.reset();
|
|
@@ -1,75 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import {
|
|
2
|
+
ISeekController,
|
|
3
|
+
ISeekControllerDelegate,
|
|
4
|
+
SeekIncrementalBaseStrategy,
|
|
5
|
+
} from "./ISeekController";
|
|
6
|
+
import { SEEK_TYPE } from "../const";
|
|
7
|
+
import { log_debug } from "./TVSeekController";
|
|
8
|
+
|
|
9
|
+
interface TapPressConfig {
|
|
10
|
+
CLICKS_BEFORE_PAUSE: number;
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export class TapSeekController implements ISeekController {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
readonly title = "TapSeek";
|
|
15
|
+
constructor(
|
|
16
|
+
private seekControllerDelegate: ISeekControllerDelegate,
|
|
17
|
+
private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
|
|
18
|
+
private config: TapPressConfig
|
|
19
|
+
) {}
|
|
14
20
|
|
|
15
21
|
onStartSeek = (seekType: SEEK_TYPE) => {
|
|
16
|
-
log_debug(
|
|
17
|
-
|
|
18
|
-
const prevSeekType = this.parent.currentSeekType;
|
|
22
|
+
log_debug(`${this.title}: start tap seek type: ${seekType}.`);
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.parent.currentSeekType = seekType;
|
|
25
|
-
|
|
26
|
-
if (this.tapSequenceTimeout) {
|
|
27
|
-
clearTimeout(this.tapSequenceTimeout);
|
|
28
|
-
this.tapSequenceTimeout = null;
|
|
29
|
-
}
|
|
24
|
+
const skipTime = this.seekIncrementalStrategy.getSkipTime();
|
|
25
|
+
const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
const skipTime = getSkipTime(this.seekMultiplier);
|
|
33
|
-
|
|
34
|
-
if (this.seekMultiplier > clicksBeforePause) {
|
|
27
|
+
if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
|
|
35
28
|
log_debug(
|
|
36
|
-
|
|
29
|
+
`${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
|
|
37
30
|
);
|
|
38
31
|
|
|
39
|
-
this.
|
|
32
|
+
this.seekControllerDelegate.handleDelayedSeek(skipTime);
|
|
40
33
|
} else {
|
|
41
34
|
log_debug(
|
|
42
|
-
|
|
35
|
+
`${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
|
|
43
36
|
);
|
|
44
37
|
|
|
45
|
-
this.
|
|
38
|
+
this.seekControllerDelegate.handleSeek(skipTime);
|
|
46
39
|
}
|
|
47
40
|
|
|
48
|
-
this.
|
|
41
|
+
this.seekIncrementalStrategy.increment();
|
|
49
42
|
};
|
|
50
43
|
|
|
51
44
|
onSeekEnd = () => {
|
|
52
|
-
this.
|
|
53
|
-
if (this.parent.delayedSeekOffset !== null) {
|
|
54
|
-
this.parent.seekPlayer(this.parent.delayedSeekOffset);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
log_debug(
|
|
58
|
-
`TapSeekController: Tap sequence timeout fired, delayedSeekOffset: ${this.parent.delayedSeekOffset}.`
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
this.parent.resetAll();
|
|
62
|
-
}, this.ON_PRESS_RESET_DURATION);
|
|
45
|
+
this.seekControllerDelegate.handleSeekComplete();
|
|
63
46
|
};
|
|
64
47
|
|
|
65
48
|
reset = () => {
|
|
66
|
-
log_debug("
|
|
67
|
-
|
|
68
|
-
this.seekMultiplier = 1;
|
|
49
|
+
log_debug("{this.title}: Reset states");
|
|
69
50
|
|
|
70
|
-
|
|
71
|
-
clearTimeout(this.tapSequenceTimeout);
|
|
72
|
-
this.tapSequenceTimeout = null;
|
|
73
|
-
}
|
|
51
|
+
this.seekIncrementalStrategy.reset();
|
|
74
52
|
};
|
|
75
53
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { act, renderHook } from "@testing-library/react-hooks";
|
|
2
|
+
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
3
|
+
|
|
4
|
+
import { ON_HOLD_INTERVAL, SEEK_TYPE, SKIP_TIME_BASE } from "../const";
|
|
5
|
+
|
|
6
|
+
import { useAutoSeek } from "../useAutoSeek";
|
|
7
|
+
|
|
8
|
+
jest.useFakeTimers({ legacyFakeTimers: true });
|
|
9
|
+
|
|
10
|
+
const rewind = jest.fn();
|
|
11
|
+
const forward = jest.fn();
|
|
12
|
+
|
|
13
|
+
jest
|
|
14
|
+
.spyOn(playerManager, "getInstanceController")
|
|
15
|
+
.mockReturnValue({ rewind, forward });
|
|
16
|
+
|
|
17
|
+
describe("useAutoSeek", () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
rewind.mockReset();
|
|
20
|
+
forward.mockReset();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("autoSeekStart", () => {
|
|
24
|
+
it("should call forward method on playerInstance", () => {
|
|
25
|
+
const { result } = renderHook(() => useAutoSeek());
|
|
26
|
+
|
|
27
|
+
act(() => {
|
|
28
|
+
result.current.autoSeekStart(SEEK_TYPE.FORWARD);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(forward).toBeCalled();
|
|
32
|
+
result.current.autoSeekStop();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should call rewind method on playerManager", () => {
|
|
36
|
+
const { result } = renderHook(() => useAutoSeek());
|
|
37
|
+
|
|
38
|
+
act(() => {
|
|
39
|
+
result.current.autoSeekStart(SEEK_TYPE.REWIND);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(rewind).toBeCalled();
|
|
43
|
+
result.current.autoSeekStop();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should call rewind with default skip time", () => {
|
|
47
|
+
const { result } = renderHook(() => useAutoSeek());
|
|
48
|
+
|
|
49
|
+
act(() => {
|
|
50
|
+
result.current.autoSeekStart(SEEK_TYPE.REWIND);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(rewind).toBeCalledWith(SKIP_TIME_BASE);
|
|
54
|
+
result.current.autoSeekStop();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should call forward with default skip time", () => {
|
|
58
|
+
const { result } = renderHook(() => useAutoSeek());
|
|
59
|
+
|
|
60
|
+
act(() => {
|
|
61
|
+
result.current.autoSeekStart(SEEK_TYPE.FORWARD);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(forward).toBeCalledWith(SKIP_TIME_BASE);
|
|
65
|
+
result.current.autoSeekStop();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should call rewind in intervals set in configuration", () => {
|
|
69
|
+
const { result } = renderHook(() => useAutoSeek());
|
|
70
|
+
|
|
71
|
+
act(() => {
|
|
72
|
+
result.current.autoSeekStart(SEEK_TYPE.REWIND);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(rewind).toBeCalled();
|
|
76
|
+
jest.advanceTimersByTime(ON_HOLD_INTERVAL * 3);
|
|
77
|
+
expect(rewind).toBeCalledTimes(4);
|
|
78
|
+
result.current.autoSeekStop();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should call forward in intervals set in configuration", () => {
|
|
82
|
+
const { result } = renderHook(() => useAutoSeek());
|
|
83
|
+
|
|
84
|
+
act(() => {
|
|
85
|
+
result.current.autoSeekStart(SEEK_TYPE.FORWARD);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(forward).toBeCalled();
|
|
89
|
+
jest.advanceTimersByTime(ON_HOLD_INTERVAL * 3);
|
|
90
|
+
expect(forward).toBeCalledTimes(4);
|
|
91
|
+
result.current.autoSeekStop();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("autoSeekStop", () => {
|
|
96
|
+
it("should stop calling playerManager method after autoSeekStop is called", () => {
|
|
97
|
+
const { result } = renderHook(() => useAutoSeek());
|
|
98
|
+
|
|
99
|
+
act(() => {
|
|
100
|
+
result.current.autoSeekStart(SEEK_TYPE.FORWARD);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(forward).toBeCalled();
|
|
104
|
+
jest.advanceTimersByTime(ON_HOLD_INTERVAL * 3);
|
|
105
|
+
expect(forward).toBeCalledTimes(4);
|
|
106
|
+
|
|
107
|
+
act(() => {
|
|
108
|
+
result.current.autoSeekStop();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
jest.advanceTimersByTime(ON_HOLD_INTERVAL * 3);
|
|
112
|
+
expect(forward).toBeCalledTimes(4);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|