@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.
Files changed (109) 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} +3 -3
  35. package/appUtils/keyCodes/keysByPlatform.ts +8 -0
  36. package/appUtils/orientationHelper.ts +1 -1
  37. package/appUtils/platform/const.ts +14 -0
  38. package/appUtils/platform/platformUtils.ts +41 -18
  39. package/appUtils/playerManager/player.ts +2 -2
  40. package/appUtils/playerManager/playerNative.ts +1 -1
  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/idleUtils/__tests__/withTimeout$.test.ts +37 -0
  46. package/idleUtils/index.ts +1 -1
  47. package/idleUtils/withTimeout.ts +15 -2
  48. package/logger/const.ts +9 -67
  49. package/manifestUtils/__tests__/createConfig.test.js +16 -16
  50. package/modalState/index.ts +1 -5
  51. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +6 -6
  52. package/navigationUtils/__tests__/itemTypes.test.js +2 -2
  53. package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
  54. package/navigationUtils/index.ts +17 -8
  55. package/package.json +3 -2
  56. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  57. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -1
  58. package/reactHooks/cell-click/__tests__/index.test.js +10 -2
  59. package/reactHooks/cell-click/index.ts +7 -1
  60. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
  61. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
  62. package/reactHooks/feed/useFeedLoader.tsx +1 -1
  63. package/reactHooks/feed/usePipesCacheReset.ts +5 -5
  64. package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
  65. package/reactHooks/layout/__tests__/index.test.tsx +10 -10
  66. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
  67. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
  68. package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
  69. package/reactHooks/navigation/__tests__/index.test.tsx +0 -4
  70. package/reactHooks/navigation/index.ts +29 -218
  71. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
  72. package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
  73. package/reactHooks/navigation/useGetTabBarHeight.ts +12 -0
  74. package/reactHooks/navigation/useIsScreenActive.ts +17 -0
  75. package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
  76. package/reactHooks/navigation/useNavigationType.ts +15 -0
  77. package/reactHooks/navigation/usePathname.ts +4 -0
  78. package/reactHooks/navigation/useRoute.ts +100 -0
  79. package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
  80. package/reactHooks/player/TVSeekControlller/ISeekController.ts +27 -2
  81. package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +23 -61
  82. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +138 -48
  83. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +29 -51
  84. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +115 -0
  85. package/reactHooks/player/__tests__/useTapSeek._test.ts +94 -0
  86. package/reactHooks/player/const.ts +1 -5
  87. package/reactHooks/state/useRefWithInitialValue.ts +14 -0
  88. package/reactHooks/state/useStateFromSubscribe.ts +23 -0
  89. package/reactHooks/usePluginConfiguration.ts +1 -1
  90. package/reactHooks/useSubscriberFor.ts +4 -3
  91. package/reactUtils/__tests__/reactUtils.test.js +10 -28
  92. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +19 -19
  93. package/reactUtils/createContext/__tests__/__snapshots__/index.test.js.snap +2 -2
  94. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -3
  95. package/reactUtils/createContext/__tests__/helpers.test.js +8 -8
  96. package/reactUtils/index.ts +0 -14
  97. package/screenState/index.ts +24 -10
  98. package/theme/__mocks__/index.ts +4 -0
  99. package/theme/index.ts +1 -1
  100. package/time/Timer.ts +5 -2
  101. package/types.ts +1 -0
  102. package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
  103. package/appUtils/keyCodes/keys.ios.js +0 -46
  104. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  105. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  106. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  107. /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
  108. /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
  109. /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
- log_debug,
5
- log_error,
6
- log_verbose,
7
- TVSeekController,
8
- } from "./TVSeekController";
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
- LONG_PRESS_THRESHOLD: number; // ms
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 parent: TVSeekController,
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.parent.currentSeekType = seekType;
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.parent.handleDelayedSeek(
49
- this.parent.currentSeekType,
50
- this.currentSkipAmount
34
+ this.seekControllerDelegate.handleDelayedSeek(
35
+ this.seekIncrementalStrategy.getSkipTime()
51
36
  );
52
37
 
53
38
  log_debug(
54
- `LongPressSeekController: handle delayed seek type: ${seekType} - currentSkipAmount: ${this.currentSkipAmount}.`
39
+ `${
40
+ this.title
41
+ }: handle delayed seek type: ${seekType} - multiplier: ${this.seekIncrementalStrategy.getSkipMultiplier()}, currentSkipAmount: ${this.seekIncrementalStrategy.getSkipTime()}.`
55
42
  );
56
43
 
57
- this.currentSkipAmount = Math.min(
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
- const pressDuration = Date.now() - this.pressStartTime;
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.isLongPressActive = false;
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 { ISeekController } from "./ISeekController";
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
- export class TVSeekController implements SeekKeyHandlerListener {
22
- public currentSeekType: SEEK_TYPE | null = null;
23
- public delayedSeekOffset: number | null = null;
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 tapSeekController: TapSeekController;
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
- this.tapSeekController = new TapSeekController(this);
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
- this.config
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
- }, this.config.LONG_PRESS_THRESHOLD);
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
- public onVideoProgress = (position: number) => {
107
- if (this.delayedSeekOffset) {
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(eventType: SEEK_TYPE, skipTime: number) {
117
- if (this.delayedSeekOffset === null) {
118
- this.pausePlayer();
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 (eventType === SEEK_TYPE.FORWARD) {
150
+ if (this.currentSeekType === SEEK_TYPE.FORWARD) {
126
151
  targetPos = Math.min(
127
- currentPos + newOffset,
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
- targetPos = Math.max(0, currentPos - newOffset);
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}, offset: ${newOffset}, skipTime: ${skipTime}`
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
- seekPlayer(offset: number) {
189
+ handleSeek = (offset: number) => {
144
190
  this.playerController[this.currentSeekType](offset);
145
- this.playerPlay();
146
- }
147
191
 
148
- playerPlay() {
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
- this.delayedSeekOffset = null;
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 { 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
+ 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(`TapSeekController: start tap seek type: ${seekType}.`);
17
-
18
- const prevSeekType = this.parent.currentSeekType;
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
- }
24
+ const skipTime = this.seekIncrementalStrategy.getSkipTime();
25
+ const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
30
26
 
31
- const clicksBeforePause = 0;
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
- `TapSeekController: handle delayed seek type:${seekType} - skipTime: ${skipTime}.`
29
+ `${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
37
30
  );
38
31
 
39
- this.parent.handleDelayedSeek(seekType, skipTime);
32
+ this.seekControllerDelegate.handleDelayedSeek(skipTime);
40
33
  } else {
41
34
  log_debug(
42
- `TapSeekController: handle seek type:${seekType} - skipTime: ${skipTime}.`
35
+ `${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
43
36
  );
44
37
 
45
- this.parent.seekPlayer(skipTime);
38
+ this.seekControllerDelegate.handleSeek(skipTime);
46
39
  }
47
40
 
48
- this.seekMultiplier++;
41
+ this.seekIncrementalStrategy.increment();
49
42
  };
50
43
 
51
44
  onSeekEnd = () => {
52
- this.tapSequenceTimeout = setTimeout(() => {
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("TapSeekController: Reset states");
67
-
68
- this.seekMultiplier = 1;
49
+ log_debug("{this.title}: Reset states");
69
50
 
70
- if (this.tapSequenceTimeout) {
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
+ });