@applicaster/zapp-react-native-utils 13.0.0-alpha.9270615060 → 13.0.0-alpha.9946926334

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.
Files changed (93) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +27 -2
  2. package/alertUtils/alert.ts +148 -0
  3. package/alertUtils/index.ts +1 -148
  4. package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
  5. package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
  6. package/analyticsUtils/helpers/hooks.ts +1 -1
  7. package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
  8. package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +10 -8
  9. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +693 -0
  10. package/appUtils/HooksManager/__tests__/hook.test.js +196 -184
  11. package/appUtils/HooksManager/__tests__/hooksManager.test.js +96 -99
  12. package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
  13. package/appUtils/HooksManager/{index.js → index.ts} +121 -112
  14. package/appUtils/HooksManager/types.ts +116 -0
  15. package/appUtils/RiverFocusManager/index.js +17 -12
  16. package/appUtils/accessibilityManager/index.ts +42 -1
  17. package/appUtils/contextKeysManager/contextResolver.ts +73 -0
  18. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +6 -6
  19. package/appUtils/focusManager/index.ts +90 -63
  20. package/appUtils/focusManager/treeDataStructure/Tree/index.js +25 -3
  21. package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -3
  22. package/appUtils/focusManager/utils/enums.ts +4 -0
  23. package/appUtils/{index.js → index.ts} +1 -1
  24. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
  25. package/appUtils/keyCodes/__tests__/keyCodes.test.js +4 -4
  26. package/appUtils/keyCodes/{index.js → index.ts} +2 -0
  27. package/appUtils/keyCodes/keys/index.ts +3 -0
  28. package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
  29. package/appUtils/keyCodes/keys/keys.android.ts +18 -0
  30. package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
  31. package/appUtils/keyCodes/keys/keys.ts +15 -0
  32. package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +4 -3
  33. package/appUtils/keyCodes/keysByPlatform.ts +8 -0
  34. package/appUtils/keyInputHandler/KeyInputHandler.ts +10 -32
  35. package/appUtils/orientationHelper.ts +1 -1
  36. package/appUtils/platform/platformUtils.ts +0 -14
  37. package/appUtils/playerManager/index.ts +6 -2
  38. package/appUtils/playerManager/player.ts +7 -8
  39. package/appUtils/playerManager/playerNative.ts +1 -1
  40. package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -15
  41. package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
  42. package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
  43. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
  44. package/functionUtils/index.ts +15 -11
  45. package/manifestUtils/__tests__/createConfig.test.js +18 -16
  46. package/modalState/index.ts +1 -5
  47. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +6 -6
  48. package/navigationUtils/__tests__/itemTypes.test.js +2 -2
  49. package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
  50. package/navigationUtils/index.ts +14 -5
  51. package/package.json +3 -2
  52. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  53. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -1
  54. package/reactHooks/cell-click/__tests__/index.test.js +5 -2
  55. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
  56. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
  57. package/reactHooks/feed/usePipesCacheReset.ts +5 -5
  58. package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
  59. package/reactHooks/layout/__tests__/index.test.tsx +10 -10
  60. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
  61. package/reactHooks/navigation/__tests__/index.test.tsx +0 -4
  62. package/reactHooks/navigation/index.ts +29 -218
  63. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
  64. package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
  65. package/reactHooks/navigation/useGetTabBarHeight.ts +12 -0
  66. package/reactHooks/navigation/useIsScreenActive.ts +17 -0
  67. package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
  68. package/reactHooks/navigation/useNavigationType.ts +15 -0
  69. package/reactHooks/navigation/usePathname.ts +4 -0
  70. package/reactHooks/navigation/useRoute.ts +100 -0
  71. package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
  72. package/reactHooks/player/TVSeekControlller/ISeekController.ts +28 -3
  73. package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +43 -95
  74. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +188 -98
  75. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +35 -55
  76. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +115 -0
  77. package/reactHooks/player/__tests__/useTapSeek._test.ts +94 -0
  78. package/reactHooks/player/const.ts +1 -5
  79. package/reactUtils/__tests__/reactUtils.test.js +10 -28
  80. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +19 -19
  81. package/reactUtils/createContext/__tests__/__snapshots__/index.test.js.snap +2 -2
  82. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -3
  83. package/reactUtils/createContext/__tests__/helpers.test.js +8 -8
  84. package/reactUtils/index.ts +0 -14
  85. package/theme/__mocks__/index.ts +4 -0
  86. package/appUtils/keyCodes/keys.ios.js +0 -46
  87. package/appUtils/playerManager/DelayedSeekPlayerWrapper.tsx +0 -180
  88. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  89. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  90. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  91. /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
  92. /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
  93. /package/domUtils/{logger.js → logger.ts} +0 -0
