@applicaster/zapp-react-native-utils 13.0.0-alpha.7210171818 → 13.0.0-alpha.7330390362
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 +7 -6
- package/appUtils/focusManager/index.ts +100 -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} +4 -3
- package/appUtils/keyCodes/keysByPlatform.ts +8 -0
- package/appUtils/keyInputHandler/KeyInputHandler.ts +10 -32
- package/appUtils/orientationHelper.ts +1 -1
- package/appUtils/platform/const.ts +14 -0
- package/appUtils/platform/platformUtils.ts +47 -18
- package/appUtils/playerManager/index.ts +6 -2
- package/appUtils/playerManager/player.ts +9 -10
- package/appUtils/playerManager/playerNative.ts +1 -1
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -15
- 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/numberUtils/index.ts +7 -5
- package/package.json +3 -2
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +6 -8
- package/reactHooks/autoscrolling/useTrackedView.ts +12 -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 +10 -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 +39 -74
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +155 -96
- package/reactHooks/player/TVSeekControlller/TapSeekController.ts +31 -53
- 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/playerManager/DelayedSeekPlayerWrapper.tsx +0 -180
- /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,12 +1,15 @@
|
|
|
1
1
|
import { BehaviorSubject } from "rxjs";
|
|
2
2
|
import { TapSeekController } from "./TapSeekController";
|
|
3
3
|
import { LongPressSeekController } from "./LongPressSeekController";
|
|
4
|
-
import { playerManager } from "../../../appUtils";
|
|
5
4
|
import { SEEK_TYPE } from "../const";
|
|
6
5
|
import { createLogger } from "../../../logger";
|
|
7
6
|
import { Player } from "../../../appUtils/playerManager/player";
|
|
8
7
|
import { SeekKeyHandlerListener } from "../../../appUtils/keyInputHandler/KeyInputHandler";
|
|
9
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ISeekController,
|
|
10
|
+
ISeekControllerDelegate,
|
|
11
|
+
SeekIncrementalBaseStrategy,
|
|
12
|
+
} from "./ISeekController";
|
|
10
13
|
|
|
11
14
|
export const { log_warning, log_debug, log_info, log_error, log_verbose } =
|
|
12
15
|
createLogger({
|
|
@@ -19,70 +22,87 @@ export enum SEEK_STRATEGY {
|
|
|
19
22
|
AUTO_SEEK = "AUTO_SEEK",
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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;
|
|
40
|
+
private readonly playerController: Player;
|
|
27
41
|
private delayedSeekProgressSubject = new BehaviorSubject<number | null>(null);
|
|
42
|
+
isPlayerInitiallyPaused: null | boolean = null;
|
|
43
|
+
currentSeekType: SEEK_TYPE | null = null;
|
|
28
44
|
|
|
29
45
|
private currentSeekController: ISeekController;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
private tapSeekController: TapSeekController;
|
|
33
|
-
private longPressSeekController: LongPressSeekController;
|
|
46
|
+
private readonly tapSeekController: TapSeekController;
|
|
47
|
+
private readonly longPressSeekController: LongPressSeekController;
|
|
34
48
|
private longPressThresholdTimeout: any | null = null;
|
|
49
|
+
private seekEndTimeout: any | null = null;
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
LONG_PRESS_THRESHOLD: 500,
|
|
39
|
-
TIMER_INTERVAL_MS: 300,
|
|
40
|
-
SKIP_INCREMENT_STEP: 5,
|
|
41
|
-
MAX_SKIP_PER_TICK: 300,
|
|
42
|
-
INITIAL_SKIP_AMOUNT: 10,
|
|
43
|
-
};
|
|
51
|
+
constructor(playerController: Player) {
|
|
52
|
+
this.playerController = playerController;
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
this.tapSeekController = new TapSeekController(
|
|
55
|
+
this,
|
|
56
|
+
new SeekIncrementalBaseStrategy(),
|
|
57
|
+
CONFIG_TAP_SEEK
|
|
58
|
+
);
|
|
48
59
|
|
|
49
60
|
this.longPressSeekController = new LongPressSeekController(
|
|
50
61
|
this,
|
|
51
|
-
|
|
62
|
+
new SeekIncrementalBaseStrategy(),
|
|
63
|
+
CONFIG_LONG_PRESS
|
|
52
64
|
);
|
|
65
|
+
|
|
66
|
+
this.currentSeekController = this.tapSeekController;
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
getDelayedSeekProgressObservable = () => {
|
|
56
70
|
return this.delayedSeekProgressSubject;
|
|
57
|
-
}
|
|
71
|
+
};
|
|
58
72
|
|
|
59
73
|
setStrategy = (seekStrategy: SEEK_STRATEGY) => {
|
|
60
74
|
switch (seekStrategy) {
|
|
61
75
|
case SEEK_STRATEGY.TAP_SEEK:
|
|
62
|
-
clearTimeout(this.longPressThresholdTimeout);
|
|
63
|
-
this.longPressThresholdTimeout = null;
|
|
64
76
|
this.currentSeekController = this.tapSeekController;
|
|
77
|
+
log_debug("TVSeekController: setStrategy - TAP_SEEK");
|
|
65
78
|
|
|
66
79
|
break;
|
|
67
80
|
case SEEK_STRATEGY.AUTO_SEEK:
|
|
68
|
-
this.currentSeekController.reset();
|
|
69
81
|
this.currentSeekController = this.longPressSeekController;
|
|
82
|
+
log_debug("TVSeekController: setStrategy - AUTO_SEEK");
|
|
83
|
+
|
|
70
84
|
break;
|
|
71
85
|
default:
|
|
72
86
|
}
|
|
73
|
-
}
|
|
87
|
+
};
|
|
74
88
|
|
|
75
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();
|
|
76
98
|
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
77
99
|
this.currentSeekController.onStartSeek(seekType);
|
|
78
|
-
log_verbose(`handleStartSeek: TAP seekType: ${seekType}`);
|
|
79
100
|
|
|
80
101
|
this.longPressThresholdTimeout = setTimeout(() => {
|
|
81
|
-
|
|
82
|
-
this.setStrategy(SEEK_STRATEGY.AUTO_SEEK);
|
|
102
|
+
this.setStrategy(SEEK_STRATEGY.AUTO_SEEK);
|
|
83
103
|
this.currentSeekController.onStartSeek(seekType);
|
|
84
|
-
},
|
|
85
|
-
}
|
|
104
|
+
}, LONG_PRESS_THRESHOLD);
|
|
105
|
+
};
|
|
86
106
|
|
|
87
107
|
onForwardPress = (): boolean => {
|
|
88
108
|
this.handleStartSeek(SEEK_TYPE.FORWARD);
|
|
@@ -97,103 +117,142 @@ export class TVSeekController implements SeekKeyHandlerListener {
|
|
|
97
117
|
};
|
|
98
118
|
|
|
99
119
|
onSeekEnd = () => {
|
|
120
|
+
this.clearLongPressThresholdTimeout();
|
|
100
121
|
this.currentSeekController.onSeekEnd();
|
|
101
|
-
|
|
102
|
-
}
|
|
122
|
+
};
|
|
103
123
|
|
|
104
124
|
onRewindPressOut = () => {
|
|
105
|
-
const isAutoSeek = this.currentSeekController === this.longPressSeekController;
|
|
106
|
-
|
|
107
|
-
log_verbose(`handleStartSeek: onRewindPressOut, isAutoSeek: ${isAutoSeek}`);
|
|
108
|
-
|
|
109
125
|
this.onSeekEnd();
|
|
110
|
-
}
|
|
126
|
+
};
|
|
111
127
|
|
|
112
128
|
onForwardPressOut = () => {
|
|
113
|
-
const isAutoSeek = this.currentSeekController === this.longPressSeekController;
|
|
114
|
-
|
|
115
|
-
log_verbose(`handleStartSeek: onForwardPressOut, isAutoSeek: ${isAutoSeek}`);
|
|
116
129
|
this.onSeekEnd();
|
|
117
|
-
}
|
|
130
|
+
};
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
132
|
+
onVideoProgress = (position: number) => {
|
|
133
|
+
if (this.delayedSeekPosition !== null) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
122
136
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const player = this.getPlayer();
|
|
126
|
-
if (!player) return;
|
|
137
|
+
this.delayedSeekProgressSubject.next(position);
|
|
138
|
+
};
|
|
127
139
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
handleDelayedSeek = (offset: number = 0) => {
|
|
141
|
+
this.handlePlayerPause();
|
|
142
|
+
|
|
143
|
+
const currentPos =
|
|
144
|
+
this.delayedSeekPosition === null
|
|
145
|
+
? this.playerController.getPosition()
|
|
146
|
+
: this.delayedSeekPosition;
|
|
132
147
|
|
|
133
|
-
const newOffset = (this.delayedSeekOffset || 0) + skipTime;
|
|
134
|
-
const currentPos = player.getPosition();
|
|
135
148
|
let targetPos = currentPos;
|
|
136
149
|
|
|
137
|
-
if (
|
|
138
|
-
targetPos = Math.min(
|
|
150
|
+
if (this.currentSeekType === SEEK_TYPE.FORWARD) {
|
|
151
|
+
targetPos = Math.min(
|
|
152
|
+
currentPos + offset,
|
|
153
|
+
this.playerController.getSeekableDuration()
|
|
154
|
+
);
|
|
155
|
+
} else if (this.currentSeekType === SEEK_TYPE.REWIND) {
|
|
156
|
+
targetPos = Math.max(0, currentPos - offset);
|
|
139
157
|
} else {
|
|
140
|
-
|
|
158
|
+
log_warning(
|
|
159
|
+
`TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
|
|
160
|
+
);
|
|
141
161
|
}
|
|
142
162
|
|
|
143
|
-
this.delayedSeekOffset = newOffset;
|
|
144
|
-
|
|
145
163
|
log_debug(
|
|
146
|
-
`TVSeekController: handleDelayedSeek - targetPos: ${targetPos},
|
|
164
|
+
`TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, skipTime: ${offset}`
|
|
147
165
|
);
|
|
148
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;
|
|
149
178
|
this.delayedSeekProgressSubject.next(targetPos);
|
|
150
|
-
}
|
|
179
|
+
};
|
|
151
180
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (!player || !this.currentSeekType) return;
|
|
155
|
-
player[this.currentSeekType](offset);
|
|
156
|
-
this.playerPlay();
|
|
157
|
-
}
|
|
181
|
+
handleSeekTo = (newPosition: number) => {
|
|
182
|
+
this.playerController.seekTo(newPosition);
|
|
158
183
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
184
|
+
if (!this.isPlayerInitiallyPaused) {
|
|
185
|
+
this.playerController.play();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
162
188
|
|
|
163
|
-
|
|
164
|
-
|
|
189
|
+
handleSeek = (offset: number) => {
|
|
190
|
+
this.playerController[this.currentSeekType](offset);
|
|
191
|
+
|
|
192
|
+
if (!this.isPlayerInitiallyPaused) {
|
|
193
|
+
this.playerController.play();
|
|
165
194
|
}
|
|
166
|
-
}
|
|
195
|
+
};
|
|
167
196
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
+
}
|
|
221
|
+
|
|
222
|
+
this.playerController.pause();
|
|
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
|
+
};
|
|
173
242
|
|
|
174
|
-
/**
|
|
175
|
-
* Reset *all* relevant state and strategy controllers
|
|
176
|
-
*/
|
|
177
243
|
resetAll() {
|
|
178
244
|
log_debug("TVSeekController: Reset all states");
|
|
179
245
|
this.currentSeekType = null;
|
|
180
|
-
this.delayedSeekOffset = null;
|
|
181
246
|
|
|
182
|
-
this.
|
|
247
|
+
this.delayedSeekPosition = null;
|
|
248
|
+
this.isPlayerInitiallyPaused = null;
|
|
249
|
+
|
|
250
|
+
this.clearSeekCompleteTimeout();
|
|
251
|
+
this.clearLongPressThresholdTimeout();
|
|
183
252
|
|
|
184
253
|
this.tapSeekController.reset();
|
|
185
254
|
this.longPressSeekController.reset();
|
|
186
255
|
|
|
187
256
|
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
188
257
|
}
|
|
189
|
-
|
|
190
|
-
private getPlayer() {
|
|
191
|
-
const player = playerManager.getInstanceController();
|
|
192
|
-
|
|
193
|
-
if (!player) {
|
|
194
|
-
log_warning("TVSeekController: No valid player controller found");
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return player;
|
|
198
|
-
}
|
|
199
258
|
}
|
|
@@ -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;
|
|
19
|
-
|
|
20
|
-
if (prevSeekType !== seekType) {
|
|
21
|
-
this.reset();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.parent.currentSeekType = seekType;
|
|
25
|
-
|
|
26
|
-
if (this.tapSequenceTimeout) {
|
|
27
|
-
clearTimeout(this.tapSequenceTimeout);
|
|
28
|
-
this.tapSequenceTimeout = null;
|
|
29
|
-
}
|
|
22
|
+
log_debug(`${this.title}: start tap seek type: ${seekType}.`);
|
|
30
23
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
24
|
+
const skipTime = this.seekIncrementalStrategy.getSkipTime();
|
|
25
|
+
const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
|
|
33
26
|
|
|
34
|
-
if (
|
|
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.
|
|
49
|
-
}
|
|
41
|
+
this.seekIncrementalStrategy.increment();
|
|
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
|
-
|
|
72
|
-
this.tapSequenceTimeout = null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
51
|
+
this.seekIncrementalStrategy.reset();
|
|
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
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
2
|
+
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
3
|
+
import { useTapSeek } from "../useTapSeek";
|
|
4
|
+
|
|
5
|
+
import { ON_PRESS_RESET_DURATION, SEEK_TYPE, SKIP_TIME_BASE } from "../const";
|
|
6
|
+
|
|
7
|
+
const rewind = jest.fn();
|
|
8
|
+
const forward = jest.fn();
|
|
9
|
+
|
|
10
|
+
jest
|
|
11
|
+
.spyOn(playerManager, "getInstanceController")
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
.mockImplementation(() => ({ rewind, forward }));
|
|
14
|
+
|
|
15
|
+
describe("useTapSeek", () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
rewind.mockReset();
|
|
18
|
+
forward.mockReset();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("forward", () => {
|
|
22
|
+
it("should call forward method on playerManager", () => {
|
|
23
|
+
const { result } = renderHook(() => useTapSeek());
|
|
24
|
+
|
|
25
|
+
result.current(SEEK_TYPE.FORWARD);
|
|
26
|
+
expect(forward).toBeCalled();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should call forward with default skip time", () => {
|
|
30
|
+
const { result } = renderHook(() => useTapSeek());
|
|
31
|
+
|
|
32
|
+
result.current(SEEK_TYPE.FORWARD);
|
|
33
|
+
expect(forward).toBeCalledWith(SKIP_TIME_BASE);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should multiply skip time for every call", async () => {
|
|
37
|
+
const { result } = renderHook(() => useTapSeek());
|
|
38
|
+
|
|
39
|
+
result.current(SEEK_TYPE.FORWARD);
|
|
40
|
+
expect(forward).toBeCalledWith(SKIP_TIME_BASE);
|
|
41
|
+
result.current(SEEK_TYPE.FORWARD);
|
|
42
|
+
expect(forward).toBeCalledWith(SKIP_TIME_BASE * 2);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should reset multiplier if waiting more then resetDuration between press actions", () => {
|
|
46
|
+
const { result, waitFor } = renderHook(() => useTapSeek());
|
|
47
|
+
|
|
48
|
+
result.current(SEEK_TYPE.FORWARD);
|
|
49
|
+
expect(forward).toBeCalledWith(SKIP_TIME_BASE);
|
|
50
|
+
waitFor(jest.fn(), { timeout: ON_PRESS_RESET_DURATION });
|
|
51
|
+
result.current(SEEK_TYPE.FORWARD);
|
|
52
|
+
expect(forward).toBeCalledWith(SKIP_TIME_BASE);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("rewind", () => {
|
|
57
|
+
it("should call rewind method on playerManager", () => {
|
|
58
|
+
const { result } = renderHook(() => useTapSeek());
|
|
59
|
+
|
|
60
|
+
result.current(SEEK_TYPE.REWIND);
|
|
61
|
+
expect(rewind).toBeCalled();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should call rewind with default skip time", () => {
|
|
65
|
+
const { result } = renderHook(() => useTapSeek());
|
|
66
|
+
|
|
67
|
+
result.current(SEEK_TYPE.REWIND);
|
|
68
|
+
expect(rewind).toBeCalledWith(SKIP_TIME_BASE);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should multiply skip time for every call", async () => {
|
|
72
|
+
const { result } = renderHook(() => useTapSeek());
|
|
73
|
+
|
|
74
|
+
result.current(SEEK_TYPE.REWIND);
|
|
75
|
+
expect(rewind).toBeCalledWith(SKIP_TIME_BASE);
|
|
76
|
+
result.current(SEEK_TYPE.REWIND);
|
|
77
|
+
expect(rewind).toBeCalledWith(SKIP_TIME_BASE * 2);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("mixed usage", () => {
|
|
82
|
+
it("should reset timer if different type of event is emitted", () => {
|
|
83
|
+
const { result } = renderHook(() => useTapSeek());
|
|
84
|
+
result.current(SEEK_TYPE.REWIND);
|
|
85
|
+
expect(rewind).toBeCalledWith(SKIP_TIME_BASE);
|
|
86
|
+
|
|
87
|
+
result.current(SEEK_TYPE.REWIND);
|
|
88
|
+
expect(rewind).toBeCalledWith(SKIP_TIME_BASE * 2);
|
|
89
|
+
|
|
90
|
+
result.current(SEEK_TYPE.FORWARD);
|
|
91
|
+
expect(forward).toBeCalledWith(SKIP_TIME_BASE);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -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
|
+
};
|