@applicaster/zapp-react-native-utils 13.0.0-rc.12 → 13.0.0-rc.121

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 (183) hide show
  1. package/README.md +0 -10
  2. package/actionsExecutor/ActionExecutor.ts +16 -9
  3. package/actionsExecutor/ActionExecutorContext.tsx +196 -11
  4. package/actionsExecutor/consts.ts +4 -0
  5. package/alertUtils/alert.ts +148 -0
  6. package/alertUtils/index.ts +1 -148
  7. package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
  8. package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
  9. package/analyticsUtils/currentScreenProperities.ts +6 -4
  10. package/analyticsUtils/helpers/hooks.ts +1 -1
  11. package/appUtils/HooksManager/{Hook.js → Hook.ts} +39 -23
  12. package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +10 -8
  13. package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +693 -0
  14. package/appUtils/HooksManager/__tests__/hook.test.js +196 -184
  15. package/appUtils/HooksManager/__tests__/hooksManager.test.js +96 -99
  16. package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
  17. package/appUtils/HooksManager/{index.js → index.ts} +124 -113
  18. package/appUtils/HooksManager/types.ts +119 -0
  19. package/appUtils/RiverFocusManager/index.js +17 -12
  20. package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
  21. package/appUtils/__tests__/allZappLocales.ts +79 -0
  22. package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
  23. package/appUtils/accessibilityManager/index.ts +42 -1
  24. package/appUtils/contextKeysManager/contextResolver.ts +66 -0
  25. package/appUtils/contextKeysManager/index.ts +2 -2
  26. package/appUtils/contextKeysManager/utils/index.ts +2 -1
  27. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +9 -6
  28. package/appUtils/focusManager/index.ios.ts +14 -4
  29. package/appUtils/focusManager/index.ts +108 -69
  30. package/appUtils/focusManager/treeDataStructure/Tree/index.js +25 -3
  31. package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -3
  32. package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
  33. package/appUtils/focusManager/utils/enums.ts +4 -0
  34. package/appUtils/focusManager/utils/index.ts +5 -0
  35. package/appUtils/focusManagerAux/utils/index.ts +4 -3
  36. package/appUtils/{index.js → index.ts} +1 -1
  37. package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
  38. package/appUtils/keyCodes/__tests__/keyCodes.test.js +4 -4
  39. package/appUtils/keyCodes/{index.js → index.ts} +13 -7
  40. package/appUtils/keyCodes/keys/index.ts +3 -0
  41. package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
  42. package/appUtils/keyCodes/keys/keys.android.ts +18 -0
  43. package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
  44. package/appUtils/keyCodes/keys/keys.ts +15 -0
  45. package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +4 -0
  46. package/appUtils/keyCodes/keysByPlatform.ts +8 -0
  47. package/appUtils/keyInputHandler/KeyInputHandler.ts +68 -0
  48. package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +31 -0
  49. package/appUtils/orientationHelper.ts +11 -13
  50. package/appUtils/platform/const.ts +14 -0
  51. package/appUtils/platform/platformUtils.ts +47 -18
  52. package/appUtils/playerManager/index.ts +1 -1
  53. package/appUtils/playerManager/player.ts +42 -18
  54. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
  55. package/appUtils/playerManager/playerNative.ts +18 -2
  56. package/appUtils/playerManager/usePlayerControllerSetup.tsx +3 -3
  57. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +7 -1
  58. package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
  59. package/cellUtils/index.ts +9 -5
  60. package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
  61. package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
  62. package/componentsUtils/__tests__/isGallery.test.ts +33 -0
  63. package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
  64. package/componentsUtils/index.ts +39 -0
  65. package/dateUtils/index.ts +19 -0
  66. package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
  67. package/equalUtils/__tests__/isEqual/combination.test.ts +263 -0
  68. package/equalUtils/index.ts +49 -30
  69. package/focusManager/FocusManager.ts +14 -2
  70. package/focusManager/index.ts +23 -4
  71. package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
  72. package/functionUtils/index.ts +61 -19
  73. package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
  74. package/idleUtils/index.ts +1 -1
  75. package/idleUtils/withTimeout.ts +15 -2
  76. package/index.d.ts +0 -12
  77. package/localizationUtils/index.ts +8 -13
  78. package/logger/const.ts +9 -67
  79. package/manifestUtils/__tests__/createConfig.test.js +16 -16
  80. package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
  81. package/manifestUtils/defaultManifestConfigurations/player.js +9 -3
  82. package/manifestUtils/fonts/index.js +5 -6
  83. package/manifestUtils/keys.js +27 -2
  84. package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
  85. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +875 -0
  86. package/manifestUtils/sharedConfiguration/screenPicker/utils.js +130 -0
  87. package/modalState/__tests__/index.test.ts +51 -0
  88. package/modalState/index.ts +33 -7
  89. package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +7 -6
  90. package/navigationUtils/__tests__/itemTypes.test.js +2 -2
  91. package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
  92. package/navigationUtils/index.ts +20 -9
  93. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +23 -3
  94. package/numberUtils/index.ts +11 -5
  95. package/package.json +5 -10
  96. package/playerUtils/configurationGenerator.ts +9 -11
  97. package/reactHooks/advertising/index.ts +5 -5
  98. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  99. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +6 -8
  100. package/reactHooks/autoscrolling/useTrackedView.ts +12 -1
  101. package/reactHooks/cell-click/__tests__/index.test.js +10 -2
  102. package/reactHooks/cell-click/index.ts +7 -1
  103. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
  104. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
  105. package/reactHooks/feed/useBatchLoading.ts +10 -12
  106. package/reactHooks/feed/useFeedLoader.tsx +18 -9
  107. package/reactHooks/feed/usePipesCacheReset.ts +5 -5
  108. package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
  109. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +39 -0
  110. package/reactHooks/layout/__tests__/index.test.tsx +10 -10
  111. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
  112. package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
  113. package/reactHooks/layout/useDimensions/useDimensions.ts +8 -0
  114. package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
  115. package/reactHooks/navigation/__tests__/index.test.tsx +6 -21
  116. package/reactHooks/navigation/getTabBarHeight.ts +10 -0
  117. package/reactHooks/navigation/index.ts +41 -240
  118. package/reactHooks/navigation/useGetBottomTabBarHeight.ts +19 -0
  119. package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
  120. package/reactHooks/navigation/useIsScreenActive.ts +17 -0
  121. package/reactHooks/navigation/useNavigationPluginData.ts +19 -0
  122. package/reactHooks/navigation/useNavigationType.ts +17 -0
  123. package/reactHooks/navigation/usePathname.ts +4 -0
  124. package/reactHooks/navigation/useProfilerLogging.ts +15 -0
  125. package/reactHooks/navigation/useRoute.ts +98 -0
  126. package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
  127. package/reactHooks/player/TVSeekControlller/ISeekController.ts +32 -0
  128. package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +71 -0
  129. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +258 -0
  130. package/reactHooks/player/TVSeekControlller/TapSeekController.ts +53 -0
  131. package/reactHooks/player/__tests__/{useAutoSeek.test.tsx → useAutoSeek._test.tsx} +2 -3
  132. package/reactHooks/player/const.ts +0 -4
  133. package/reactHooks/player/index.ts +0 -6
  134. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
  135. package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
  136. package/reactHooks/state/useRefWithInitialValue.ts +14 -0
  137. package/reactHooks/state/useStateFromSubscribe.ts +23 -0
  138. package/reactHooks/useListenEventBusEvent.ts +1 -1
  139. package/reactHooks/usePluginConfiguration.ts +1 -1
  140. package/reactHooks/useSubscriberFor.ts +4 -3
  141. package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
  142. package/reactUtils/__tests__/reactUtils.test.js +1 -37
  143. package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
  144. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +13 -79
  145. package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +2 -2
  146. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
  147. package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
  148. package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
  149. package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
  150. package/reactUtils/createContext/__tests__/{index.test.js → index.test.ts} +2 -2
  151. package/reactUtils/createContext/__tests__/provider.test.js +4 -6
  152. package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
  153. package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
  154. package/reactUtils/createContext/helpers.ts +6 -1
  155. package/reactUtils/createContext/index.ts +11 -25
  156. package/reactUtils/index.ts +9 -25
  157. package/screenState/index.ts +24 -10
  158. package/services/js2native.ts +673 -0
  159. package/testUtils/index.tsx +0 -10
  160. package/theme/__mocks__/index.ts +4 -0
  161. package/theme/index.ts +1 -1
  162. package/time/BackgroundTimer.ts +55 -0
  163. package/time/Timer.ts +9 -6
  164. package/types.ts +1 -0
  165. package/utils/README.md +30 -0
  166. package/utils/chunk.ts +5 -0
  167. package/utils/index.ts +5 -0
  168. package/utils/times.ts +5 -0
  169. package/zappFrameworkUtils/localStorageHelper.ts +6 -0
  170. package/zappFrameworkUtils/loginPluginUtils.ts +6 -2
  171. package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
  172. package/zappFrameworkUtils/types.ts +11 -0
  173. package/appUtils/keyCodes/keys.ios.js +0 -46
  174. package/reactHooks/player/useAutoSeek.tsx +0 -87
  175. package/reactHooks/player/useSeek.tsx +0 -66
  176. package/reactHooks/player/useTapSeek.tsx +0 -58
  177. package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
  178. /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
  179. /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
  180. /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
  181. /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
  182. /package/domUtils/{logger.js → logger.ts} +0 -0
  183. /package/reactHooks/player/__tests__/{useTapSeek.test.ts → useTapSeek._test.ts} +0 -0
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+ import { isWeb } from "../../reactUtils";
3
+ import { useNavigation } from "./useNavigation";
4
+ import { usePathname } from "./usePathname";
5
+ import {
6
+ QUICK_BRICK_CONTENT,
7
+ QUICK_BRICK_NAVBAR,
8
+ } from "@applicaster/quick-brick-core/const";
9
+
10
+ type Return = (prefix: string) => string;
11
+
12
+ export const useUniqueRouteSuffix = (): Return => {
13
+ const pathname = usePathname();
14
+ const { currentRoute } = useNavigation();
15
+
16
+ const suffix = `currentRoute:${currentRoute}_pathname:${pathname}`;
17
+
18
+ const withUniqueRouteSuffix = React.useCallback((prefix: string) => {
19
+ if (isWeb()) {
20
+ // we should use extended route prefix only on Web, where we have stack-navigator
21
+ return `${prefix}___${suffix}`;
22
+ }
23
+
24
+ // for other platforms use previous version
25
+ return prefix;
26
+ }, []);
27
+
28
+ return withUniqueRouteSuffix;
29
+ };
30
+
31
+ export const useNavbarId = () => {
32
+ const withUniqueRouteSuffix = useUniqueRouteSuffix();
33
+
34
+ return withUniqueRouteSuffix(QUICK_BRICK_NAVBAR);
35
+ };
36
+
37
+ export const useContentId = () => {
38
+ const withUniqueRouteSuffix = useUniqueRouteSuffix();
39
+
40
+ return withUniqueRouteSuffix(QUICK_BRICK_CONTENT);
41
+ };
@@ -0,0 +1,32 @@
1
+ import { MAX_SEEK_TIME, SEEK_TYPE, SKIP_TIME_BASE } from "../const";
2
+
3
+ export interface ISeekControllerDelegate {
4
+ handleDelayedSeek(offset: number): void;
5
+ handleSeek(offset: number): void;
6
+ handleSeekComplete(): void;
7
+ handlePlayerPause(): void;
8
+ }
9
+
10
+ export interface ISeekController {
11
+ title: string;
12
+ onStartSeek(seekType: SEEK_TYPE): void;
13
+ onSeekEnd(): void;
14
+ reset(): void;
15
+ }
16
+
17
+ export class SeekIncrementalBaseStrategy {
18
+ private skipMultiplier = 1;
19
+
20
+ getSkipTime = () =>
21
+ Math.min(MAX_SEEK_TIME, SKIP_TIME_BASE * this.skipMultiplier);
22
+
23
+ increment = () => {
24
+ this.skipMultiplier++;
25
+ };
26
+
27
+ getSkipMultiplier = () => this.skipMultiplier;
28
+
29
+ reset = () => {
30
+ this.skipMultiplier = 1;
31
+ };
32
+ }
@@ -0,0 +1,71 @@
1
+ import {
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";
10
+
11
+ interface LongPressConfig {
12
+ TIMER_INTERVAL_MS: number;
13
+ }
14
+
15
+ export class LongPressSeekController implements ISeekController {
16
+ private subscription: Subscription | null = null;
17
+ readonly title = "LongPressSeek";
18
+ constructor(
19
+ private seekControllerDelegate: ISeekControllerDelegate,
20
+ private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
21
+ private config: LongPressConfig
22
+ ) {}
23
+
24
+ onStartSeek(seekType: SEEK_TYPE) {
25
+ this.cleanUpSubscription();
26
+
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
+ });
53
+ }
54
+
55
+ onSeekEnd() {
56
+ this.cleanUpSubscription();
57
+ this.seekControllerDelegate.handleSeekComplete();
58
+ }
59
+
60
+ reset() {
61
+ this.seekIncrementalStrategy.reset();
62
+ this.cleanUpSubscription();
63
+ }
64
+
65
+ private cleanUpSubscription() {
66
+ if (this.subscription) {
67
+ this.subscription.unsubscribe();
68
+ this.subscription = null;
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,258 @@
1
+ import { BehaviorSubject } from "rxjs";
2
+ import { TapSeekController } from "./TapSeekController";
3
+ import { LongPressSeekController } from "./LongPressSeekController";
4
+ import { SEEK_TYPE } from "../const";
5
+ import { createLogger } from "../../../logger";
6
+ import { Player } from "../../../appUtils/playerManager/player";
7
+ import { SeekKeyHandlerListener } from "../../../appUtils/keyInputHandler/KeyInputHandler";
8
+ import {
9
+ ISeekController,
10
+ ISeekControllerDelegate,
11
+ SeekIncrementalBaseStrategy,
12
+ } from "./ISeekController";
13
+
14
+ export const { log_warning, log_debug, log_info, log_error, log_verbose } =
15
+ createLogger({
16
+ category: "General",
17
+ subsystem: "TVSeekController",
18
+ });
19
+
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;
41
+ private delayedSeekProgressSubject = new BehaviorSubject<number | null>(null);
42
+ isPlayerInitiallyPaused: null | boolean = null;
43
+ currentSeekType: SEEK_TYPE | null = null;
44
+
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;
50
+
51
+ constructor(playerController: Player) {
52
+ this.playerController = playerController;
53
+
54
+ this.tapSeekController = new TapSeekController(
55
+ this,
56
+ new SeekIncrementalBaseStrategy(),
57
+ CONFIG_TAP_SEEK
58
+ );
59
+
60
+ this.longPressSeekController = new LongPressSeekController(
61
+ this,
62
+ new SeekIncrementalBaseStrategy(),
63
+ CONFIG_LONG_PRESS
64
+ );
65
+
66
+ this.currentSeekController = this.tapSeekController;
67
+ }
68
+
69
+ getDelayedSeekProgressObservable = () => {
70
+ return this.delayedSeekProgressSubject;
71
+ };
72
+
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");
78
+
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
+ }
87
+ };
88
+
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();
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);
105
+ };
106
+
107
+ onForwardPress = (): boolean => {
108
+ this.handleStartSeek(SEEK_TYPE.FORWARD);
109
+
110
+ return true;
111
+ };
112
+
113
+ onRewindPress = (): boolean => {
114
+ this.handleStartSeek(SEEK_TYPE.REWIND);
115
+
116
+ return true;
117
+ };
118
+
119
+ onSeekEnd = () => {
120
+ this.clearLongPressThresholdTimeout();
121
+ this.currentSeekController.onSeekEnd();
122
+ };
123
+
124
+ onRewindPressOut = () => {
125
+ this.onSeekEnd();
126
+ };
127
+
128
+ onForwardPressOut = () => {
129
+ this.onSeekEnd();
130
+ };
131
+
132
+ onVideoProgress = (position: number) => {
133
+ if (this.delayedSeekPosition !== null) {
134
+ return;
135
+ }
136
+
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
+
148
+ let targetPos = currentPos;
149
+
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);
157
+ } else {
158
+ log_warning(
159
+ `TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
160
+ );
161
+ }
162
+
163
+ log_debug(
164
+ `TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, skipTime: ${offset}`
165
+ );
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;
178
+ this.delayedSeekProgressSubject.next(targetPos);
179
+ };
180
+
181
+ handleSeekTo = (newPosition: number) => {
182
+ this.playerController.seekTo(newPosition);
183
+
184
+ if (!this.isPlayerInitiallyPaused) {
185
+ this.playerController.play();
186
+ }
187
+ };
188
+
189
+ handleSeek = (offset: number) => {
190
+ this.playerController[this.currentSeekType](offset);
191
+
192
+ if (!this.isPlayerInitiallyPaused) {
193
+ this.playerController.play();
194
+ }
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
+ }
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
+ };
242
+
243
+ resetAll() {
244
+ log_debug("TVSeekController: Reset all states");
245
+ this.currentSeekType = null;
246
+
247
+ this.delayedSeekPosition = null;
248
+ this.isPlayerInitiallyPaused = null;
249
+
250
+ this.clearSeekCompleteTimeout();
251
+ this.clearLongPressThresholdTimeout();
252
+
253
+ this.tapSeekController.reset();
254
+ this.longPressSeekController.reset();
255
+
256
+ this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
257
+ }
258
+ }
@@ -0,0 +1,53 @@
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
+ }
12
+
13
+ export class TapSeekController implements ISeekController {
14
+ readonly title = "TapSeek";
15
+ constructor(
16
+ private seekControllerDelegate: ISeekControllerDelegate,
17
+ private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
18
+ private config: TapPressConfig
19
+ ) {}
20
+
21
+ onStartSeek = (seekType: SEEK_TYPE) => {
22
+ log_debug(`${this.title}: start tap seek type: ${seekType}.`);
23
+
24
+ const skipTime = this.seekIncrementalStrategy.getSkipTime();
25
+ const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
26
+
27
+ if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
28
+ log_debug(
29
+ `${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
30
+ );
31
+
32
+ this.seekControllerDelegate.handleDelayedSeek(skipTime);
33
+ } else {
34
+ log_debug(
35
+ `${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
36
+ );
37
+
38
+ this.seekControllerDelegate.handleSeek(skipTime);
39
+ }
40
+
41
+ this.seekIncrementalStrategy.increment();
42
+ };
43
+
44
+ onSeekEnd = () => {
45
+ this.seekControllerDelegate.handleSeekComplete();
46
+ };
47
+
48
+ reset = () => {
49
+ log_debug("{this.title}: Reset states");
50
+
51
+ this.seekIncrementalStrategy.reset();
52
+ };
53
+ }
@@ -5,15 +5,14 @@ import { ON_HOLD_INTERVAL, SEEK_TYPE, SKIP_TIME_BASE } from "../const";
5
5
 
6
6
  import { useAutoSeek } from "../useAutoSeek";
7
7
 
8
- jest.useFakeTimers();
8
+ jest.useFakeTimers({ legacyFakeTimers: true });
9
9
 
10
10
  const rewind = jest.fn();
11
11
  const forward = jest.fn();
12
12
 
13
13
  jest
14
14
  .spyOn(playerManager, "getInstanceController")
15
- // @ts-ignore
16
- .mockImplementation(() => ({ rewind, forward }));
15
+ .mockReturnValue({ rewind, forward });
17
16
 
18
17
  describe("useAutoSeek", () => {
19
18
  beforeEach(() => {
@@ -1,9 +1,5 @@
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
3
  export const MAX_SEEK_TIME = 600;
8
4
 
9
5
  export enum SEEK_TYPE {
@@ -1,7 +1 @@
1
1
  export { useLanguageData } from "./useLanguageData";
2
-
3
- export { useAutoSeek } from "./useAutoSeek";
4
-
5
- export { useTapSeek } from "./useTapSeek";
6
-
7
- export { useSeek, withUseSeek } from "./useSeek";
@@ -0,0 +1,69 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+
3
+ jest.mock(
4
+ "@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration",
5
+ () => ({
6
+ useScreenConfiguration: jest.fn(),
7
+ })
8
+ );
9
+
10
+ const {
11
+ useScreenConfiguration,
12
+ } = require("@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration");
13
+
14
+ const { useScreenBackgroundColor } = require("../useScreenBackgroundColor");
15
+
16
+ describe("useScreenBackgroundColor", () => {
17
+ afterEach(() => {
18
+ jest.clearAllMocks();
19
+ });
20
+
21
+ it("should return the background color from screen configuration", () => {
22
+ useScreenConfiguration.mockReturnValue({
23
+ backgroundColor: "#FF0000",
24
+ });
25
+
26
+ // Render the hook with a screen ID
27
+ const { result } = renderHook(() =>
28
+ useScreenBackgroundColor("test-screen-id")
29
+ );
30
+
31
+ expect(result.current).toBe("#FF0000");
32
+
33
+ expect(useScreenConfiguration).toHaveBeenCalledWith("test-screen-id");
34
+ });
35
+
36
+ it("should return 'transparent' when background color is empty", () => {
37
+ useScreenConfiguration.mockReturnValue({
38
+ backgroundColor: "",
39
+ });
40
+
41
+ const { result } = renderHook(() =>
42
+ useScreenBackgroundColor("test-screen-id")
43
+ );
44
+
45
+ expect(result.current).toBe("transparent");
46
+ });
47
+
48
+ it("should return 'transparent' when background color is null", () => {
49
+ useScreenConfiguration.mockReturnValue({
50
+ backgroundColor: null,
51
+ });
52
+
53
+ const { result } = renderHook(() =>
54
+ useScreenBackgroundColor("test-screen-id")
55
+ );
56
+
57
+ expect(result.current).toBe("transparent");
58
+ });
59
+
60
+ it("should return 'transparent' when background color is undefined", () => {
61
+ useScreenConfiguration.mockReturnValue({});
62
+
63
+ const { result } = renderHook(() =>
64
+ useScreenBackgroundColor("test-screen-id")
65
+ );
66
+
67
+ expect(result.current).toBe("transparent");
68
+ });
69
+ });
@@ -1,23 +1,11 @@
1
- import * as React from "react";
2
- import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
3
1
  import { useScreenConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration";
4
2
  import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
5
3
 
4
+ const DEFAULT_BACKGROUND_FALLBACK = "transparent";
5
+
6
6
  export const useScreenBackgroundColor = (screenId: string): string => {
7
7
  const { backgroundColor: screenBackgroundColor } =
8
8
  useScreenConfiguration(screenId);
9
9
 
10
- const theme = useTheme();
11
-
12
- const themeBackgroundColor = React.useMemo(
13
- () => theme.app_background_color,
14
- [theme.app_background_color]
15
- );
16
-
17
- const backgroundColor = ifEmptyUseFallback(
18
- screenBackgroundColor,
19
- themeBackgroundColor
20
- );
21
-
22
- return backgroundColor;
10
+ return ifEmptyUseFallback(screenBackgroundColor, DEFAULT_BACKGROUND_FALLBACK);
23
11
  };
@@ -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
+ };
@@ -23,5 +23,5 @@ export const useListenEventBusEvent = (
23
23
  sub.remove(); // stop listening to DeviceEventEmitter
24
24
  };
25
25
  }
26
- }, []);
26
+ }, [handler, subscriptionID, source, events]);
27
27
  };
@@ -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
  /**