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

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 (113) hide show
  1. package/actionsExecutor/ActionExecutor.ts +16 -9
  2. package/actionsExecutor/ActionExecutorContext.tsx +74 -9
  3. package/alertUtils/alert.ts +148 -0
  4. package/alertUtils/index.ts +1 -148
  5. package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
  6. package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
  7. package/analyticsUtils/helpers/hooks.ts +1 -1
  8. package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
  9. package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +10 -8
  10. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +693 -0
  11. package/appUtils/HooksManager/__tests__/hook.test.js +196 -184
  12. package/appUtils/HooksManager/__tests__/hooksManager.test.js +96 -99
  13. package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
  14. package/appUtils/HooksManager/{index.js → index.ts} +121 -112
  15. package/appUtils/HooksManager/types.ts +119 -0
  16. package/appUtils/RiverFocusManager/index.js +17 -12
  17. package/appUtils/accessibilityManager/index.ts +42 -1
  18. package/appUtils/contextKeysManager/contextResolver.ts +77 -0
  19. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +6 -6
  20. package/appUtils/focusManager/index.ts +91 -65
  21. package/appUtils/focusManager/treeDataStructure/Tree/index.js +25 -3
  22. package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -3
  23. package/appUtils/focusManager/utils/enums.ts +4 -0
  24. package/appUtils/focusManagerAux/utils/index.ts +4 -3
  25. package/appUtils/{index.js → index.ts} +1 -1
  26. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
  27. package/appUtils/keyCodes/__tests__/keyCodes.test.js +4 -4
  28. package/appUtils/keyCodes/{index.js → index.ts} +2 -0
  29. package/appUtils/keyCodes/keys/index.ts +3 -0
  30. package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
  31. package/appUtils/keyCodes/keys/keys.android.ts +18 -0
  32. package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
  33. package/appUtils/keyCodes/keys/keys.ts +15 -0
  34. package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +4 -3
  35. package/appUtils/keyCodes/keysByPlatform.ts +8 -0
  36. package/appUtils/keyInputHandler/KeyInputHandler.ts +10 -32
  37. package/appUtils/orientationHelper.ts +1 -1
  38. package/appUtils/platform/const.ts +14 -0
  39. package/appUtils/platform/platformUtils.ts +41 -18
  40. package/appUtils/playerManager/index.ts +6 -2
  41. package/appUtils/playerManager/player.ts +9 -10
  42. package/appUtils/playerManager/playerNative.ts +1 -1
  43. package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -15
  44. package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
  45. package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
  46. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
  47. package/functionUtils/index.ts +15 -11
  48. package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
  49. package/idleUtils/index.ts +1 -1
  50. package/idleUtils/withTimeout.ts +15 -2
  51. package/logger/const.ts +9 -67
  52. package/manifestUtils/__tests__/createConfig.test.js +16 -16
  53. package/modalState/index.ts +1 -5
  54. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +6 -6
  55. package/navigationUtils/__tests__/itemTypes.test.js +2 -2
  56. package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
  57. package/navigationUtils/index.ts +17 -8
  58. package/package.json +3 -2
  59. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  60. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -1
  61. package/reactHooks/cell-click/__tests__/index.test.js +10 -2
  62. package/reactHooks/cell-click/index.ts +7 -1
  63. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
  64. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
  65. package/reactHooks/feed/useFeedLoader.tsx +1 -1
  66. package/reactHooks/feed/usePipesCacheReset.ts +5 -5
  67. package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
  68. package/reactHooks/layout/__tests__/index.test.tsx +10 -10
  69. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
  70. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
  71. package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
  72. package/reactHooks/navigation/__tests__/index.test.tsx +0 -4
  73. package/reactHooks/navigation/index.ts +29 -218
  74. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
  75. package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
  76. package/reactHooks/navigation/useGetTabBarHeight.ts +10 -0
  77. package/reactHooks/navigation/useIsScreenActive.ts +17 -0
  78. package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
  79. package/reactHooks/navigation/useNavigationType.ts +15 -0
  80. package/reactHooks/navigation/usePathname.ts +4 -0
  81. package/reactHooks/navigation/useRoute.ts +100 -0
  82. package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
  83. package/reactHooks/player/TVSeekControlller/ISeekController.ts +28 -3
  84. package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +43 -95
  85. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +188 -98
  86. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +35 -55
  87. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +115 -0
  88. package/reactHooks/player/__tests__/useTapSeek._test.ts +94 -0
  89. package/reactHooks/player/const.ts +1 -5
  90. package/reactHooks/state/useRefWithInitialValue.ts +14 -0
  91. package/reactHooks/state/useStateFromSubscribe.ts +23 -0
  92. package/reactHooks/usePluginConfiguration.ts +1 -1
  93. package/reactHooks/useSubscriberFor.ts +4 -3
  94. package/reactUtils/__tests__/reactUtils.test.js +10 -28
  95. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +19 -19
  96. package/reactUtils/createContext/__tests__/__snapshots__/index.test.js.snap +2 -2
  97. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -3
  98. package/reactUtils/createContext/__tests__/helpers.test.js +8 -8
  99. package/reactUtils/index.ts +0 -14
  100. package/screenState/index.ts +24 -10
  101. package/theme/__mocks__/index.ts +4 -0
  102. package/theme/index.ts +1 -1
  103. package/time/Timer.ts +5 -2
  104. package/types.ts +1 -0
  105. package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
  106. package/appUtils/keyCodes/keys.ios.js +0 -46
  107. package/appUtils/playerManager/DelayedSeekPlayerWrapper.tsx +0 -180
  108. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  109. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  110. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  111. /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
  112. /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
  113. /package/domUtils/{logger.js → logger.ts} +0 -0
@@ -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
  }
@@ -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
+ });
@@ -1,10 +1,6 @@
1
1
  export const SKIP_TIME_BASE = 10;
2
2
 
3
- export const ON_PRESS_RESET_DURATION = 2000;
4
-
5
- export const ON_HOLD_INTERVAL = 500;
6
-
7
- export const MAX_SEEK_TIME = 60;
3
+ export const MAX_SEEK_TIME = 600;
8
4
 
9
5
  export enum SEEK_TYPE {
10
6
  FORWARD = "forward",