@applicaster/zapp-react-native-utils 13.0.0-alpha.5428512514 → 13.0.0-alpha.5771418585

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 (55) hide show
  1. package/README.md +0 -10
  2. package/actionsExecutor/ActionExecutor.ts +16 -9
  3. package/actionsExecutor/ActionExecutorContext.tsx +47 -7
  4. package/appUtils/HooksManager/Hook.ts +2 -2
  5. package/appUtils/HooksManager/types.ts +5 -2
  6. package/appUtils/accessibilityManager/index.ts +42 -1
  7. package/appUtils/contextKeysManager/contextResolver.ts +6 -2
  8. package/appUtils/contextKeysManager/index.ts +2 -2
  9. package/appUtils/contextKeysManager/utils/index.ts +2 -1
  10. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
  11. package/appUtils/focusManager/index.ts +11 -3
  12. package/appUtils/focusManagerAux/utils/index.ts +4 -3
  13. package/appUtils/platform/const.ts +14 -0
  14. package/appUtils/platform/platformUtils.ts +47 -4
  15. package/appUtils/playerManager/player.ts +10 -9
  16. package/appUtils/playerManager/playerNative.ts +5 -5
  17. package/equalUtils/index.ts +10 -3
  18. package/focusManager/FocusManager.ts +11 -1
  19. package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
  20. package/idleUtils/index.ts +1 -1
  21. package/idleUtils/withTimeout.ts +15 -2
  22. package/logger/const.ts +9 -67
  23. package/manifestUtils/__tests__/createConfig.test.js +0 -2
  24. package/navigationUtils/index.ts +3 -3
  25. package/package.json +2 -2
  26. package/reactHooks/cell-click/__tests__/index.test.js +5 -0
  27. package/reactHooks/cell-click/index.ts +7 -1
  28. package/reactHooks/feed/useFeedLoader.tsx +1 -1
  29. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
  30. package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
  31. package/reactHooks/navigation/index.ts +7 -19
  32. package/reactHooks/navigation/useGetTabBarHeight.ts +1 -3
  33. package/reactHooks/navigation/useProfilerLogging.ts +15 -0
  34. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +40 -13
  35. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +10 -3
  36. package/reactHooks/state/useRefWithInitialValue.ts +14 -0
  37. package/reactHooks/state/useStateFromSubscribe.ts +23 -0
  38. package/reactHooks/usePluginConfiguration.ts +1 -1
  39. package/reactHooks/useSubscriberFor.ts +4 -3
  40. package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
  41. package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
  42. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +6 -72
  43. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
  44. package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
  45. package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
  46. package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
  47. package/reactUtils/createContext/__tests__/provider.test.js +4 -6
  48. package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
  49. package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
  50. package/screenState/index.ts +24 -10
  51. package/testUtils/index.tsx +0 -10
  52. package/theme/index.ts +1 -1
  53. package/time/Timer.ts +5 -2
  54. package/types.ts +1 -0
  55. package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
@@ -250,7 +250,17 @@ class FocusManager {
250
250
  }
251
251
 