@@ -1,116 +1,64 @@
1
- import { ISeekController } from "./ISeekController";
2
- import { SEEK_TYPE } from "../const";
3
1
  import {
4
- log_debug,
5
- log_error,
6
- log_verbose,
7
- TVSeekController,
8
- } from "./TVSeekController";
9
- import { timer, interval, Subscription } from "rxjs";
10
- import { switchMap, tap } from "rxjs/operators";
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";
8
+ import { interval, Subscription } from "rxjs";
9
+ import { tap } from "rxjs/operators";
11
10
 
12
- /**
13
- * Example interface for the configuration needed for long-press seeking.
14
- */
15
11
  interface LongPressConfig {
16
- LONG_PRESS_THRESHOLD: number; // ms
17
- TIMER_INTERVAL_MS: number; // ms
18
- SKIP_INCREMENT_STEP: number; // seconds
19
- MAX_SKIP_PER_TICK: number; // seconds
20
- INITIAL_SKIP_AMOUNT: number; // e.g., 10s to start
12
+ TIMER_INTERVAL_MS: number;
21
13
  }
22
14
 
23
15
  export class LongPressSeekController implements ISeekController {
24
- private pressStartTime = 0;
25
- private isLongPressActive = false;
26
- private currentSkipAmount = 0;
27
16
  private subscription: Subscription | null = null;
28
-
17
+ readonly title = "LongPressSeek";
29
18
  constructor(
30
- private parent: TVSeekController,
19
+ private seekControllerDelegate: ISeekControllerDelegate,
20
+ private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
31
21
  private config: LongPressConfig
32
22
  ) {}
33
23
 
34
- onPressStart(seekType: SEEK_TYPE) {
24
+ onStartSeek(seekType: SEEK_TYPE) {
35
25
  this.cleanUpSubscription();
36
26
 
37
- log_debug(
38
- `LongPressSeekController: start long press seek type:${seekType}.`
39
- );
40
-
41
- this.parent.currentSeekType = seekType;
42
- this.parent.pausePlayer();
43
-
44
- this.pressStartTime = Date.now();
45
- this.isLongPressActive = true;
46
- this.currentSkipAmount = this.config.INITIAL_SKIP_AMOUNT;
47
-
48
- /**
49
- * We wait LONG_PRESS_THRESHOLD ms to confirm it's a long press.
50
- * If it still active, we switch to an interval that emits every TIMER_INTERVAL_MS.
51
- */
52
- const threshold$ = timer(this.config.LONG_PRESS_THRESHOLD).pipe(
53
- tap(() =>
54
- log_verbose("LongPressSeekController: Crossed long-press threshold")
55
- ),
56
- switchMap(() => {
57
- return interval(this.config.TIMER_INTERVAL_MS).pipe(
58
- tap(() => {
59
- this.parent.handleDelayedSeek(
60
- this.parent.currentSeekType,
61
- this.currentSkipAmount
62
- );
63
-
64
- log_debug(
65
- `LongPressSeekController: handle delayed seek type: ${seekType} - currentSkipAmount: ${this.currentSkipAmount}.`
66
- );
67
-
68
- this.currentSkipAmount = Math.min(
69
- this.currentSkipAmount + this.config.SKIP_INCREMENT_STEP,
70
- this.config.MAX_SKIP_PER_TICK
71
- );
72
- })
73
- );
74
- })
75
- );
76
-
77
- this.subscription = threshold$.subscribe({
78
- complete: () =>
79
- log_verbose(
80
- "LongPressSeekController: Long press subscription complete"
81
- ),
82
- error: (err) =>
83
- log_error(
84
- "LongPressSeekController: Long press subscription error",
85
- err
86
- ),
87
- });
27
+ log_debug(`${this.title}: start long press seek type: ${seekType}.`);
28
+
29
+ this.seekControllerDelegate.handlePlayerPause();
30
+
31
+ this.subscription = interval(this.config.TIMER_INTERVAL_MS)
32
+ .pipe(
33
+ tap(() => {
34
+ this.seekControllerDelegate.handleDelayedSeek(
35
+ this.seekIncrementalStrategy.getSkipTime()
36
+ );
37
+
38
+ log_debug(
39
+ `${
40
+ this.title
41
+ }: handle delayed seek type: ${seekType} - multiplier: ${this.seekIncrementalStrategy.getSkipMultiplier()}, currentSkipAmount: ${this.seekIncrementalStrategy.getSkipTime()}.`
42
+ );
43
+
44
+ this.seekIncrementalStrategy.increment();
45
+ })
46
+ )
47
+ .subscribe({
48
+ complete: () =>
49
+ log_verbose(`${this.title}: Long press subscription complete`),
50
+ error: (err) =>
51
+ log_error(`${this.title}: Long press subscription error`, err),
52
+ });
88
53
  }
89
54
 
90
- onPressEnd() {
91
- const pressDuration = Date.now() - this.pressStartTime;
92
-
93
- if (
94
- this.isLongPressActive &&
95
- pressDuration > this.config.LONG_PRESS_THRESHOLD
96
- ) {
97
- const offset = this.parent.delayedSeekOffset;
98
-
99
- if (offset) {
100
- log_debug(
101
- `LongPressSeekController: onPressEnd for LongPressSeekController, seek offset: ${offset}`
102
- );
103
-
104
- this.parent.seekPlayer(offset);
105
- }
106
- }
107
-
108
- this.parent.resetAll();
55
+ onSeekEnd() {
56
+ this.cleanUpSubscription();
57
+ this.seekControllerDelegate.handleSeekComplete();
109
58
  }
110
59
 
111
60
  reset() {
112
- this.isLongPressActive = false;
113
- this.currentSkipAmount = 0;
61
+ this.seekIncrementalStrategy.reset();
114
62
  this.cleanUpSubscription();
115
63
  }
116
64
 
@@ -1,11 +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";
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({
@@ -13,156 +17,242 @@ export const { log_warning, log_debug, log_info, log_error, log_verbose } =
13
17
  subsystem: "TVSeekController",
14
18
  });
15
19
 
16
- export class TVSeekController implements SeekKeyHandlerListener {
17
- // -- SHARED STATE ACROSS STRATEGIES --
18
- public currentSeekType: SEEK_TYPE | null = null;
19
- public delayedSeekOffset: number | null = null;
20
- private controller: Player;
20
+ export enum SEEK_STRATEGY {
21
+ TAP_SEEK = "TAP_SEEK",
22
+ AUTO_SEEK = "AUTO_SEEK",
23
+ }
24
+
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;
21
41
  private delayedSeekProgressSubject = new BehaviorSubject<number | null>(null);
42
+ isPlayerInitiallyPaused: null | boolean = null;
43
+ currentSeekType: SEEK_TYPE | null = null;
22
44
 
23
- // -- STRATEGY INSTANCES --
24
- private tapSeekController: TapSeekController;
25
- private longPressSeekController: LongPressSeekController;
45
+ private currentSeekController: ISeekController;
46
+ private readonly tapSeekController: TapSeekController;
47
+ private readonly longPressSeekController: LongPressSeekController;
48
+ private longPressThresholdTimeout: any | null = null;
49
+ private seekEndTimeout: any | null = null;
26
50
 
27
- // Example config for long press
28
- private config = {
29
- LONG_PRESS_THRESHOLD: 500,
30
- TIMER_INTERVAL_MS: 300,
31
- SKIP_INCREMENT_STEP: 5,
32
- MAX_SKIP_PER_TICK: 300,
33
- INITIAL_SKIP_AMOUNT: 10,
34
- };
51
+ constructor(playerController: Player) {
52
+ this.playerController = playerController;
35
53
 
36
- constructor(controller: Player) {
37
- this.controller = controller;
38
- this.tapSeekController = new TapSeekController(this);
54
+ this.tapSeekController = new TapSeekController(
55
+ this,
56
+ new SeekIncrementalBaseStrategy(),
57
+ CONFIG_TAP_SEEK
58
+ );
39
59
 
40
60
  this.longPressSeekController = new LongPressSeekController(
41
61
  this,
42
- this.config
62
+ new SeekIncrementalBaseStrategy(),
63
+ CONFIG_LONG_PRESS
43
64
  );
65
+
66
+ this.currentSeekController = this.tapSeekController;
44
67
  }
45
68
 
46
- getDelayedSeekProgressObservable() {
69
+ getDelayedSeekProgressObservable = () => {
47
70
  return this.delayedSeekProgressSubject;
48
- }
71
+ };
49
72
 
50
- onForwardPress = (): boolean => {
51
- this.tapSeekController.onPressStart(SEEK_TYPE.FORWARD);
73
+ setStrategy = (seekStrategy: SEEK_STRATEGY) => {
74
+ switch (seekStrategy) {
75
+ case SEEK_STRATEGY.TAP_SEEK:
76
+ this.currentSeekController = this.tapSeekController;
77
+ log_debug("TVSeekController: setStrategy - TAP_SEEK");
52
78
 
53
- return true;
79
+ break;
80
+ case SEEK_STRATEGY.AUTO_SEEK:
81
+ this.currentSeekController = this.longPressSeekController;
82
+ log_debug("TVSeekController: setStrategy - AUTO_SEEK");
83
+
84
+ break;
85
+ default:
86
+ }
54
87
  };
55
88
 
56
- onRewindPress = (): boolean => {
57
- this.tapSeekController.onPressStart(SEEK_TYPE.REWIND);
89
+ handleStartSeek = (seekType: SEEK_TYPE) => {
90
+ this.currentSeekType = seekType;
58
91
 
59
- return true;
92
+ if (this.isPlayerInitiallyPaused === null) {
93
+ this.isPlayerInitiallyPaused = this.playerController.isPaused();
94
+ }
95
+
96
+ this.clearLongPressThresholdTimeout();
97
+ this.clearSeekCompleteTimeout();
98
+ this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
99
+ this.currentSeekController.onStartSeek(seekType);
100
+
101
+ this.longPressThresholdTimeout = setTimeout(() => {
102
+ this.setStrategy(SEEK_STRATEGY.AUTO_SEEK);
103
+ this.currentSeekController.onStartSeek(seekType);
104
+ }, LONG_PRESS_THRESHOLD);
60
105
  };
61
106
 
62
- onForwardLongPress = (): boolean => {
63
- this.longPressSeekController.onPressStart(SEEK_TYPE.FORWARD);
64
- this.tapSeekController.reset();
107
+ onForwardPress = (): boolean => {
108
+ this.handleStartSeek(SEEK_TYPE.FORWARD);
65
109
 
66
110
  return true;
67
111
  };
68
112
 
69
- onRewindLongPress = (): boolean => {
70
- this.longPressSeekController.onPressStart(SEEK_TYPE.REWIND);
71
- this.tapSeekController.reset();
113
+ onRewindPress = (): boolean => {
114
+ this.handleStartSeek(SEEK_TYPE.REWIND);
72
115
 
73
116
  return true;
74
117
  };
75
118
 
76
- onForwardPressOut = () => this.longPressSeekController.onPressEnd();
77
- onRewindPressOut = () => this.longPressSeekController.onPressEnd();
78
-
79
- getSeekMethods() {
80
- return {
81
- onForwardPress: this.onForwardPress,
82
- onRewindPress: this.onRewindPress,
83
- onForwardLongPress: this.onForwardLongPress,
84
- onRewindLongPress: this.onRewindLongPress,
85
- onForwardPressOut: this.onForwardPressOut,
86
- onRewindPressOut: this.onRewindPressOut,
87
- };
88
- }
119
+ onSeekEnd = () => {
120
+ this.clearLongPressThresholdTimeout();
121
+ this.currentSeekController.onSeekEnd();
122
+ };
89
123
 
90
- // ---------------------------
91
- // SHARED METHODS
92
- // ---------------------------
124
+ onRewindPressOut = () => {
125
+ this.onSeekEnd();
126
+ };
93
127
 
94
- /** Called by either strategy to accumulate offset while paused. */
95
- handleDelayedSeek(eventType: SEEK_TYPE, skipTime: number) {
96
- const player = this.getPlayer();
97
- if (!player) return;
128
+ onForwardPressOut = () => {
129
+ this.onSeekEnd();
130
+ };
98
131
 
99
- // If not previously delayed, we pause the first time
100
- if (this.delayedSeekOffset === null) {
101
- this.pausePlayer();
132
+ onVideoProgress = (position: number) => {
133
+ if (this.delayedSeekPosition !== null) {
134
+ return;
102
135
  }
103
136
 
104
- const newOffset = (this.delayedSeekOffset || 0) + skipTime;
105
- const currentPos = player.getPosition();
137
+ this.delayedSeekProgressSubject.next(position);
138
+ };
139
+
140
+ handleDelayedSeek = (offset: number = 0) => {
141
+ this.handlePlayerPause();
142
+
143
+ const currentPos =
144
+ this.delayedSeekPosition === null
145
+ ? this.playerController.getPosition()
146
+ : this.delayedSeekPosition;
147
+
106
148
  let targetPos = currentPos;
107
149
 
108
- if (eventType === SEEK_TYPE.FORWARD) {
109
- targetPos = Math.min(currentPos + newOffset, player.getDuration());
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);
110
157
  } else {
111
- targetPos = Math.max(0, currentPos - newOffset);
158
+ log_warning(
159
+ `TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
160
+ );
112
161
  }
113
162
 
114
- this.delayedSeekOffset = newOffset;
115
-
116
163
  log_debug(
117
- `TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, offset: ${newOffset}, skipTime: ${skipTime}`
164
+ `TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, skipTime: ${offset}`
118
165
  );
119
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;
120
178
  this.delayedSeekProgressSubject.next(targetPos);
121
- }
179
+ };
122
180
 
123
- seekPlayer(offset: number) {
124
- const player = this.getPlayer();
125
- if (!player || !this.currentSeekType) return;
126
- player[this.currentSeekType](offset);
127
- this.playerPlay();
128
- }
181
+ handleSeekTo = (newPosition: number) => {
182
+ this.playerController.seekTo(newPosition);
183
+
184
+ if (!this.isPlayerInitiallyPaused) {
185
+ this.playerController.play();
186
+ }
187
+ };
129
188
 
130
- playerPlay() {
131
- const player = this.getPlayer();
132
- if (!player) return;
189
+ handleSeek = (offset: number) => {
190
+ this.playerController[this.currentSeekType](offset);
133
191
 
134
- if (player.isPaused()) {
135
- player.play();
192
+ if (!this.isPlayerInitiallyPaused) {
193
+ this.playerController.play();
136
194
  }
137
- }
195
+ };
138
196
 
139
- pausePlayer() {
140
- const player = this.getPlayer();
141
- if (!player) return;
142
- player.pause();
143
- }
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
+ };
144
242
 
145
- /**
146
- * Reset *all* relevant state and strategy controllers
147
- */
148
243
  resetAll() {
149
244
  log_debug("TVSeekController: Reset all states");
150
245
  this.currentSeekType = null;
151
- this.delayedSeekOffset = null;
152
246
 
153
- this.delayedSeekProgressSubject.next(null);
247
+ this.delayedSeekPosition = null;
248
+ this.isPlayerInitiallyPaused = null;
249
+
250
+ this.clearSeekCompleteTimeout();
251
+ this.clearLongPressThresholdTimeout();
154
252
 
155
253
  this.tapSeekController.reset();
156
254
  this.longPressSeekController.reset();
157
- }
158
-
159
- private getPlayer() {
160
- const player = playerManager.getInstanceController();
161
-
162
- if (!player) {
163
- log_warning("TVSeekController: No valid player controller found");
164
- }
165
255
 
166
- return player;
256
+ this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
167
257
  }
168
258
  }
@@ -1,73 +1,53 @@
1
- import { ISeekController } from "./ISeekController";
2
- import { SEEK_TYPE, MAX_SEEK_TIME, SKIP_TIME_BASE } from "../const";
3
- import { log_debug, TVSeekController } from "./TVSeekController";
4
-
5
- const getSkipTime = (multiplier: number) =>
6
- Math.min(MAX_SEEK_TIME, SKIP_TIME_BASE * multiplier);
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
- private tapSequenceTimeout: any | null = null;
10
- private seekMultiplier = 1;
11
- private readonly ON_PRESS_RESET_DURATION = 500; // ms
12
-
13
- constructor(private parent: TVSeekController) {}
14
-
15
- onPressStart(seekType: SEEK_TYPE) {
16
- log_debug(`TapSeekController: start tap seek type: ${seekType}.`);
14
+ readonly title = "TapSeek";
15
+ constructor(
16
+ private seekControllerDelegate: ISeekControllerDelegate,
17
+ private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
18
+ private config: TapPressConfig
19
+ ) {}
17
20
 
18
- const prevSeekType = this.parent.currentSeekType;
21
+ onStartSeek = (seekType: SEEK_TYPE) => {
22
+ log_debug(`${this.title}: start tap seek type: ${seekType}.`);
19
23
 
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
- }
30
-
31
- this.tapSequenceTimeout = setTimeout(() => {
32
- if (this.parent.delayedSeekOffset !== null) {
33
- this.parent.seekPlayer(this.parent.delayedSeekOffset);
34
- }
24
+ const skipTime = this.seekIncrementalStrategy.getSkipTime();
25
+ const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
35
26
 
27
+ if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
36
28
  log_debug(
37
- `TapSeekController: Tap sequence timeout fired, delayedSeekOffset: ${this.parent.delayedSeekOffset}.`
29
+ `${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
38
30
  );
39
31
 
40
- this.parent.resetAll();
41
- }, this.ON_PRESS_RESET_DURATION);
42
-
43
- const clicksBeforePause = 3;
44
- const skipTime = getSkipTime(this.seekMultiplier);
45
-
46
- if (this.seekMultiplier > clicksBeforePause) {
47
- log_debug(
48
- `TapSeekController: handle delayed seek type:${seekType} - skipTime: ${skipTime}.`
49
- );
50
-
51
- this.parent.handleDelayedSeek(seekType, skipTime);
32
+ this.seekControllerDelegate.handleDelayedSeek(skipTime);
52
33
  } else {
53
34
  log_debug(
54
- `TapSeekController: handle seek type:${seekType} - skipTime: ${skipTime}.`
35
+ `${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
55
36
  );
56
37
 
57
- this.parent.seekPlayer(skipTime);
38
+ this.seekControllerDelegate.handleSeek(skipTime);
58
39
  }
59
40
 
60
- this.seekMultiplier++;
61
- }
41
+ this.seekIncrementalStrategy.increment();
42
+ };
62
43
 
63
- reset() {
64
- log_debug("TapSeekController: Reset states");
44
+ onSeekEnd = () => {
45
+ this.seekControllerDelegate.handleSeekComplete();
46
+ };
65
47
 
66
- this.seekMultiplier = 1;
48
+ reset = () => {
49
+ log_debug("{this.title}: Reset states");
67
50
 
68
- if (this.tapSequenceTimeout) {
69
- clearTimeout(this.tapSequenceTimeout);
70
- this.tapSequenceTimeout = null;
71
- }
72
- }
51
+ this.seekIncrementalStrategy.reset();
52
+ };
73
53
  }