252
252
  onDisableFocusChange = (id) => {
253
- if (FocusManager.instance.isFocused(id)) {
253
+ const isFocused = FocusManager.instance.isFocused(id);
254
+
255
+ const isDescendantFocused = (() => {
256
+ try {
257
+ return FocusManager.instance.isAnyDescendantFocused(id);
258
+ } catch (_e) {
259
+ return false;
260
+ }
261
+ })();
262
+
263
+ if (isFocused || isDescendantFocused) {
254
264
  // Move focus to next one
255
265
  const nextFocus = FocusManager.instance.focused?.props
256
266
  ?.nextFocusDown as string;
@@ -0,0 +1,37 @@
1
+ import { TestScheduler } from "rxjs/testing";
2
+
3
+ import { withTimeout$ } from "..";
4
+
5
+ describe("withTimeout$", () => {
6
+ let testScheduler: TestScheduler;
7
+
8
+ beforeEach(() => {
9
+ testScheduler = new TestScheduler((actual, expected) => {
10
+ expect(actual).toEqual(expected);
11
+ });
12
+ });
13
+
14
+ it("should emit undefined after the specified timeout", () => {
15
+ testScheduler.run(({ expectObservable }) => {
16
+ const timeout = 1000;
17
+ const expectedMarble = "1s (a|)";
18
+ const expectedValues = { a: undefined };
19
+
20
+ const result$ = withTimeout$(timeout);
21
+
22
+ expectObservable(result$).toBe(expectedMarble, expectedValues);
23
+ });
24
+ });
25
+
26
+ it("should complete after emitting undefined", () => {
27
+ testScheduler.run(({ expectObservable }) => {
28
+ const timeout = 500;
29
+ const expectedMarble = "500ms (a|)";
30
+ const expectedValues = { a: undefined };
31
+
32
+ const result$ = withTimeout$(timeout);
33
+
34
+ expectObservable(result$).toBe(expectedMarble, expectedValues);
35
+ });
36
+ });
37
+ });
@@ -1,4 +1,4 @@
1
- export { withTimeout } from "./withTimeout";
1
+ export { withTimeout, withTimeout$ } from "./withTimeout";
2
2
 
3
3
  export { onIdle } from "./onIdle";
4
4
 
@@ -1,7 +1,20 @@
1
- export const withTimeout = (timeout: number) => {
2
- return new Promise((resolve) => {
1
+ import { of, Observable } from "rxjs";
2
+ import { delay } from "rxjs/operators";
3
+
4
+ export const withTimeout = (timeout: number): Promise<void> => {
5
+ return new Promise<void>((resolve) => {
3
6
  setTimeout(() => {
4
7
  resolve(undefined);
5
8
  }, timeout);
6
9
  });
7
10
  };
11
+
12
+ /**
13
+ * Creates an observable that emits a single value after a specified timeout.
14
+ *
15
+ * @param timeout - The delay duration in milliseconds before emitting the value.
16
+ * @returns An observable that emits `undefined` after the specified timeout.
17
+ */
18
+ export const withTimeout$ = (timeout: number): Observable<void> => {
19
+ return of(undefined).pipe(delay(timeout));
20
+ };
package/logger/const.ts CHANGED
@@ -1,6 +1,3 @@
1
- /* eslint-disable unused-imports/no-unused-vars */
2
- import * as R from "ramda";
3
-
4
1
  enum XRayLogLevel {
5
2
  verbose = 0,
6
3
  debug = 1,
@@ -11,51 +8,9 @@ enum XRayLogLevel {
11
8
  off = 100,
12
9
  }
13
10
 
14
- enum ToastLogLevel {
15
- off = "off",
16
- verbose = "verbose",
17
- debug = "debug",
18
- info = "info",
19
- warning = "warning",
20
- error = "error",
21
- }
22
-
23
- const toastLevelToLogLevel = (toastLogLevel: ToastLogLevel): XRayLogLevel => {
24
- if (R.isNil(toastLogLevel)) {
25
- return XRayLogLevel.off;
26
- }
27
-
28
- switch (toastLogLevel) {
29
- case ToastLogLevel.verbose:
30
- return XRayLogLevel.verbose;
31
- case ToastLogLevel.debug:
32
- return XRayLogLevel.debug;
33
- case ToastLogLevel.info:
34
- return XRayLogLevel.info;
35
- case ToastLogLevel.error:
36
- return XRayLogLevel.error;
37
- default:
38
- return XRayLogLevel.off;
39
- }
40
- };
41
-
42
- const PLUGIN_IDENTIFIER = "xray_logging_plugin";
43
-
44
- const wrapInObject = (obj, name) => {};
45
-
46
- const applyConditions = (wrappedObj) => {};
47
-
48
- const sanitizeEventPayload = () => {};
11
+ const wrapInObject = (_ctx: unknown, _key: string) => {};
49
12
 
50
- const __isRunningRepoTests = () => {};
51
-
52
- const isWeb = () => {};
53
-
54
- const isDOMApp = () => {};
55
-
56
- const isBrowser = () => {};
57
-
58
- const eventAllowed = () => {};
13
+ const applyConditions = (_fn: unknown) => {};
59
14
 
60
15
  /**
61
16
  * This is the mocked version of XRayLogger
@@ -90,34 +45,21 @@ export default class XRayLogger {
90
45
  );
91
46
  };
92
47
 
93
- log = (event: string | XRayEventData) => {
94
- const logEvent = typeof event === "string" ? { message: event } : event;
95
- };
48
+ log = (_event) => {};
96
49
 
97
- debug = (event: string | XRayEventData) => {
98
- const logEvent = typeof event === "string" ? { message: event } : event;
99
- };
50
+ debug = (_event) => {};
100
51
 
101
- info = (event: string | XRayEventData) => {
102
- const logEvent = typeof event === "string" ? { message: event } : event;
103
- };
52
+ info = (_event) => {};
104
53
 
105
- warning = (event: string | XRayEventData) => {
106
- const logEvent = typeof event === "string" ? { message: event } : event;
107
- };
54
+ warning = (_event) => {};
108
55
 
109
- warn = (event: string | XRayEventData) => {
56
+ warn = (_event) => {
110
57
  this.warning(event);
111
58
  };
112
59
 
113
- error = (event: Error | XRayEventData) => {
114
- const logEvent: XRayEventData =
115
- event instanceof Error
116
- ? { exception: event as Error, message: event.message }
117
- : (event as XRayEventData);
118
- };
60
+ error = (_event) => {};
119
61
 
120
- sendEvent = (event, eventData) => {};
62
+ sendEvent = (_event, _eventData) => {};
121
63
 
122
64
  // To be used by QuickBrick Hook events
123
65
  sendAudienceEvent = (event, eventData = null) => {
@@ -78,8 +78,6 @@ describe("createConfig", () => {
78
78
  extend: "styles",
79
79
  })(props);
80
80
 
81
- console.log(mockGenerators.styles.mock.calls);
82
-
83
81
  expect(mockGenerators.styles).toBeCalledWith(props);
84
82
  });
85
83
 
@@ -644,8 +644,8 @@ export const getScreenId = (
644
644
  fallbackRiver: ZappRiver
645
645
  ) => {
646
646
  return screenData?.targetScreen
647
- ? screenData?.targetScreen?.id ?? fallbackRiver.id
647
+ ? (screenData?.targetScreen?.id ?? fallbackRiver.id)
648
648
  : "screen_id" in screenData
649
- ? screenData.screen_id
650
- : fallbackRiver.id;
649
+ ? screenData.screen_id
650
+ : fallbackRiver.id;
651
651
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.0-alpha.5428512514",
3
+ "version": "13.0.0-alpha.5771418585",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "13.0.0-alpha.5428512514",
31
+ "@applicaster/applicaster-types": "13.0.0-alpha.5771418585",
32
32
  "buffer": "^5.2.1",
33
33
  "camelize": "^1.0.0",
34
34
  "create-react-context": "^0.2.3",
@@ -25,6 +25,11 @@ jest.mock("@applicaster/zapp-react-native-utils/analyticsUtils/", () => ({
25
25
  }),
26
26
  }));
27
27
 
28
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
29
+ useTargetScreenData: jest.fn(() => ({})),
30
+ useCurrentScreenData: jest.fn(() => ({})),
31
+ }));
32
+
28
33
  jest.mock("@applicaster/zapp-react-native-ui-components/Contexts", () => ({
29
34
  ...jest.requireActual(
30
35
  "@applicaster/zapp-react-native-ui-components/Contexts"
@@ -16,6 +16,7 @@ import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/acti
16
16
  import { isFunction, noop } from "../../functionUtils";
17
17
  import { useSendAnalyticsOnPress } from "../analytics";
18
18
  import { logOnPress, warnEmptyContentType } from "./helpers";
19
+ import { useCurrentScreenData } from "../screen";
19
20
 
20
21
  /**
21
22
  * If onCellTap is defined execute the function and
@@ -44,6 +45,7 @@ export const useCellClick = ({
44
45
 
45
46
  const onCellTap: Option<Function> = React.useContext(CellTapContext);
46
47
  const actionExecutor = React.useContext(ActionExecutorContext);
48
+ const screenData = useCurrentScreenData();
47
49
 
48
50
  const cellSelectable = toBooleanWithDefaultTrue(
49
51
  component?.rules?.component_cells_selectable
@@ -78,7 +80,10 @@ export const useCellClick = ({
78
80
  logOnPress(selectedItem, pathname, component, onCellTap);
79
81
 
80
82
  if (selectedItem) {
81
- await actionExecutor?.handleEntryActions(selectedItem);
83
+ await actionExecutor?.handleEntryActions(selectedItem, {
84
+ component,
85
+ screenData,
86
+ });
82
87
  }
83
88
 
84
89
  if (selectedItem?.type?.value === "action") {
@@ -111,6 +116,7 @@ export const useCellClick = ({
111
116
  pathname,
112
117
  push,
113
118
  sendAnalyticsOnPress,
119
+ screenData,
114
120
  ]
115
121
  );
116
122
 
@@ -87,7 +87,7 @@ export const useFeedLoader = ({
87
87
  );
88
88
  }
89
89
  }
90
- }, [callableFeedUrl, currentFeed?.data]);
90
+ }, [callableFeedUrl, currentFeed?.data?.next]);
91
91
 
92
92
  useEffect(() => {
93
93
  if (
@@ -5,7 +5,7 @@ const mockDimensionsGet = jest.fn(
5
5
  ({
6
6
  window: { width: 1, height: 1 },
7
7
  screen: { width: 360, height: 540 },
8
- }[type])
8
+ })[type]
9
9
  );
10
10
 
11
11
  ReactNative.Dimensions.get = mockDimensionsGet;
@@ -52,6 +52,14 @@ export const useDimensions: UseDimensions = (
52
52
  ? { fullDimensions }
53
53
  : { ...fullDimensions };
54
54
 
55
+ if (options.updateForInactiveScreens === undefined) {
56
+ options.updateForInactiveScreens = true;
57
+ }
58
+
59
+ if (options.fullDimensions === undefined) {
60
+ options.fullDimensions = false;
61
+ }
62
+
55
63
  const [dimensions, setDimensions] = useState(getInitialDimensions(context));
56
64
  const deviceInfo = useRef(getDeviceInfo(dimensions, appData));
57
65
 
@@ -1,5 +1,5 @@
1
- import React, { useEffect, useMemo } from "react";
2
- import { BackHandler, InteractionManager, NativeModules } from "react-native";
1
+ import { useContext, useEffect, useMemo, useRef } from "react";
2
+ import { BackHandler } from "react-native";
3
3
 
4
4
  import {
5
5
  useContentTypes,
@@ -31,6 +31,8 @@ export { useGetBottomTabBarHeight } from "./useGetBottomTabBarHeight";
31
31
 
32
32
  export { useIsScreenActive } from "./useIsScreenActive";
33
33
 
34
+ export { useProfilerLogging } from "./useProfilerLogging";
35
+
34
36
  export {
35
37
  useUniqueRouteSuffix,
36
38
  useNavbarId,
@@ -121,10 +123,8 @@ export function isNavBarVisible(
121
123
  return true;
122
124
  }
123
125
 
124
- const { ProfilerBridge } = NativeModules;
125
-
126
126
  export const useBackHandler = (cb: () => boolean) => {
127
- React.useEffect(() => {
127
+ useEffect(() => {
128
128
  BackHandler.addEventListener("hardwareBackPress", cb);
129
129
 
130
130
  return () => {
@@ -158,7 +158,7 @@ export const useZappHooksForEntry = (
158
158
  setIsHooksExecutionInProgress,
159
159
  setIsPresentingFullScreen,
160
160
  setIsRunningInBackground,
161
- }: HookModalContextT = React.useContext(ZappHookModalContext.ReactContext);
161
+ }: HookModalContextT = useContext(ZappHookModalContext.ReactContext);
162
162
 
163
163
  const {
164
164
  appData: { layoutVersion },
@@ -256,22 +256,10 @@ export const useZappHooksForEntry = (
256
256
  }, [entry.id]);
257
257
  };
258
258
 
259
- export const useProfilerLogging = () => {
260
- const logTimestamp = (data: string) =>
261
- InteractionManager.runAfterInteractions(() => {
262
- if (ProfilerBridge && data) {
263
- ProfilerBridge.updateTimer("navigation", data, Date.now());
264
- }
265
- });
266
-
267
- return logTimestamp;
268
- };
269
-
270
259
  export const useRunIfLongPress = (
271
260
  options = { timeout: LONG_KEY_PRESS_TIMEOUT }
272
261
  ) => {
273
- const pressTimeout =
274
- React.useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
262
+ const pressTimeout = useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
275
263
 
276
264
  const resetTimeout = () => {
277
265
  clearTimeout(pressTimeout.current as ReturnType<typeof setTimeout>);
@@ -1,5 +1,3 @@
1
- import { useSafeAreaInsets } from "react-native-safe-area-context";
2
-
3
1
  import { platformSelect } from "../../reactUtils";
4
2
 
5
3
  const TAB_BAR_HEIGHT_IOS = 49;
@@ -7,6 +5,6 @@ const TAB_BAR_HEIGHT_ANDROID = 56;
7
5
 
8
6
  export const useGetTabBarHeight = () =>
9
7
  platformSelect({
10
- ios: TAB_BAR_HEIGHT_IOS + (useSafeAreaInsets().bottom ?? 0),
8
+ ios: TAB_BAR_HEIGHT_IOS,
11
9
  android: TAB_BAR_HEIGHT_ANDROID,
12
10
  }) as number;
@@ -0,0 +1,15 @@
1
+ import { useCallback } from "react";
2
+ import { InteractionManager, NativeModules } from "react-native";
3
+
4
+ const { ProfilerBridge } = NativeModules;
5
+
6
+ export const useProfilerLogging = () =>
7
+ useCallback(
8
+ (data: string) =>
9
+ InteractionManager.runAfterInteractions(() => {
10
+ if (ProfilerBridge && data) {
11
+ ProfilerBridge.updateTimer("navigation", data, Date.now());
12
+ }
13
+ }),
14
+ []
15
+ );
@@ -23,7 +23,7 @@ export enum SEEK_STRATEGY {
23
23
  }
24
24
 
25
25
  const CONFIG_TAP_SEEK = {
26
- CLICKS_BEFORE_PAUSE: 0,
26
+ CLICKS_BEFORE_PAUSE: 1,
27
27
  };
28
28
 
29
29
  const CONFIG_LONG_PRESS = {
@@ -93,6 +93,8 @@ export class TVSeekController
93
93
  this.isPlayerInitiallyPaused = this.playerController.isPaused();
94
94
  }
95
95
 
96
+ log_debug(`handleStartSeek: seekType: ${seekType}, initiallyPaused: ${this.isPlayerInitiallyPaused}, isPlayerPaused: ${this.playerController.isPaused()}`);
97
+
96
98
  this.clearLongPressThresholdTimeout();
97
99
  this.clearSeekCompleteTimeout();
98
100
  this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
@@ -105,27 +107,36 @@ export class TVSeekController
105
107
  };
106
108
 
107
109
  onForwardPress = (): boolean => {
110
+ log_debug("onForwardPress:");
111
+
108
112
  this.handleStartSeek(SEEK_TYPE.FORWARD);
109
113
 
110
114
  return true;
111
115
  };
112
116
 
113
117
  onRewindPress = (): boolean => {
118
+ log_debug("onRewindPress:");
119
+
114
120
  this.handleStartSeek(SEEK_TYPE.REWIND);
115
121
 
116
122
  return true;
117
123
  };
118
124
 
119
125
  onSeekEnd = () => {
126
+ log_debug("onSeekEnd: Seek ended");
120
127
  this.clearLongPressThresholdTimeout();
121
128
  this.currentSeekController.onSeekEnd();
122
129
  };
123
130
 
124
131
  onRewindPressOut = () => {
132
+ log_debug("onRewindPressOut:");
133
+
125
134
  this.onSeekEnd();
126
135
  };
127
136
 
128
137
  onForwardPressOut = () => {
138
+ log_debug("onForwardPressOut:");
139
+
129
140
  this.onSeekEnd();
130
141
  };
131
142
 
@@ -138,7 +149,9 @@ export class TVSeekController
138
149
  };
139
150
 
140
151
  handleDelayedSeek = (offset: number = 0) => {
141
- this.handlePlayerPause();
152
+ if (this.delayedSeekPosition === null) {
153
+ void this.handlePlayerPause();
154
+ }
142
155
 
143
156
  const currentPos =
144
157
  this.delayedSeekPosition === null
@@ -174,36 +187,46 @@ export class TVSeekController
174
187
  }
175
188
  }
176
189
 
190
+ log_debug(`handleDelayedSeek: currentPos: ${currentPos} targetPos:${targetPos}, initiallyPaused: ${this.isPlayerInitiallyPaused}`);
191
+
177
192
  this.delayedSeekPosition = targetPos;
178
193
  this.delayedSeekProgressSubject.next(targetPos);
179
194
  };
180
195
 
181
- handleSeekTo = (newPosition: number) => {
182
- this.playerController.seekTo(newPosition);
196
+ handleSeekTo = async (newPosition: number) => {
197
+ await this.playerController.seekTo(newPosition);
198
+
199
+ log_debug(
200
+ `handleSeekTo: isPlayerInitiallyPaused: ${this.isPlayerInitiallyPaused} CallPlayerPlay: ${this.isPlayerInitiallyPaused ? "NO" : "YES"}`
201
+ );
183
202
 
184
203
  if (!this.isPlayerInitiallyPaused) {
185
- this.playerController.play();
204
+ await this.playerController.play();
186
205
  }
187
206
  };
188
207
 
189
- handleSeek = (offset: number) => {
190
- this.playerController[this.currentSeekType](offset);
208
+ handleSeek = async (offset: number) => {
209
+ await this.playerController[this.currentSeekType](offset);
210
+
211
+ log_debug(
212
+ `handleSeek: isPlayerInitiallyPaused: ${this.isPlayerInitiallyPaused} CallPlayerPlay: ${this.isPlayerInitiallyPaused ? "NO" : "YES"}`
213
+ );
191
214
 
192
215
  if (!this.isPlayerInitiallyPaused) {
193
- this.playerController.play();
216
+ await this.playerController.play();
194
217
  }
195
218
  };
196
219
 
197
220
  handleSeekComplete = () => {
198
221
  this.clearSeekCompleteTimeout();
199
222
 
200
- this.seekEndTimeout = setTimeout(() => {
223
+ this.seekEndTimeout = setTimeout(async () => {
201
224
  if (this.delayedSeekPosition !== null) {
202
- this.handleSeekTo(this.delayedSeekPosition);
225
+ await this.handleSeekTo(this.delayedSeekPosition);
203
226
  }
204
227
 
205
228
  log_debug(
206
- `handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}. Reset all states and seek position.`
229
+ `handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}, playerPaused: ${this.playerController.isPaused()}. Reset all states and seek position.`
207
230
  );
208
231
 
209
232
  this.resetAll();
@@ -214,12 +237,16 @@ export class TVSeekController
214
237
  return this.delayedSeekPosition !== null;
215
238
  };
216
239
 
217
- handlePlayerPause = () => {
240
+ handlePlayerPause = async () => {
241
+ log_debug(
242
+ `handleSeek: handlePlayerPause isPlayerPaused: ${this.playerController.isPaused()}`
243
+ );
244
+
218
245
  if (this.playerController.isPaused()) {
219
246
  return;
220
247
  }
221
248
 
222
- this.playerController.pause();
249
+ await this.playerController.pause();
223
250
  };
224
251
 
225
252
  clearSeekCompleteTimeout = () => {
@@ -12,6 +12,8 @@ interface TapPressConfig {
12
12
 
13
13
  export class TapSeekController implements ISeekController {
14
14
  readonly title = "TapSeek";
15
+ private lastFastSeekExit = 0;
16
+
15
17
  constructor(
16
18
  private seekControllerDelegate: ISeekControllerDelegate,
17
19
  private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
@@ -23,13 +25,18 @@ export class TapSeekController implements ISeekController {
23
25
 
24
26
  const skipTime = this.seekIncrementalStrategy.getSkipTime();
25
27
  const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
28
+ const cooldown = Date.now() - this.lastFastSeekExit > 1000;
26
29
 
27
30
  if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
28
31
  log_debug(
29
32
  `${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
30
33
  );
31
34
 
32
- this.seekControllerDelegate.handleDelayedSeek(skipTime);
35
+ if (cooldown) {
36
+ this.seekControllerDelegate.handleDelayedSeek(skipTime);
37
+ } else {
38
+ this.seekControllerDelegate.handleSeek(skipTime);
39
+ }
33
40
  } else {
34
41
  log_debug(
35
42
  `${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
@@ -46,8 +53,8 @@ export class TapSeekController implements ISeekController {
46
53
  };
47
54
 
48
55
  reset = () => {
49
- log_debug("{this.title}: Reset states");
50
-
56
+ log_debug(`${this.title}: Reset states`);
57
+ this.lastFastSeekExit = Date.now();
51
58
  this.seekIncrementalStrategy.reset();
52
59
  };
53
60
  }
@@ -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
+ };
@@ -0,0 +1,23 @@
1
+ import { useEffect, useState } from "react";
2
+ import type { Subject } from "rxjs";
3
+
4
+ export const useStateFromSubscribe = <T, U>(
5
+ subject: Subject<T>,
6
+ onUpdate: (
7
+ observerValue: T,
8
+ next: React.Dispatch<React.SetStateAction<U>>
9
+ ) => void,
10
+ initialValue: U
11
+ ) => {
12
+ const [state, setState] = useState<U>(initialValue);
13
+
14
+ useEffect(() => {
15
+ const subscription = subject.subscribe((observer) =>
16
+ onUpdate(observer, setState)
17
+ );
18
+
19
+ return () => subscription.unsubscribe();
20
+ }, [subject]);
21
+
22
+ return state;
23
+ };
@@ -91,7 +91,7 @@ export function useLayoutConfiguration<
91
91
  S extends unknown,
92
92
  G extends unknown,
93
93
  R extends unknown,
94
- L extends unknown
94
+ L extends unknown,
95
95
  >(layoutData, manifest, options: Options = {}) {
96
96
  const keys = options.keys || [
97
97
  "assets",
@@ -4,9 +4,10 @@ import { subscriber } from "@applicaster/zapp-react-native-utils/functionUtils";
4
4
  const subscriberObjects = Object.create({});
5
5
 
6
6
  interface UseSubscriberFor {
7
- (eventName: string, handler?: (...args: unknown[]) => void): (
8
- ...args: unknown[]
9
- ) => ReturnType<typeof subscriber>;
7
+ (
8
+ eventName: string,
9
+ handler?: (...args: unknown[]) => void
10
+ ): (...args: unknown[]) => ReturnType<typeof subscriber>;
10
11
  }
11
12
 
12
13
  /**